From fda1a554506666290ae89f35d7ad303cbcc43197 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 18 Jun 2021 11:35:33 -0400 Subject: [PATCH 001/425] Update Readme Summary: Ref T15006 Test Plan: We'll test it in prod Reviewers: #trusted_contributors, chris, tobiaswiese Reviewed By: #trusted_contributors, chris, tobiaswiese Subscribers: tobiaswiese, chris Maniphest Tasks: T15006 Differential Revision: https://we.phorge.it/D25000 --- README.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5190845da4..19dc39cd82 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,24 @@ -Effective June 1, 2021: Phabricator is no longer actively maintained. +**Phorge** is a collection of web applications which help software companies build better software. -**Phabricator** is a collection of web applications for software development. +Phorge is a community-maintained fork of [Phabricator](http://phabricator.org). + +Phorge includes applications for: + + - reviewing and auditing source code; + - hosting and browsing repositories; + - tracking bugs; + - managing projects; + - conversing with team members; + - assembling a party to venture forth; + - writing stuff down and reading it later; + - hiding stuff from coworkers; and + - also some other things. + + +Phorge is developed and maintained by [The Phorge Team](https://phorge.it). + +---------- **LICENSE** -Phabricator is released under the Apache 2.0 license except as otherwise noted. +Phorge is released under the Apache 2.0 license except as otherwise noted. From 7157dd96b34a69ab7edfe3c21e5e5fcd1e8f7cac Mon Sep 17 00:00:00 2001 From: Christopher Speck Date: Thu, 17 Jun 2021 23:38:55 -0400 Subject: [PATCH 002/425] T15006: Update .arcconfig to point to we.phorge.it Summary: Update the `.arcconfig` file to point to our decided-upon URL for hosting Refs T15006 Test Plan: I used `arc which` and verified it identified `https://we.phorge.it`: ```lang=console REPOSITORY To identify the repository associated with this working copy, arc followed this process: Configuration value "repository.callsign" is empty. This repository has no VCS UUID (this is normal for git/hg). The remote URI for this working copy is "ssh://git@we.phorge.it/source/phorge.git". Found a unique matching repository. This working copy is associated with the Phorge repository. ``` Reviewers: avivey, chris, tobiaswiese Reviewed By: avivey, tobiaswiese Maniphest Tasks: T15006 Differential Revision: https://we.phorge.it/D25001 --- .arcconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.arcconfig b/.arcconfig index 88f04f72c5..b381a3ac53 100644 --- a/.arcconfig +++ b/.arcconfig @@ -1,5 +1,5 @@ { - "phabricator.uri": "https://secure.phabricator.com/", + "phabricator.uri": "https://we.phorge.it/", "load": ["src/"], "history.immutable": false } From 1ddb953b9f7742689b8cf8d9d34db14f5672fa91 Mon Sep 17 00:00:00 2001 From: Matthew Bowker Date: Sat, 19 Jun 2021 17:19:56 -0600 Subject: [PATCH 003/425] Update Diviner documentation to reference Phorge instead of Phabricator. Summary: This commit also removes references to support pacts and updates links to point to the new upstream. Test Plan: Generated Diviner documentation on a local install and verified that the changes look good. Reviewers: O1 Blessed Committers, chris Reviewed By: O1 Blessed Committers, chris Subscribers: chris, speck, tobiaswiese Maniphest Tasks: T15012 Differential Revision: https://we.phorge.it/D25007 --- src/docs/book/contributor.book | 10 +- src/docs/book/flavor.book | 13 +- .../book/{phabricator.book => phorge.book} | 10 +- src/docs/book/user.book | 13 +- .../contributor/adding_new_classes.diviner | 77 ++++--- .../contributor/adding_new_css_and_js.diviner | 14 +- .../assistive_technologies.diviner | 8 +- src/docs/contributor/bug_reports.diviner | 153 ++++++++++++- .../{tech => contributor}/celerity.diviner | 2 +- src/docs/contributor/cla.diviner | 169 -------------- src/docs/contributor/contrib_intro.diviner | 23 +- .../contributor/contributing_code.diviner | 195 +++++++++++++++- .../contributor/css_coding_standards.diviner | 12 +- src/docs/contributor/database.diviner | 34 +-- .../contributor/describing_problems.diviner | 42 ++-- src/docs/contributor/developer_setup.diviner | 18 +- src/docs/contributor/feature_requests.diviner | 209 +++++++++++++++++- .../general_coding_standards.diviner | 4 +- .../{tech => contributor}/handles.diviner | 4 +- .../contributor/internationalization.diviner | 14 +- .../javascript_coding_standards.diviner | 4 +- src/docs/contributor/n_plus_one.diviner | 2 +- ...out.diviner => phorge_code_layout.diviner} | 42 ++-- .../contributor/php_coding_standards.diviner | 4 +- src/docs/contributor/rendering_html.diviner | 8 +- .../contributor/reproduction_steps.diviner | 44 +--- .../running_builtin_php_webserver.diviner | 4 +- src/docs/contributor/unit_tests.diviner | 14 +- src/docs/contributor/using_edges.diviner | 3 +- .../contributor/using_oauthserver.diviner | 18 +- src/docs/contributor/version.diviner | 10 +- src/docs/flavor/about_flavor_text.diviner | 9 - src/docs/flavor/project_history.diviner | 61 ++--- .../recommendations_on_branching.diviner | 6 +- ...ecommendations_on_revision_control.diviner | 6 +- src/docs/flavor/so_many_databases.diviner | 20 +- src/docs/flavor/soon_static_resources.diviner | 2 +- .../flavor/things_you_should_do_now.diviner | 2 +- .../flavor/writing_reviewable_code.diviner | 10 +- src/docs/user/cluster/cluster.diviner | 22 +- src/docs/user/cluster/cluster_daemons.diviner | 4 +- .../user/cluster/cluster_databases.diviner | 96 ++++---- src/docs/user/cluster/cluster_devices.diviner | 34 +-- .../cluster/cluster_notifications.diviner | 6 +- .../user/cluster/cluster_partitioning.diviner | 36 +-- .../user/cluster/cluster_repositories.diviner | 56 ++--- src/docs/user/cluster/cluster_search.diviner | 16 +- src/docs/user/cluster/cluster_ssh.diviner | 8 +- .../user/cluster/cluster_webservers.diviner | 8 +- .../advanced_configuration.diviner | 32 +-- .../configuration/configuration_guide.diviner | 46 ++-- .../configuration_locked.diviner | 32 +-- ...figuring_accounts_and_registration.diviner | 12 +- .../configuration/configuring_backups.diviner | 40 ++-- .../configuring_encryption.diviner | 14 +- .../configuring_file_domain.diviner | 34 +-- .../configuring_file_storage.diviner | 36 +-- .../configuring_inbound_email.diviner | 72 +++--- .../configuring_outbound_email.diviner | 50 ++--- .../configuring_preamble.diviner | 22 +- .../user/configuration/custom_fields.diviner | 14 +- .../configuration/managing_caches.diviner | 16 +- .../configuration/managing_daemons.diviner | 24 +- .../configuration/managing_garbage.diviner | 6 +- .../user/configuration/notifications.diviner | 40 ++-- .../user/configuration/storage_adjust.diviner | 30 +-- .../troubleshooting_https.diviner | 8 +- src/docs/user/feedback.diviner | 7 - src/docs/user/field/conduit_changes.diviner | 2 +- src/docs/user/field/darkconsole.diviner | 4 +- src/docs/user/field/exit_codes.diviner | 21 +- src/docs/user/field/performance.diviner | 8 +- .../field/permanently_destroying_data.diviner | 14 +- src/docs/user/field/repository_hints.diviner | 14 +- .../user/field/repository_imports.diviner | 26 +-- src/docs/user/field/restarting.diviner | 6 +- .../user/field/revoking_credentials.diviner | 16 +- src/docs/user/field/worker_queue.diviner | 12 +- src/docs/user/field/xhprof.diviner | 4 +- src/docs/user/installation_guide.diviner | 52 ++--- src/docs/user/introduction.diviner | 25 ++- src/docs/user/reporting_security.diviner | 35 +-- src/docs/user/support.diviner | 33 ++- src/docs/user/upgrading.diviner | 65 +++--- src/docs/user/userguide/almanac.diviner | 6 +- src/docs/user/userguide/amazon_rds.diviner | 2 +- src/docs/user/userguide/arcanist.diviner | 8 +- .../user/userguide/arcanist_coverage.diviner | 6 +- src/docs/user/userguide/arcanist_diff.diviner | 4 +- .../user/userguide/arcanist_lint_unit.diviner | 2 +- .../userguide/arcanist_new_project.diviner | 14 +- .../userguide/arcanist_quick_start.diviner | 8 +- src/docs/user/userguide/audit.diviner | 10 +- src/docs/user/userguide/calendar.diviner | 2 +- .../user/userguide/calendar_exports.diviner | 8 +- .../user/userguide/calendar_imports.diviner | 24 +- src/docs/user/userguide/conduit.diviner | 4 +- src/docs/user/userguide/differential.diviner | 4 +- .../user/userguide/differential_land.diviner | 4 +- src/docs/user/userguide/diffusion.diviner | 8 +- src/docs/user/userguide/diffusion_api.diviner | 2 +- .../user/userguide/diffusion_existing.diviner | 4 +- .../user/userguide/diffusion_hooks.diviner | 8 +- .../user/userguide/diffusion_hosting.diviner | 110 ++++----- .../user/userguide/diffusion_managing.diviner | 34 +-- .../userguide/diffusion_permanent.diviner | 10 +- .../user/userguide/diffusion_symbols.diviner | 12 +- .../user/userguide/diffusion_updates.diviner | 18 +- .../user/userguide/diffusion_uris.diviner | 94 ++++---- src/docs/user/userguide/diviner.diviner | 4 +- src/docs/user/userguide/drydock.diviner | 2 +- src/docs/user/userguide/drydock_hosts.diviner | 8 +- .../userguide/drydock_quick_start.diviner | 2 +- .../drydock_repository_automation.diviner | 6 +- .../user/userguide/drydock_security.diviner | 26 +-- src/docs/user/userguide/events.diviner | 34 +-- .../user/userguide/external_editor.diviner | 2 +- src/docs/user/userguide/forms.diviner | 12 +- src/docs/user/userguide/harbormaster.diviner | 10 +- src/docs/user/userguide/herald.diviner | 2 +- src/docs/user/userguide/jump.diviner | 2 +- src/docs/user/userguide/legalpad.diviner | 14 +- src/docs/user/userguide/mail_rules.diviner | 10 +- .../user/userguide/multi_factor_auth.diviner | 28 +-- src/docs/user/userguide/multimeter.diviner | 6 +- src/docs/user/userguide/phame.diviner | 12 +- src/docs/user/userguide/profile_menu.diviner | 2 +- src/docs/user/userguide/projects.diviner | 10 +- src/docs/user/userguide/prototypes.diviner | 2 +- src/docs/user/userguide/remarkup.diviner | 12 +- .../user/userguide/reviews_vs_audit.diviner | 4 +- src/docs/user/userguide/search.diviner | 10 +- src/docs/user/userguide/spaces.diviner | 4 +- src/docs/user/userguide/tone.diviner | 6 +- src/docs/user/userguide/unlocking.diviner | 2 +- src/docs/user/userguide/users.diviner | 2 +- src/docs/user/userguide/utf8.diviner | 8 +- src/docs/user/userguide/webhooks.diviner | 12 +- 138 files changed, 1712 insertions(+), 1399 deletions(-) rename src/docs/book/{phabricator.book => phorge.book} (97%) rename src/docs/{tech => contributor}/celerity.diviner (99%) delete mode 100644 src/docs/contributor/cla.diviner rename src/docs/{tech => contributor}/handles.diviner (97%) rename src/docs/contributor/{phabricator_code_layout.diviner => phorge_code_layout.diviner} (78%) delete mode 100644 src/docs/flavor/about_flavor_text.diviner delete mode 100644 src/docs/user/feedback.diviner diff --git a/src/docs/book/contributor.book b/src/docs/book/contributor.book index 10db63c011..1d0386251f 100644 --- a/src/docs/book/contributor.book +++ b/src/docs/book/contributor.book @@ -1,11 +1,11 @@ { - "name": "phabcontrib", - "title": "Phabricator Contributor Documentation", - "short": "Phabricator Contributor Docs", - "preface": "Information for Phabricator contributors and developers.", + "name": "contrib", + "title": "Phorge Contributor Documentation", + "short": "Contributor Docs", + "preface": "Information for Phorge contributors and developers.", "root": "../../../", "uri.source": - "https://secure.phabricator.com/diffusion/P/browse/master/%f$%l", + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", "rules": { "(\\.diviner$)": "DivinerArticleAtomizer" }, diff --git a/src/docs/book/flavor.book b/src/docs/book/flavor.book index 978244f19d..4404e94bf7 100644 --- a/src/docs/book/flavor.book +++ b/src/docs/book/flavor.book @@ -1,11 +1,11 @@ { - "name": "phabflavor", - "title": "Phabricator Flavor Text", + "name": "flavor", + "title": "Phorge Flavor Text", "short": "Flavor Text", - "preface": "Recommendations, lore, and dark rituals.", + "preface": "A collection of short articles which pertain to software development in general, not necessarily to Phorge specifically.", "root": "../../../", "uri.source": - "https://secure.phabricator.com/diffusion/P/browse/master/%f$%l", + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", "rules": { "(\\.diviner$)": "DivinerArticleAtomizer" }, @@ -20,14 +20,11 @@ "(^webroot/rsrc/externals/)" ], "groups": { - "overview": { - "name": "Overview" - }, "javascript": { "name": "Javascript" }, "lore": { - "name": "Phabricator Lore" + "name": "Phorge Lore" }, "php": { "name": "PHP" diff --git a/src/docs/book/phabricator.book b/src/docs/book/phorge.book similarity index 97% rename from src/docs/book/phabricator.book rename to src/docs/book/phorge.book index 2beef23023..d423482813 100644 --- a/src/docs/book/phabricator.book +++ b/src/docs/book/phorge.book @@ -1,11 +1,11 @@ { - "name": "phabdev", - "title": "Phabricator Technical Documentation", - "short": "Phabricator Tech Docs", - "preface": "Technical reference material for Phabricator developers.", + "name": "dev", + "title": "Phorge Technical Documentation", + "short": "Tech Docs", + "preface": "Technical reference material for Phorge developers.", "root": "../../../", "uri.source": - "https://secure.phabricator.com/diffusion/P/browse/master/%f$%l", + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", "rules": { "(\\.diviner$)": "DivinerArticleAtomizer", "(\\.php$)": "DivinerPHPAtomizer" diff --git a/src/docs/book/user.book b/src/docs/book/user.book index fb2dccc578..449acd4ffc 100644 --- a/src/docs/book/user.book +++ b/src/docs/book/user.book @@ -1,11 +1,11 @@ { - "name": "phabricator", - "title": "Phabricator User Documentation", - "short": "Phabricator User Docs", - "preface": "Instructions for installing, configuring, and using Phabricator.", + "name": "phorge", + "title": "Phorge User Documentation", + "short": "User Docs", + "preface": "Instructions for installing, configuring, and using Phorge.", "root": "../../../", "uri.source": - "https://secure.phabricator.com/diffusion/P/browse/master/%f$%l", + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", "rules": { "(\\.diviner$)": "DivinerArticleAtomizer" }, @@ -37,9 +37,6 @@ }, "fieldmanual": { "name": "Field Manuals" - }, - "cellar": { - "name": "Musty Cellar" } } } diff --git a/src/docs/contributor/adding_new_classes.diviner b/src/docs/contributor/adding_new_classes.diviner index ea932eba5c..64ab228ea3 100644 --- a/src/docs/contributor/adding_new_classes.diviner +++ b/src/docs/contributor/adding_new_classes.diviner @@ -1,22 +1,22 @@ @title Adding New Classes @group developer -Guide to adding new classes to extend Phabricator. +Guide to adding new classes to extend Phorge. Overview ======== -Phabricator is highly modular, and many parts of it can be extended by adding +Phorge is highly modular, and many parts of it can be extended by adding new classes. This document explains how to write new classes to change or -expand the behavior of Phabricator. +expand the behavior of Phorge. -IMPORTANT: The upstream does not offer support with extension development. +NOTE: The upstream offers limited support with extension development. Fundamentals ============ -Phabricator primarily discovers functionality by looking at concrete subclasses -of some base class. For example, Phabricator determines which applications are +Phorge primarily discovers functionality by looking at concrete subclasses +of some base class. For example, Phorge determines which applications are available by looking at all of the subclasses of @{class@phabricator:PhabricatorApplication}. It discovers available workflows in `arc` by looking at all of the subclasses of @@ -24,7 +24,7 @@ discovers available workflows in `arc` by looking at all of the subclasses of by looking at all of the subclasses of @{class@arcanist:PhutilLocale}. This pattern holds in many cases, so you can often add functionality by adding -new classes with no other work. Phabricator will automatically discover and +new classes with no other work. Phorge will automatically discover and integrate the new capabilities or features at runtime. There are two main ways to add classes: @@ -42,20 +42,20 @@ The next sections walk through these approaches in greater detail. Extensions Directory ==================== -The easiest way to extend Phabricator by adding new classes is to drop them -into the extensions directory, at `phabricator/src/extensions/`. +The easiest way to extend Phorge by adding new classes is to drop them +into the extensions directory, at `pohrge/src/extensions/`. This is intended as a quick way to add small pieces of functionality, test new -features, or get started on a larger project. Extending Phabricator like this +features, or get started on a larger project. Extending Phorge like this imposes a small performance penalty compared to using a library. -This directory exists in all libphutil libraries, so you can find a similar +This directory also exists for Arcanist, so you can find a similar directory in `arcanist/src/extensions/`. For example, to add a new application, create a file like this one and add it -to `phabricator/src/extensions/`. +to `phorge/src/extensions/`. -```name=phabricator/src/extensions/ExampleApplication.php, lang=php +```name=phorge/src/extensions/ExampleApplication.php, lang=php 4. You represent that you are legally entitled to grant the above license. If -> your employer(s) has rights to intellectual property that you create that -> includes your Contributions, you represent that you have received permission -> to make Contributions on behalf of that employer, that your employer has -> waived such rights for your Contributions to Phacility, or that your employer -> has executed a separate Corporate CLA with Phacility. - -Ownership of your work varies based on where you live, how you are employed, -and your agreements with your employer. However, at least in the US, it is -likely that your employer owns your work unless you have anticipated conflicts -and specifically avoided them. This generally makes sense: if you are paid by -your employer for your work, they own the product of your work and you receive -salary and benefits in fair exchange for that work. - -Your employer may have an ownership claim on your work even if you perform it -on your own time, if you use their equipment (like a company laptop or phone), -resources, facilities, or trade secrets, or signed something like an "Invention -Assignment Agreement" when you were hired. Such agreements are common. The -details of the strength of their claim will vary based on your situation and -local law. - -If you are unsure, you should speak with your employer or a lawyer. If you -contribute code you do not own under the individual CLA, you are exposing -yourself to liability. You may also be exposing us to liability, but we'll have -the CLA on our side to show that we were unwilling pawns in your malicious -scheme to defraud your employer. - -The good news is that most employers are happy to contribute to open source -projects. Incentives are generally well aligned: they get features they want, -and it reflects well on them. In the past, potential contributors who have -approached their employers about a corporate CLA have generally had little -difficulty getting approval. diff --git a/src/docs/contributor/contrib_intro.diviner b/src/docs/contributor/contrib_intro.diviner index 59ad9b44df..00a2e42c0e 100644 --- a/src/docs/contributor/contrib_intro.diviner +++ b/src/docs/contributor/contrib_intro.diviner @@ -1,12 +1,12 @@ @title Contributor Introduction @group contrib -Introduction to contributing to Phabricator and Arcanist. +Introduction to contributing to Phorge and Arcanist. Overview ======== -If you'd like to contribute to Phabricator, this document can guide you though +If you'd like to contribute to Phorge, this document can guide you though ways you can help improve the project. Writing code is valuable, but often isn't the best or easiest way to contribute. @@ -19,24 +19,11 @@ heavy lifting. Without writing any code, learning the whole codebase, making a big time commitment, or having to touch PHP, here are some ways you can materially -contribute to Phabricator: +contribute to Phorge: - - Drop by the [[ https://phurl.io/u/discourse | community forum ]] just to - say "thanks". A big part of the reason we build this software is to help - people solve problems, and knowing that our efforts are appreciated is - really rewarding. - - Recommend Phabricator to people who you think might find it useful. Our + - Recommend Phorge to people who you think might find it useful. Our most powerful growth channel is word of mouth, and mentioning or tweeting - about Phabricator helps the project grow. If writing a tweet sounds like - too much work, you can use one of these form tweets written by our PR - department to quickly and easily shill on our behalf. Hail corporate! - -> Phabricator seems like it's pretty okay - -> I am not being paid to mention Phabricator in this extemporaneous, completely organic tweet - -> Phabricator is objectively the best thing. Source: I am a certified, internationally recognized expert. - + about Phorge helps the project grow. - Submit high-quality bug reports by carefully following the guide in @{article:Contributing Bug Reports}. diff --git a/src/docs/contributor/contributing_code.diviner b/src/docs/contributor/contributing_code.diviner index b6816e03b5..c7bdec25c9 100644 --- a/src/docs/contributor/contributing_code.diviner +++ b/src/docs/contributor/contributing_code.diviner @@ -1,4 +1,197 @@ @title Contributing Code @group detail -Effective June 1, 2021: Phabricator is no longer actively maintained, and no longer accepting contributions. +Phorge is an open-source project, and welcomes contributions from the community +at large. However, there are some guidelines we ask you to follow. + + +Overview +======== + +The most important parts of contributing code to Phorge are: + + - File a task with a bug report or feature request //before// you write code. + - We do not accept GitHub pull requests. + - Some alternative approaches are available if your change isn't something + we want to bring upstream. + +The rest of this article describes these points in more detail, and then +provides guidance on writing and submitting patches. + +If you just want to contribute some code but don't have a specific bug or +feature in mind, see the bottom of this document for tips on finding ways to get +started. + +For general information on contributing to Phorge, see +@{article:Contributor Introduction}. + + +Coordinate First +================ + +Before sending code, you should file a task describing what you'd like to write. + +When you file a task, mention that you'd like to write the code to fix it. We +can help contextualize your request or bug and guide you through writing an +upstreamable patch, provided it's something that's upstreamable. If it isn't +upstreamable, we can let you know what the issues are and help find another +plan of attack. + +You don't have to file first (for example, if you spot a misspelling it's +normally fine to just send a diff), but for anything even moderately complex +you're strongly encouraged to file first and coordinate with the upstream. + + +Rejecting Patches +================= + +If you send us a patch without coordinating it with us first, it will probably +be immediately rejected, or sit in limbo for a long time and eventually be +rejected. The reasons we do this vary from patch to patch, but some of the most +common reasons are: + +**Unjustifiable Costs**: We support code in the upstream forever. Support is +enormously expensive and takes up a huge amount of our time. The cost to support +a change over its lifetime is often 10x or 100x or 1000x greater than the cost +to write the first version of it. Many uncoordinated patches we receive are +"white elephants", which would cost much more to maintain than the value they +provide. + +As an author, it may look like you're giving us free work and we're rejecting it +as too expensive, but this viewpoint doesn't align with the reality of a large +project which is actively supported by a small, experienced team. Writing code +is cheap; maintaining it is expensive. + +By coordinating with us first, you can make sure the patch is something we +consider valuable enough to put long-term support resources behind, and that +you're building it in a way that we're comfortable taking over. + +**Not a Good Fit**: Many patches aren't good fits for the upstream: they +implement features we simply don't want. Coordinating with us first helps +make sure we're on the same page and interested in a feature. + +The most common type of patch along these lines is a patch which adds new +configuration options. We consider additional configuration options to have +an exceptionally high lifetime support cost and are very unlikely to accept +them. Coordinate with us first. + +**Not a Priority**: If you send us a patch against something which isn't a +priority, we probably won't have time to look at it. We don't give special +treatment to low-priority issues just because there's code written: we'd still +be spending time on something lower-priority when we could be spending it on +something higher-priority instead. + +If you coordinate with us first, you can make sure your patch is in an area +of the codebase that we can prioritize. + +**Overly Ambitious Patches**: Sometimes we'll get huge patches from new +contributors. These can have a lot of fundamental problems and require a huge +amount of our time to review and correct. If you're interested in contributing, +you'll have more success if you start small and learn as you go. + +We can help you break a large change into smaller pieces and learn how the +codebase works as you proceed through the implementation, but only if you +coordinate with us first. + +**Generality**: We often receive several feature requests which ask for similar +features, and can come up with a general approach which covers all of the use +cases. If you send us a patch for //your use case only//, the approach may be +too specific. When a cleaner and more general approach is available, we usually +prefer to pursue it. + +By coordinating with us first, we can make you aware of similar use cases and +opportunities to generalize an approach. These changes are often small, but can +have a big impact on how useful a piece of code is. + +**Infrastructure and Sequencing**: Sometimes patches are written against a piece +of infrastructure with major planned changes. We don't want to accept these +because they'll make the infrastructure changes more difficult to implement. + +Coordinate with us first to make sure a change doesn't need to wait on other +pieces of infrastructure. We can help you identify technical blockers and +possibly guide you through resolving them if you're interested. + + +No Prototype Changes +==================== + +With rare exceptions, we do not accept patches for prototype applications for +the same reasons that we don't accept feature requests or bug reports. To learn +more about prototype applications, see +@{article:User Guide: Prototype Applications}. + + +No Pull Requests +================ + +We do not accept pull requests on GitHub: + + - Pull requests do not get lint and unit tests run, so issues which are + normally caught statically can slip by. + - Phorge is code review software, and developed using its own workflows. + Pull requests bypass some of these workflows (for example, they will not + trigger Herald rules to notify interested parties). + - GitHub is not the authoritative master repository and we maintain a linear + history, so merging pull requests is cumbersome on our end. + - If you're comfortable enough with Phorge to contribute to it, you + should also be comfortable using it to submit changes. + +Instead of sending a pull request, use `arc diff` to create a revision on the +upstream install. Your change will go through the normal Phorge review +process. + +(GitHub does not allow repositories to disable pull requests, which is why +it's technically possible to submit them.) + + +Alternatives +============ + +If you've written code but we're not accepting it into the upstream, some +alternative approaches include: + +**Maintain a local fork.** This will require some ongoing effort to port your +changes forward when you update, but is often very reasonable for simple +changes. + +**Develop as an application.** Many parts of Phorge's infrastructure are +modular, and modularity is increasing over time. A lot of changes can be built +as external modules or applications without forking Phorge itself. There +isn't much documentation for this right now, but you can look at +how other applications are implemented, and at other third-party code that +extends Phorge. + +**Rise to prominence.** We're more willing to accept borderline changes from +community members who are active, make multiple contributions, or have a history +with the project. This is not carte blanche, but distinguishing yourself can +make us feel more comfortable about supporting a change which is slightly +outside of our comfort zone. + + +Writing and Submitting Patches +================== + +To actually submit a patch, run `arc diff` in `phorge/` or `arcanist/`. +When executed in these directories, `arc` should automatically talk to the +upstream install. You can add #blessed_reviewers as a reviewer. + +You should read the relevant coding convention documents before you submit a +change. If you're a new contributor, you don't need to worry about this too +much. Just try to make your code look similar to the code around it, and we +can help you through the details during review. + + - @{article:General Coding Standards} (for all languages) + - @{article:PHP Coding Standards} (for PHP) + - @{article:Javascript Coding Standards} (for Javascript) + +In general, if you're coordinating with us first, we can usually provide +guidance on how to implement things. The other articles in this section also +provide information on how to work in the Phorge codebase. + + +Next Steps +========== + +Continue by: + + - returning to the @{article:Contributor Introduction}. diff --git a/src/docs/contributor/css_coding_standards.diviner b/src/docs/contributor/css_coding_standards.diviner index c321124eae..e83778f24c 100644 --- a/src/docs/contributor/css_coding_standards.diviner +++ b/src/docs/contributor/css_coding_standards.diviner @@ -1,14 +1,14 @@ @title CSS Coding Standards @group standards -This document describes CSS features and coding standards for Phabricator. +This document describes CSS features and coding standards for Phorge. = Overview = This document describes technical and style guidelines for writing CSS in -Phabricator. +Phorge. -Phabricator has a limited CSS preprocessor. This document describes the features +Phorge has a limited CSS preprocessor. This document describes the features it makes available. = Z-Indexes = @@ -19,7 +19,7 @@ Great Z-Index War where all indexes grow without bound in an endless arms race. = Color Variables = -Phabricator's preprocessor provides some standard color variables. You can +Phorge's preprocessor provides some standard color variables. You can reference these with `{$color}`. For example: lang=css @@ -60,7 +60,7 @@ by the media query. = Device Rules = -Phabricator's environment defines several device classes which can be used to +Phorge's environment defines several device classes which can be used to adjust behavior responsively. In particular: lang=css @@ -86,6 +86,6 @@ either tablets or phones: = Image Inlining = -Phabricator's CSS preprocessor automatically inlines images which are less than +Phorge's CSS preprocessor automatically inlines images which are less than 32KB using `data:` URIs. This is primarily useful for gradients or textures which are small and difficult to sprite. diff --git a/src/docs/contributor/database.diviner b/src/docs/contributor/database.diviner index aaea485dc6..fc39c1ff1c 100644 --- a/src/docs/contributor/database.diviner +++ b/src/docs/contributor/database.diviner @@ -7,10 +7,10 @@ questions like how to store new types of data. Database System =============== -Phabricator uses MySQL or another MySQL-compatible database (like MariaDB +Phorge uses MySQL or another MySQL-compatible database (like MariaDB or Amazon RDS). -Phabricator uses the InnoDB table engine. The only exception is the +Phorge uses the InnoDB table engine. The only exception is the `search_documentfield` table which uses MyISAM because MySQL doesn't support fulltext search in InnoDB (recent versions do, but we haven't added support yet). @@ -21,22 +21,22 @@ SQLite. PHP Drivers =========== -Phabricator supports [[ http://www.php.net/book.mysql | MySQL ]] and +Phorge supports [[ http://www.php.net/book.mysql | MySQL ]] and [[ http://www.php.net/book.mysqli | MySQLi ]] PHP extensions. Databases ========= -Each Phabricator application has its own database. The names are prefixed by -`phabricator_` (this is configurable). +Each Phorge application has its own database. The names are prefixed by +`phorge_` (this is configurable). -Phabricator uses a separate database for each application. To understand why, -see @{article:Why does Phabricator need so many databases?}. +Phorge uses a separate database for each application. To understand why, +see @{article:Why does Phorge need so many databases?}. Connections =========== -Phabricator specifies if it will use any opened connection just for reading or +Phorge specifies if it will use any opened connection just for reading or also for writing. This allows opening write connections to a primary and read connections to a replica in primary/replica setups (which are not actually supported yet). @@ -45,7 +45,7 @@ Tables ====== Most table names are prefixed by their application names. For example, -Differential revisions are stored in database `phabricator_differential` and +Differential revisions are stored in database `phorge_differential` and table `differential_revision`. This generally makes queries easier to recognize and understand. @@ -57,7 +57,7 @@ We use lower-case table names with words separated by underscores. Column Names ============ -Phabricator uses `camelCase` names for columns. The main advantage is that they +Phorge uses `camelCase` names for columns. The main advantage is that they directly map to properties in PHP classes. Don't use MySQL reserved words (such as `order`) for column names. @@ -65,17 +65,17 @@ Don't use MySQL reserved words (such as `order`) for column names. Data Types ========== -Phabricator defines a set of abstract data types (like `uint32`, `epoch`, and +Phorge defines a set of abstract data types (like `uint32`, `epoch`, and `phid`) which map to MySQL column types. The mapping depends on the MySQL version. -Phabricator uses `utf8mb4` character sets where available (MySQL 5.5 or newer), +Phorge uses `utf8mb4` character sets where available (MySQL 5.5 or newer), and `binary` character sets in most other cases. The primary motivation is to allow 4-byte unicode characters to be stored (the `utf8` character set, which is more widely available, does not support them). On newer MySQL, we use `utf8mb4` to take advantage of improved collation rules. -Phabricator stores dates with an `epoch` abstract data type, which maps to +Phorge stores dates with an `epoch` abstract data type, which maps to `int unsigned`. Although this makes dates less readable when browsing the database, it makes date and time manipulation more consistent and straightforward in the application. @@ -134,8 +134,8 @@ eventually, but there isn't a strong case for them at the present time. PHIDs ===== -Each globally referencable object in Phabricator has an associated PHID -("Phabricator ID") which serves as a global identifier, similar to a GUID. +Each globally referencable object in Phorge has an associated PHID +("Phorge ID") which serves as a global identifier, similar to a GUID. We use PHIDs for referencing data in different databases. We use both auto-incrementing IDs and global PHIDs because each is useful in @@ -150,6 +150,8 @@ are permitted to subscribe to different types of objects (revisions, tasks, etc). Without PHIDs, we would need to add a "type" column to avoid ID collision; using PHIDs makes implementing features like this simpler. +For more information, see @{article:Handles Technical Documentation} + Transactions ============ @@ -169,7 +171,7 @@ update, and understand than application code. Schema Denormalization ====================== -Phabricator uses schema denormalization sparingly. Avoid denormalization unless +Phorge uses schema denormalization sparingly. Avoid denormalization unless there is a compelling reason (usually, performance) to denormalize. Schema Changes and Migrations diff --git a/src/docs/contributor/describing_problems.diviner b/src/docs/contributor/describing_problems.diviner index d06d7b7d64..30f3b4cad0 100644 --- a/src/docs/contributor/describing_problems.diviner +++ b/src/docs/contributor/describing_problems.diviner @@ -37,21 +37,21 @@ a problem and why it's important for you to resolve it. Here are some examples of good ways to start a problem description: -> My company does contracting work for government agencies. Because of the -> nature of our customers, deadlines are critical and it's very important -> for us to keep track of where we are on a timeline. We're using Maniphest -> to track tasks... +(NOTE) My company does contracting work for government agencies. Because of the + nature of our customers, deadlines are critical and it's very important +for us to keep track of where we are on a timeline. We're using Maniphest +to track tasks... -> I have poor eyesight, and use a screenreader to help me use software like -> Phabricator in my job as a developer. I'm having difficulty... +(NOTE) I have poor eyesight, and use a screenreader to help me use software like + Phorge in my job as a developer. I'm having difficulty... -> We work on a large server program which has very long compile times. -> Switching branches is a huge pain (you have to rebuild the binary after -> every switch, which takes about 8 minutes), but we've recently begun using -> `git worktree` to help, which has made life a lot better. However, ... +(NOTE) We work on a large server program which has very long compile times. + Switching branches is a huge pain (you have to rebuild the binary after + every switch, which takes about 8 minutes), but we've recently begun using + `git worktree` to help, which has made life a lot better. However, ... -> I triage manual test failures from our offshore QA team. Here's how our -> workflow works... +(NOTE) I triage manual test failures from our offshore QA team. Here's how our + workflow works... All of these descriptions are helpful: the provide context about what goals you're trying to accomplish and why. @@ -59,19 +59,19 @@ you're trying to accomplish and why. Here are some examples of ways to start a problem description that probably are not very good: -> {icon times color=red} Add custom keyboard shortcuts. +(IMPORTANT) Add custom keyboard shortcuts. -> {icon times color=red} I have a problem: there is no way to download -> .tar archives of repositories. +(IMPORTANT) I have a problem: there is no way to download + .tar archives of repositories. -> {icon times color=red} I want an RSS feed of my tokens. My root problem is -> that I do not have an RSS feed of my tokens. +(IMPORTANT) I want an RSS feed of my tokens. My root problem is + that I do not have an RSS feed of my tokens. -> {icon times color=red} There is no way to see other users' email addresses. -> That is a problem. +(IMPORTANT) There is no way to see other users' email addresses. + That is a problem. -> {icon times color=red} I've used some other software that has a cool -> feature. Phabricator should have that feature too. +(IMPORTANT) I've used some other software that has a cool + feature. Phorge should have that feature too. These problem descriptions are not helpful. They do not describe goals or provide context. diff --git a/src/docs/contributor/developer_setup.diviner b/src/docs/contributor/developer_setup.diviner index 95508ccd19..8c39c5fc3d 100644 --- a/src/docs/contributor/developer_setup.diviner +++ b/src/docs/contributor/developer_setup.diviner @@ -1,19 +1,19 @@ @title Developer Setup @group developer -How to configure a Phabricator development environment. +How to configure a Phorge development environment. Overview ======== There are some options and workflows that may be useful if you are developing -or debugging Phabricator. +or debugging Phorge. Configuration ============= -To adjust Phabricator for development: +To adjust Phorge for development: - Enable `phabricator.developer-mode` to enable some options and show more debugging information. @@ -48,17 +48,17 @@ After adding, renaming, or moving classes, run `arc liberate` to rebuild the class map: ``` -phabricator/ $ arc liberate +phorge/ $ arc liberate ``` -Until you do this, Phabricator won't recognize your new, moved, or renamed +Until you do this, Phorge won't recognize your new, moved, or renamed classes. You do not need to run this after modifying an existing class. After any modifications to static resources (CSS / JS) but before sending changes for review or pushing them to the remote, run `bin/celerity map`: ``` -phabricator/ $ ./bin/celerity map +phorge/ $ ./bin/celerity map ``` This rebuilds the static resource map. @@ -91,7 +91,7 @@ Phame) you can normally test them by adding more entries to your webserver configuration that look exactly like the primary entry (or expanding the primary entry to match more domains). -Phabricator routes all requests based on host headers, so alternate domains +Phorge routes all requests based on host headers, so alternate domains do not normally need any kind of special configuration. You may also need to add `/etc/hosts` entries for the domains themselves. @@ -103,8 +103,8 @@ Creating Test Data You can create test objects with the "Lipsum" utility: ``` -phabricator/ $ ./bin/lipsum help generate -phabricator/ $ ./bin/lipsum generate ... +phorge/ $ ./bin/lipsum help generate +phorge/ $ ./bin/lipsum generate ... ``` Test data can make your local install feel a little more realistic. With diff --git a/src/docs/contributor/feature_requests.diviner b/src/docs/contributor/feature_requests.diviner index 20fe4b2d30..b2ca702cec 100644 --- a/src/docs/contributor/feature_requests.diviner +++ b/src/docs/contributor/feature_requests.diviner @@ -1,4 +1,211 @@ @title Contributing Feature Requests @group detail -Effective June 1, 2021: Phabricator is no longer actively maintained, and there is no way to file a feature request. +Describes how to file an effective Phorge feature request. + +Overview +======== + +Phorge is an open-source project, and welcomes feature requests from the community +at large. However, there are some guidelines we ask you to follow. + +Overview +======== + +This article describes how to file an effective feature request. + +The most important things to do are: + + - understand the upstream; + - make sure your feature makes sense in the project; + - align your expectations around timelines and priorities; + - describe your problem, not your solution. + +The rest of this article walks through these points in detail. + +If you have a bug report (not a feature request), see +@{article:Contributing Bug Reports} for a more tailored guide. + +For general information on contributing to Phorge, see +@{article:Contributor Introduction}. + + +Understanding the Upstream +========================== + +Before filing a feature request, it may be useful to understand how the +upstream operates. + +Phorge has a designated core team who controls the project and roadmap. +We have a cohesive vision for the project in the long term, and a general +roadmap that extends for years into the future. While the specifics of how +we get there are flexible, many major milestones are well-established. + +Although we set project direction, the community is also a critical part of +Phorge. We aren't all-knowing, and we rely on feedback to help us identify +issues, guide product direction, prioritize changes, and suggest features. + +Feature requests are an important part of this, but we ultimately build only +features which make sense as part of the long term plan. + +Since it's hard to absorb a detailed understanding of that vision, //describing +a problem// is often more effective than //requesting a feature//. We have the +context to develop solutions which fit into our plans, address similar use +cases, make sense with the available infrastructure, and work within the +boundaries of our product vision. For more details on this, see below. + + +Target Audiences +================ + +Some feature requests support very unusual use cases. Although we are broadly +inclusive of many different kinds of users and use cases, we are not trying +to make the software all things to all users. Use cases which are far afield +from the things the majority of users do with Phorge often face substantial +barriers. + +Phorge is primarily targeted at software projects and organizations with +a heavy software focus. We are most likely to design, build, and prioritize +features which serve these organizations and projects. + +Phorge is primarily targeted at software professionals and other +professionals with adjacent responsibilities (like project management and +operations). Particularly, we assume users are proficient computer users and +familiar with software development concepts. We are most likely to design, build +and prioritize features which serve these users. + +Phorge is primarily targeted at professionals working in teams on full-time +projects. Particularly, we assume most users will use the software regularly and +are often willing to spend a little more time up front to get a more efficient +workflow in the long run. We are most likely to design, build and prioritize +features which serve these use cases. + +Phorge is not limited to these kinds of organizations, users and use cases, +but features which are aimed at a different group of users (like students, +casual projects, or inexperienced computer users) may be harder to get +upstreamed. Features aimed at very different groups of users (like wedding +planners, book clubs, or dogs) will be much harder to get upstreamed. + +In many cases, a feature makes something better for all users. For example, +suppose we fixed an issue where colorblind users had difficulty doing something. +Dogs would benefit the most, but colorblind human users would also benefit, and +no one would be worse off. If the benefit for core users is very small these +kinds of features may be hard to prioritize, but there is no exceptional barrier +to getting them upstreamed. + +In other cases, a feature makes something better for some users and worse for +other users. These kinds of features face a high barrier if they make the +software better at planning weddings and worse at reviewing code. + + +Setting Expectations +==================== + +We have a lot of users and a small team. Even if your feature is something we're +interested in and a good fit for where we want the product to go, it may take +us a long time to get around to building it. + +Our long-term roadmap (which we call our +[[ https://we.phorge.it/w/starmap/ | Starmap ]]) has many years worth +of work. Your feature request is competing against thousands of other requests +for priority. + +In general, we try to prioritize work that will have the greatest impact on the +most users. Many feature requests are perfectly reasonable requests, but have +very little impact, impact only a few users, and/or are complex to develop and +support relative to their impact. It can take us a long time to get to these. + +Even if your feature request is simple and has substantial impact for a large +number of users, the size of the request queue means that it is mathematically +unlikely to be near the top. + +As a whole, this means that the overwhelming majority of feature requests will +sit in queue for a long time without any updates, and that we won't be able to +give you any updates or predictions about timelines. One day, out of nowhere, +your feature will materialize. That day may be a decade from now. You should +have realistic expectations about this when filing a feature request. + + +Describe Problems +================= + +When you file a feature request, we need you to describe the problem you're +facing first, not just your desired solution. Describing the problem you are +facing is the **most important part** of a feature request. + +Often, your problem may have a lot in common with other similar problems. If we +understand your use case we can compare it to other use cases and sometimes find +a more powerful or more general solution which solves several problems at once. + +At other times, we'll have a planned solution to the problem that might be +different from your desired solution but accomplish the same goal. Understanding +the root issue can let us merge and contextualize things. + +Sometimes there's already a way to solve your problem that might just not be +obvious. + +Finally, your proposed solution may not be compatible with the direction we +want to take the product, but we may be able to come up with another solution +which has approximately the same effect and does fit into the product direction. + +If you only describe the solution and not the problem, we can't generalize, +contextualize, merge, reframe, or offer alternative solutions or workarounds. + +You must describe the problem you are facing when filing a feature request. We +will not accept feature requests which do not contextualize the request by +describing the root problem. + +If you aren't sure exactly what we're after when we ask you to describe a root +problem, you can find examples and more discussion in +@{article:Describing Root Problems}. + + +Hypotheticals +============= + +We sometimes receive hypothetical feature requests about anticipated problems +or concerns which haven't actually occurred yet. We usually can't do much about +these until the problems actually occur, since the context required to +understand and properly fix the root issue won't exist. + +One situation where this happens is when installs are thinking about adopting +Phorge and trying to guess what problems users might encounter during the +transition. More generally, this includes any request like "if users do **X**, +they might find **Y** confusing", where no actual users have encountered +confusion yet. + +These requests are necessarily missing important context, maybe including the +answers to questions like these: + + - Why did users do **X**? + - What were they trying to do? + - What did they expect to happen? + - How often do users do this? + +The answers to these questions are important in establishing that the issue is +really a problem, figuring out the best solution for it, and prioritizing the +issue relative to other issues. + +Without knowing this information, we can't be confident that we've found a good +solution to the problem, can't know if we've actually fixed the problem, and +can't even know if the issue was really a problem in the first place (some +hypothetical requests describe problems which no users ever encounter). + +We usually can't move forward without this information. In particular, we don't +want to spend time solving hypothetical problems which no real users will ever +encounter: the value of those changes is zero (or negative, by making the +product more complex without providing a benefit), but they consume development +time which could be better spent building much more valuable features. + +Generally, you should wait until a problem actually occurs before filing a +request about it. + + +Next Steps +========== + +Continue by: + + - learning about @{article: Contributing Bug Reports}; or + - reading general support information in @{article:Support Resources}; or + - returning to the @{article:Contributor Introduction}. diff --git a/src/docs/contributor/general_coding_standards.diviner b/src/docs/contributor/general_coding_standards.diviner index 9b151312fd..5127aebbfc 100644 --- a/src/docs/contributor/general_coding_standards.diviner +++ b/src/docs/contributor/general_coding_standards.diviner @@ -1,7 +1,7 @@ @title General Coding Standards @group standards -This document is a general coding standard for contributing to Phabricator, +This document is a general coding standard for contributing to Phorge, Arcanist, and Diviner. = Overview = @@ -60,7 +60,7 @@ And, obviously, don't do this sort of thing: determine if code is fast or slow by measuring it. - Reject performance discussions that aren't rooted in concrete data. -In Phabricator, you can usually use the builtin XHProf profiling to quickly +In Phorge, you can usually use the builtin XHProf profiling to quickly gather concrete performance data. diff --git a/src/docs/tech/handles.diviner b/src/docs/contributor/handles.diviner similarity index 97% rename from src/docs/tech/handles.diviner rename to src/docs/contributor/handles.diviner index 18e33dc133..ffdd0f0705 100644 --- a/src/docs/tech/handles.diviner +++ b/src/docs/contributor/handles.diviner @@ -1,12 +1,12 @@ @title Handles Technical Documentation -@group handles +@group developer Technical overview of Handles. Overview ======== -Most objects in Phabricator have PHIDs, which are globally unique identifiers +Most objects in Phorge have PHIDs, which are globally unique identifiers that look like `PHID-USER-2zw4hwdt4i5b5ypikv6x`. If you know the PHID for an object, you can load a **handle** for that object to get more information about it. diff --git a/src/docs/contributor/internationalization.diviner b/src/docs/contributor/internationalization.diviner index 99c35e675e..84fe2d2591 100644 --- a/src/docs/contributor/internationalization.diviner +++ b/src/docs/contributor/internationalization.diviner @@ -1,12 +1,12 @@ @title Internationalization @group developer -Describes Phabricator translation and localization. +Describes Phorge translation and localization. Overview ======== -Phabricator partially supports internationalization, but many of the tools +Phorge partially supports internationalization, but many of the tools are missing or in a prototype state. This document describes what tools exist today, how to add new translations, @@ -23,7 +23,7 @@ Once you've created a locale, applications can add translations for that locale. For instructions on adding new classes, see -@{article@phabcontrib:Adding New Classes}. +@{article@contrib:Adding New Classes}. Adding Translations to Locale @@ -38,7 +38,7 @@ provide translations into different locales without needing to define those locales themselves. For instructions on adding new classes, see -@{article@phabcontrib:Adding New Classes}. +@{article@contrib:Adding New Classes}. Writing Translatable Code @@ -58,7 +58,7 @@ $dialog->appendParagraph(pht('This is an example.')); ``` This allows the code to return the correct Spanish or German or Russian -version of the text, if the viewer is using Phabricator in one of those +version of the text, if the viewer is using Phorge in one of those languages and a translation is available. Using `pht()` properly so that strings are translatable can be tricky. Briefly, @@ -275,7 +275,7 @@ return pht('This will take %s hour(s).', new PhutilNumber($count)); If you now load the web UI, you'll see "hour(s)" literally in the UI. To fix this so the translation sounds better in English, provide translations for this -string in the @{class@phabricator:PhabricatorUSEnglishTranslation} file: +string in the @{class:PhabricatorUSEnglishTranslation} file: ```lang=php 'This will take %s hour(s).' => array( @@ -378,4 +378,4 @@ Next Steps Continue by: - adding a new locale or translation file with - @{article@phabcontrib:Adding New Classes}. + @{article@contrib:Adding New Classes}. diff --git a/src/docs/contributor/javascript_coding_standards.diviner b/src/docs/contributor/javascript_coding_standards.diviner index 3b47a566a6..39103e94ec 100644 --- a/src/docs/contributor/javascript_coding_standards.diviner +++ b/src/docs/contributor/javascript_coding_standards.diviner @@ -1,12 +1,12 @@ @title Javascript Coding Standards @group standards -This document describes Javascript coding standards for Phabricator and Javelin. +This document describes Javascript coding standards for Phorge and Javelin. = Overview = This document outlines technical and style guidelines which are followed in -Phabricator and Javelin. Contributors should also follow these guidelines. Many +Phorge and Javelin. Contributors should also follow these guidelines. Many of these guidelines are automatically enforced by lint. These guidelines are essentially identical to the Facebook guidelines, since I diff --git a/src/docs/contributor/n_plus_one.diviner b/src/docs/contributor/n_plus_one.diviner index 6d259671a1..21bac266e7 100644 --- a/src/docs/contributor/n_plus_one.diviner +++ b/src/docs/contributor/n_plus_one.diviner @@ -39,7 +39,7 @@ queries which each return 1 result.** This is particularly true if your database is on a different machine which is, say, 1-2ms away on the network. In this case, issuing 100 queries serially has a minimum cost of 100-200ms, even if they can be satisfied instantly by MySQL. This is far higher than the entire -server-side generation cost for most Phabricator pages should be. +server-side generation cost for most Phorge pages should be. = Batching Queries = diff --git a/src/docs/contributor/phabricator_code_layout.diviner b/src/docs/contributor/phorge_code_layout.diviner similarity index 78% rename from src/docs/contributor/phabricator_code_layout.diviner rename to src/docs/contributor/phorge_code_layout.diviner index 422f228a27..fee99ed897 100644 --- a/src/docs/contributor/phabricator_code_layout.diviner +++ b/src/docs/contributor/phorge_code_layout.diviner @@ -1,19 +1,19 @@ -@title Phabricator Code Layout +@title Phorge Code Layout @group developer -Guide to Phabricator code layout, including how URI mapping works through +Guide to Phorge code layout, including how URI mapping works through application class and subdirectory organization best practices. = URI Mapping = -When a user visits a Phabricator URI, the Phabricator infrastructure parses -that URI with a regular expression to determine what controller class to load. +When a user visits a Phorge URI, the Phorge infrastructure parses that URI with + a regular expression to determine what controller class to load. -The Phabricator infrastructure knows where a given controller class lives on +The Phorge infrastructure knows where a given controller class lives on disk from a cache file the Arcanist phutil mapper generates. This mapping should be updated whenever new classes or files are added: - arc liberate /path/to/phabricator/src + arc liberate /path/to/phorge/src Finally, a given controller class will map to an application which will have most of its code in standardized subdirectories and classes. @@ -22,11 +22,11 @@ most of its code in standardized subdirectories and classes. Suppose you were working on the application `Derp`. - phabricator/src/applications/derp/ + phorge/src/applications/derp/ If `Derp` were as simple as possible, it would have one subdirectory: - phabricator/src/applications/derp/controller/ + phorge/src/applications/derp/controller/ containing the file `DerpController.php` with the class @@ -37,25 +37,25 @@ containing the file `DerpController.php` with the class If `Derp` were (relatively) complex, one could reasonably expect to see the following directory layout: - phabricator/src/applications/derp/conduit/ - phabricator/src/applications/derp/constants/ - phabricator/src/applications/derp/controller/ - phabricator/src/applications/derp/editor/ - phabricator/src/applications/derp/exception/ - phabricator/src/applications/derp/query/ - phabricator/src/applications/derp/replyhandler/ - phabricator/src/applications/derp/storage/ - phabricator/src/applications/derp/view/ + phorge/src/applications/derp/conduit/ + phorge/src/applications/derp/constants/ + phorge/src/applications/derp/controller/ + phorge/src/applications/derp/editor/ + phorge/src/applications/derp/exception/ + phorge/src/applications/derp/query/ + phorge/src/applications/derp/replyhandler/ + phorge/src/applications/derp/storage/ + phorge/src/applications/derp/view/ (The following two folders are also likely to be included for JavaScript and CSS respectively. However, static resources are largely outside the scope of this document. See @{article:Adding New CSS and JS}.) - phabricator/webroot/rsrc/js/application/derp/ - phabricator/webroot/rsrc/css/application/derp/ + phorge/webroot/rsrc/js/application/derp/ + phorge/webroot/rsrc/css/application/derp/ -These directories under `phabricator/src/applications/derp/` represent -the basic set of class types from which most Phabricator applications are +These directories under `phorge/src/applications/derp/` represent +the basic set of class types from which most Phorge applications are assembled. Each would contain a class file. For `Derp`, these classes could be something like: diff --git a/src/docs/contributor/php_coding_standards.diviner b/src/docs/contributor/php_coding_standards.diviner index a14acf17f2..bb54478fa3 100644 --- a/src/docs/contributor/php_coding_standards.diviner +++ b/src/docs/contributor/php_coding_standards.diviner @@ -1,13 +1,13 @@ @title PHP Coding Standards @group standards -This document describes PHP coding standards for Phabricator and related +This document describes PHP coding standards for Phorge and related projects (like Arcanist). = Overview = This document outlines technical and style guidelines which are followed in -Phabricator and Arcanist. Contributors should also follow these guidelines. +Phorge and Arcanist. Contributors should also follow these guidelines. Many of these guidelines are automatically enforced by lint. These guidelines are essentially identical to the Facebook guidelines, since I diff --git a/src/docs/contributor/rendering_html.diviner b/src/docs/contributor/rendering_html.diviner index a8fe5a899d..40892b5ad7 100644 --- a/src/docs/contributor/rendering_html.diviner +++ b/src/docs/contributor/rendering_html.diviner @@ -1,11 +1,11 @@ @title Rendering HTML @group developer -Rendering HTML in the Phabricator environment. +Rendering HTML in the Phorge environment. = Overview = -Phabricator attempts to prevent XSS by treating strings as default-unsafe when +Phorge attempts to prevent XSS by treating strings as default-unsafe when rendering. This means that if you try to build HTML through string concatenation, it won't work: the string will be escaped by the rendering pipeline, and the browser will treat it as plain text, not HTML. @@ -51,7 +51,7 @@ content correctly (without double-escaping): array(), $content)); -In Phabricator, the @{function:javelin_tag} function is similar to +In Phorge, the @{function:javelin_tag} function is similar to @{function@arcanist:phutil_tag}, but provides special handling for the `sigil` and `meta` attributes. @@ -117,7 +117,7 @@ If you need to build a list of items with some element in between each of them = AphrontView Classes = -Subclasses of @{class:AphrontView} in Phabricator should return a +Subclasses of @{class:AphrontView} in Phorge should return a @{class@arcanist:PhutilSafeHTML} object. The easiest way to do this is to return `phutil_tag()` or `javelin_tag()`: diff --git a/src/docs/contributor/reproduction_steps.diviner b/src/docs/contributor/reproduction_steps.diviner index 1050e43c48..4a8d916efc 100644 --- a/src/docs/contributor/reproduction_steps.diviner +++ b/src/docs/contributor/reproduction_steps.diviner @@ -6,7 +6,7 @@ Describes how to provide reproduction steps. Overview ======== -When you submit a bug report about Phabricator, you **MUST** include +When you submit a bug report about Phorge, you **MUST** include reproduction steps. We can not help you with bugs we can not reproduce, and will not accept reports which omit reproduction steps or have incomplete or insufficient instructions. @@ -15,7 +15,7 @@ This document explains what we're looking for in good reproduction steps. Briefly: - Reproduction steps must allow us to reproduce the problem locally on a - clean, up-to-date install of Phabricator. + clean, up-to-date install of Phorge. - Reproduction should be as simple as possible. Good reproduction steps can take time to write out clearly, simplify, and @@ -70,7 +70,7 @@ Reliable Reproduction ===================== When you file a bug report, the first thing we do to fix it is to try to -reproduce the problem locally on an up-to-date install of Phabricator. We will +reproduce the problem locally on an up-to-date install of Phorge. We will do this by following the steps you provide. If we can recreate the issue locally, we can almost always resolve the problem (often very promptly). @@ -80,7 +80,7 @@ settings, feed stories, etc) that we don't have access to. We either can't follow these steps, or can't reproduce issues by following them. Reproduction steps must be complete and self-contained, and must allow -**anyone** to reproduce the issue on a new, empty install of Phabricator. If +**anyone** to reproduce the issue on a new, empty install of Phorge. If the bug you're seeing depends on data or configuration which would not be present on a new install, you need to include that information in your steps. @@ -101,11 +101,7 @@ to private or proprietary data. Now, to verify that your steps provide a complete, self-contained way to reproduce the issue, follow them yourself on a new, empty, up-to-date instance -of Phabricator. - -If you can't easily start an empty instance locally, you can launch an empty -instance on Phacility in about 60 seconds (see the "Resources" section for -details). +of Phorge. If you can follow your steps and reproduce the issue on a clean instance, we'll probably be able to follow them and reproduce the issue ourselves. @@ -128,32 +124,6 @@ them by removing some steps or describing steps more narrowly. For help, see "Simplifying Steps" below. -Resources -========= - -We provide some resources which can make it easier to start building steps, or -to simplify steps. - -**Phacility Test Instances**: You can launch a new, up-to-date instance of -Phabricator on Phacility in about a minute at . -These instances run `stable`. - -You can use these instances to make sure that issues haven't already been -fixed, that they reproduce on a clean install, or that your steps are really -complete, and that the root cause isn't custom code or local extensions. Using -a test instance will avoid disrupting other users on your install. - -**Test Repositories**: There are several test repositories on -`secure.phabricator.com` which you can push commits to if you need to build -an example to demonstrate a problem. - -For example, if you're seeing an issue with a certain filename but the commit -where the problem occurs is in a proprietary internal repository, push a commit -that affects a file with a similar name to a test repository, then reproduce -against the test data. This will allow you to generate steps which anyone can -follow. - - Simplifying Steps ================= @@ -239,10 +209,6 @@ to a minimal reproduction case, we can't accept it as a bug report. These issues are tremendously time consuming for us to pursue and rarely benefit more than one install. -If the issue is important but falls outside the scope of permissible bug -reports, we're happy to provide more tailored support at consulting rates. See -[[ https://secure.phabricator.com/w/consulting/ | Consulting ]] for details. - Next Steps ========== diff --git a/src/docs/contributor/running_builtin_php_webserver.diviner b/src/docs/contributor/running_builtin_php_webserver.diviner index 1e4bdacb5c..b511731dc1 100644 --- a/src/docs/contributor/running_builtin_php_webserver.diviner +++ b/src/docs/contributor/running_builtin_php_webserver.diviner @@ -3,7 +3,7 @@ As of version 5.4.0, the PHP command line interface provides a built-in web server. This web server is designed for developmental purposes only, and should -not be used in production. Phabricator can be executed under it with the +not be used in production. Phorge can be executed under it with the command: - $ php -S localhost:8000 -t path/to/phabricator/webroot/ path/to/phabricator/webroot/index.php + $ php -S localhost:8000 -t path/to/phorge/webroot/ path/to/phorge/webroot/index.php diff --git a/src/docs/contributor/unit_tests.diviner b/src/docs/contributor/unit_tests.diviner index 7977a4a876..35cee09566 100644 --- a/src/docs/contributor/unit_tests.diviner +++ b/src/docs/contributor/unit_tests.diviner @@ -1,11 +1,11 @@ @title Writing Unit Tests @group developer -Simple guide to Arcanist and Phabricator unit tests. +Simple guide to Arcanist and Phorge unit tests. = Overview = -Arcanist and Phabricator provide and use a simple unit test framework. This +Arcanist and Phorge provide and use a simple unit test framework. This document is aimed at project contributors and describes how to use it to add and run tests in these projects or other libphutil libraries. @@ -16,11 +16,11 @@ for information on customizing engines. = Adding Tests = -To add new tests to a Arcanist or Phabricator module: +To add new tests to a Arcanist or Phorge module: - Create a `__tests__/` directory in the module if it doesn't exist yet. - Add classes to the `__tests__/` directory which extend from - @{class:PhabricatorTestCase} (in Phabricator) or + @{class:PhabricatorTestCase} (in Phorge) or @{class@arcanist:PhutilTestCase} (elsewhere). - Run `arc liberate` on the library root so your classes are loadable. @@ -38,7 +38,7 @@ Once you've added test classes, you can run them with: Here's a simple example test: lang=php - class PhabricatorTrivialTestCase extends PhabricatorTestCase { + class PhorgeTrivialTestCase extends PhabricatorTestCase { private $two; @@ -56,7 +56,7 @@ Here's a simple example test: You can see this class at @{class:PhabricatorTrivialTestCase} and run it with: - phabricator/ $ arc unit src/infrastructure/testing/testcase/ + phorge/ $ arc unit src/infrastructure/testing/testcase/ PASS <1ms* testAllIsRightWithTheWorld For more information on writing tests, see @@ -64,7 +64,7 @@ For more information on writing tests, see = Database Isolation = -By default, Phabricator isolates unit tests from the database. It makes a crude +By default, Phorge isolates unit tests from the database. It makes a crude effort to simulate some side effects (principally, ID assignment on insert), but any queries which read data will fail to select any rows and throw an exception about isolation. In general, isolation is good, but this can make certain types diff --git a/src/docs/contributor/using_edges.diviner b/src/docs/contributor/using_edges.diviner index 89d07f4c2f..132d728019 100644 --- a/src/docs/contributor/using_edges.diviner +++ b/src/docs/contributor/using_edges.diviner @@ -6,8 +6,7 @@ Guide to the Edges infrastructure. = Overview = Edges are a generic way of storing a relationship between two objects (like a -Task and its attached files). If you are familiar with the Facebook associations -framework, Phabricator Edges are substantially similar. +Task and its attached files). An edge is defined by a source PHID (the edge origin), a destination PHID (the edge destination) and an edge type (which describes the relationship, diff --git a/src/docs/contributor/using_oauthserver.diviner b/src/docs/contributor/using_oauthserver.diviner index b40496d7cf..3e30065fd1 100644 --- a/src/docs/contributor/using_oauthserver.diviner +++ b/src/docs/contributor/using_oauthserver.diviner @@ -1,19 +1,19 @@ -@title Using the Phabricator OAuth Server +@title Using the Phorge OAuth Server @group developer -How to use the Phabricator OAuth Server. +How to use the Phorge OAuth Server. = Overview = -Phabricator includes an OAuth Server which supports the +Phorge includes an OAuth Server which supports the `Authorization Code Grant` flow as described in the OAuth 2.0 specification: http://tools.ietf.org/html/draft-ietf-oauth-v2-23 This functionality can allow clients to integrate with a given -Phabricator instance in a secure way with granular data access. -For example, Phabricator can be used as a central identity store for any +Phorge instance in a secure way with granular data access. +For example, Phorge can be used as a central identity store for any clients that implement OAuth 2.0. = Vocabulary = @@ -41,7 +41,7 @@ clients that implement OAuth 2.0. = Obtaining an Authorization Code = -POST or GET `https://phabricator.example.com/oauthserver/auth/` with the +POST or GET `https://phorge.example.com/oauthserver/auth/` with the following parameters: - Required - **client_id** - the id of the newly registered client. @@ -69,14 +69,14 @@ redirect_uri with a valid authorization code. If there is an error, the OAuth Server will return a descriptive error message. This error will be presented to the resource owner on the -Phabricator domain if there is reason to believe there is something fishy +Phorge domain if there is reason to believe there is something fishy with the client. For example, if there is an issue with the redirect_uri. Otherwise, the OAuth Server will redirect to the pertinent redirect_uri and include the pertinent error information. = Obtaining an Access Token = -POST or GET `https://phabricator.example.com/oauthserver/token/` +POST or GET `https://phorge.example.com/oauthserver/token/` with the following parameters: - Required - **client_id** - the id of the client @@ -101,7 +101,7 @@ message. Simply include a query param with the key of "access_token" and the value as the earlier obtained access token. For example: -```https://phabricator.example.com/api/user.whoami?access_token=ykc7ly7vtibj334oga4fnfbuvnwz4ocp``` +```https://phorge.example.com/api/user.whoami?access_token=ykc7ly7vtibj334oga4fnfbuvnwz4ocp``` If the token has expired or is otherwise invalid, the client will receive an error indicating as such. In these cases, the client should re-initiate diff --git a/src/docs/contributor/version.diviner b/src/docs/contributor/version.diviner index 8ffafede1b..6e960c7993 100644 --- a/src/docs/contributor/version.diviner +++ b/src/docs/contributor/version.diviner @@ -19,10 +19,10 @@ resolved because valid reproduction steps must also reproduce against a clean, up-to-date install. See @{article:Providing Reproduction Steps} for details. -Phabricator Version -=================== +Phorge Version +============== -To get Phabricator version information: +To get Phorge version information: - Go to the {nav Config} application. You can type "Config" into the global search box, or navigate to `https://your.install.com/config/`. You must @@ -52,7 +52,7 @@ prevents you from reaching the version reporting screen. Running a Fork? =============== -If you've forked Phabricator and have local commits, please make sure you are +If you've forked Phorge and have local commits, please make sure you are reporting upstream commit hashes, not local commit hashes. The UI will attempt to figure out where you branched from, but it may not be able to in all cases. @@ -68,7 +68,7 @@ $ git merge-base HEAD origin/master ``` Note that if you report a bug and have local commits, we will almost always ask -you to reproduce the issue against a clean copy of Phabricator before we +you to reproduce the issue against a clean copy of Phorge before we continue. You can get help faster by doing this //before// reporting an issue. diff --git a/src/docs/flavor/about_flavor_text.diviner b/src/docs/flavor/about_flavor_text.diviner deleted file mode 100644 index 3d46ff41d0..0000000000 --- a/src/docs/flavor/about_flavor_text.diviner +++ /dev/null @@ -1,9 +0,0 @@ -@title About Flavor Text -@group overview - -Explains what's going on here. - -= Overview = - -Flavor Text is a collection of short articles which pertain to software -development in general, not necessarily to Phabricator specifically. diff --git a/src/docs/flavor/project_history.diviner b/src/docs/flavor/project_history.diviner index c3b5363d50..1159304cda 100644 --- a/src/docs/flavor/project_history.diviner +++ b/src/docs/flavor/project_history.diviner @@ -1,30 +1,27 @@ -@title Phabricator Project History +@title Phorge Project History @group lore -A riveting tale of adventure. In this document, I refer to worldly and -sophisticated engineer Evan Priestley as "I", which is only natural as I am he. - -This document is mostly just paragraph after paragraph of self-aggrandizement. +A riveting tale of adventure. = In The Beginning = -I wrote the original version of Differential in one night at a Facebook -Hackathon in April or May 2007, along with Luke Shepard. I joined the company in -April and code review was already an established and mostly-mandatory part of -the culture, but it happened over email and was inefficient and hard to keep -track of. I remember feeling like I was spending a lot of time waiting for code -review to happen, which was a major motivator for building the tool. +Evan Priestley wrote the original version of Differential in one night at a +Facebook Hackathon in April or May 2007, along with Luke Shepard. He joined the +company in April and code review was already an established and mostly-mandatory +part of the culture, but it happened over email and was inefficient and hard to +keep track of. Evan remembers feeling like he was spending a lot of time waiting + for code review to happen, which was a major motivator for building the tool. The original name of the tool was "Diffcamp". Some time earlier there had been an attempt to create a project management tool that was a sort of hybrid between -Trac and Basecamp called "Traccamp". Since we were writing the code review tool -at the height of the brief popularity Traccamp enjoyed, we integrated and called -the new tool Diffcamp even though it had no relation to Basecamp. Traccamp fell -by the wayside shortly thereafter and was eventually removed. +Trac and Basecamp called "Traccamp". Since they were writing the code review tool +at the height of the brief popularity Traccamp enjoyed, Evan and Luke integrated + and called the new tool Diffcamp even though it had no relation to Basecamp. + Traccamp fell by the wayside shortly thereafter and was eventually removed. -However, Diffcamp didn't share its fate. We spent some more time working on it -and got good enough to win hearts and minds over emailing diffs around and was -soon the de facto method of code review at Facebook. +However, Diffcamp didn't share its fate. Evan and Luke spent some more time +working on it and got good enough to win hearts and minds over emailing diffs +around and was soon the de facto method of code review at Facebook. = The Long Bloat = @@ -34,10 +31,10 @@ was 100% SVN in early 2007 but 90%+ of Engineers worked primarily in git with SVN bridging by 2010). As these patches were contributed pretty much randomly, it also gained a lot of performance problems, usability issues, and bugs. -Through 2007 and 2008 I worked mostly on frontend and support infrastructure; -among other things, I wrote a static resource management system called Haste. In -2009 I worked on the Facebook Lite site, where I built the Javelin Javascript -library and an MVC-flavored framework called Alite. +Through 2007 and 2008 Evan worked mostly on frontend and support infrastructure; +among other things, he wrote a static resource management system called Haste. +In 2009 Evan worked on the Facebook Lite site, where he built the Javelin +Javascript library and an MVC-flavored framework called Alite. But by early 2010, Diffcamp was in pretty bad shape. Two years of having random features grafted onto it without real direction had left it slow and difficult @@ -49,12 +46,18 @@ problems. = Differential = -I joined the new Dev Tools team around February 2010 and took over Diffcamp. I -renamed it to Differential, moved it to a new Alite-based infrastructure with -Javelin, and started making it somewhat less terrible. I eventually wrote +Evan joined the new Dev Tools team around February 2010 and took over Diffcamp. +He renamed it to Differential, moved it to a new Alite-based infrastructure with +Javelin, and started making it somewhat less terrible. He eventually wrote Diffusion and built Herald to replace a very difficult-to-use predecessor. These -tools were less negatively received than the older versions. By December 2010 I -started open sourcing them; Haste became //Celerity// and Alite became -//Aphront//. I wrote Maniphest to track open issues with the project in January -or February, left Facebook in April, and shortly after, we open sourced +tools were less negatively received than the older versions. By December 2010, +Evan started open sourcing them; Haste became //Celerity// and Alite became +//Aphront//. He wrote Maniphest to track open issues with the project in January +or February, left Facebook in April, and shortly after, open sourced Phabricator. + += Phork = +In 2021, Evan announced that Phabricator was no longer maintained. A group of +open-source contributors came together and forked it. This new +group called renamed the project "Phorge" and continues to maintain this +beloved and well-used project. diff --git a/src/docs/flavor/recommendations_on_branching.diviner b/src/docs/flavor/recommendations_on_branching.diviner index 38d196ad9b..521b08d873 100644 --- a/src/docs/flavor/recommendations_on_branching.diviner +++ b/src/docs/flavor/recommendations_on_branching.diviner @@ -7,13 +7,13 @@ This document discusses organizing branches in your remote/origin for feature development and release management, not the use of local branches in Git or queues or bookmarks in Mercurial. -This document is purely advisory. Phabricator works with a variety of branching +This document is purely advisory. Phorge works with a variety of branching strategies, and diverging from the recommendations in this document will not impact your ability to use it for code review and source management. = Overview = -This document describes a branching strategy used by Facebook and Phabricator to +This document describes a branching strategy used by Facebook and Phorge to develop software. It scales well and removes the pain associated with most branching strategies. This strategy is most applicable to web applications, and may be less applicable to other types of software. The basics are: @@ -118,7 +118,7 @@ describing similar systems: - Martin Fowler discusses these systems in a 2010 blog post here: [[http://martinfowler.com/bliki/FeatureToggle.html | Martin Fowler's FeatureToggle]]. - - Phabricator just adds config options but defaults them to off. When + - Phorge just adds config options but defaults them to off. When developing, we turn them on locally. Once a feature is ready, we default it on. We have a vastly less context to deal with than most projects, however, and sometimes get away with simply not linking new features in the UI until diff --git a/src/docs/flavor/recommendations_on_revision_control.diviner b/src/docs/flavor/recommendations_on_revision_control.diviner index 8e30e2f4f1..3e5a5a791f 100644 --- a/src/docs/flavor/recommendations_on_revision_control.diviner +++ b/src/docs/flavor/recommendations_on_revision_control.diviner @@ -3,12 +3,12 @@ Project recommendations on how to organize revision control. -This document is purely advisory. Phabricator works with a variety of revision +This document is purely advisory. Phorge works with a variety of revision control strategies, and diverging from the recommendations in this document will not impact your ability to use it for code review and source management. -This is my (epriestley's) personal take on the issue and not necessarily -representative of the views of the Phabricator team as a whole. +This is Evan's personal take on the issue and not necessarily +representative of the views of the Phorge team as a whole. = Overview = diff --git a/src/docs/flavor/so_many_databases.diviner b/src/docs/flavor/so_many_databases.diviner index 74fb21332f..cde2c93825 100644 --- a/src/docs/flavor/so_many_databases.diviner +++ b/src/docs/flavor/so_many_databases.diviner @@ -1,19 +1,19 @@ -@title Why does Phabricator need so many databases? +@title Why does Phorge need so many databases? @group lore -Phabricator uses about 60 databases (and we may have added more by the time you +Phorge uses about 60 databases (and we may have added more by the time you read this document). This sometimes comes as a surprise, since you might assume it would only use one database. The approach we use is designed to work at scale for huge installs with many thousands of users. We care a lot about working well for large installs, and about scaling up gracefully to meet the needs of growing organizations. We want -small startups to be able to install Phabricator and have it grow with them as +small startups to be able to install Phorge and have it grow with them as they expand to many thousands of employees. -A cost of this approach is that it makes Phabricator more difficult to install +A cost of this approach is that it makes Phorge more difficult to install on shared hosts which require a lot of work to create or authorize access to -each database. However, Phabricator does a lot of advanced or complex things +each database. However, Phorge does a lot of advanced or complex things which are difficult to configure or manage on shared hosts, and we don't recommend installing it on a shared host. The install documentation explicitly discourages installing on shared hosts. @@ -26,11 +26,11 @@ operating at scale. Listing Databases ================= -You can get a full list of the databases Phabricator needs with `bin/storage +You can get a full list of the databases Phorge needs with `bin/storage databases`. It will look something like this: ``` -$ /core/lib/phabricator/bin/storage databases +$ /core/lib/phorge/bin/storage databases secure_audit secure_calendar secure_chatlog @@ -89,9 +89,9 @@ tables which are organized into separate application databases, just like it's easier to work with a large project if you organize source files into directories. -If you aren't developing Phabricator and never look at the data in the +If you aren't developing Phorge and never look at the data in the database, you probably won't benefit from this organization. However, if you -are a developer or want to extend Phabricator or look under the hood, it's +are a developer or want to extend Phorge or look under the hood, it's easier to find what you're looking for and work with the tables when they're organized by application. @@ -118,7 +118,7 @@ each database, databases do cost something. However, this cost is an artificial cost imposed by the selected environment, and this is only the first of many issues you'll run into trying to install and -run Phabricator on a shared host. These issues are why we strongly discourage +run Phorge on a shared host. These issues are why we strongly discourage using shared hosts, and recommend against them in the install guide. diff --git a/src/docs/flavor/soon_static_resources.diviner b/src/docs/flavor/soon_static_resources.diviner index 96f28cfe2f..79fbc29819 100644 --- a/src/docs/flavor/soon_static_resources.diviner +++ b/src/docs/flavor/soon_static_resources.diviner @@ -121,6 +121,6 @@ version of CSS and JS into the database. = Reference Implementation: Celerity = -Some of the ideas discussed here are implemented in Phabricator's //Celerity// +Some of the ideas discussed here are implemented in Phorge's's //Celerity// system, which is essentially a simplified version of the //Haste// system used by Facebook. diff --git a/src/docs/flavor/things_you_should_do_now.diviner b/src/docs/flavor/things_you_should_do_now.diviner index 0d3b4135ba..58a5b8eaea 100644 --- a/src/docs/flavor/things_you_should_do_now.diviner +++ b/src/docs/flavor/things_you_should_do_now.diviner @@ -134,5 +134,5 @@ query escaping system the rest of the application does. Hopefully, whatever language you're writing in has good query libraries that can handle escaping for you. If so, use them. If you're using PHP and don't have -a solution in place yet, the Phabricator implementation of `qsprintf()` is +a solution in place yet, the Phorge implementation of `qsprintf()` is similar to Facebook's system and was successful there. diff --git a/src/docs/flavor/writing_reviewable_code.diviner b/src/docs/flavor/writing_reviewable_code.diviner index edff09f7d0..8cebc5d3e4 100644 --- a/src/docs/flavor/writing_reviewable_code.diviner +++ b/src/docs/flavor/writing_reviewable_code.diviner @@ -3,14 +3,14 @@ Project recommendations on how to structure changes. -This document is purely advisory. Phabricator works with a variety of revision +This document is purely advisory. Phorge works with a variety of revision control strategies, and diverging from the recommendations in this document will not impact your ability to use it for code review and source management. = Overview = This document describes a strategy for structuring changes used successfully at -Facebook and in Phabricator. In essence: +Facebook and in Phorge. In essence: - Each commit should be as small as possible, but no smaller. - The smallest a commit can be is a single cohesive idea: don't make commits @@ -71,8 +71,8 @@ make any sense and you would increase the collective complexity. The real goal is for each change to have minimal complexity, line size is just a proxy that is often well-correlated with complexity. -We generally follow these practices in Phabricator. The median change size for -Phabricator is 35 lines. +We generally follow these practices in Phorge. The median change size for +Phorge is 35 lines. = Write Sensible Commit Messages = @@ -146,7 +146,7 @@ really all that important in commit messages include: reasons //why// a change is happening into the commit message. - Although maybe the spelling and grammar shouldn't be egregiously bad? -Phabricator does not have guidelines for this stuff. You can obviously set +Phorge does not have guidelines for this stuff. You can obviously set guidelines at your organization if you prefer, but getting the //why// into the message is the most important part. diff --git a/src/docs/user/cluster/cluster.diviner b/src/docs/user/cluster/cluster.diviner index 2362b80485..10f8e98e4c 100644 --- a/src/docs/user/cluster/cluster.diviner +++ b/src/docs/user/cluster/cluster.diviner @@ -1,7 +1,7 @@ @title Clustering Introduction @group cluster -Guide to configuring Phabricator across multiple hosts for availability and +Guide to configuring Phorge across multiple hosts for availability and performance. @@ -12,7 +12,7 @@ WARNING: This feature is a prototype. Installs should expect a challenging adventure when deploying clusters. In the best of times, configuring a cluster is complex and requires significant operations experience. -Phabricator can be configured to run on multiple hosts with redundant services +Phorge can be configured to run on multiple hosts with redundant services to improve its availability and scalability, and make disaster recovery much easier. @@ -20,7 +20,7 @@ Clustering is more complex to setup and maintain than running everything on a single host, but greatly reduces the cost of recovering from hardware and network failures. -Each Phabricator service has an array of clustering options that can be +Each Phorge service has an array of clustering options that can be configured somewhat independently. Configuring a cluster is inherently complex, and this is an advanced feature aimed at installs with large userbases and experienced operations personnel who need this high degree of flexibility. @@ -55,7 +55,7 @@ See below for a walkthrough of these services in greater detail. Preparing for Clustering ======================== -To begin deploying Phabricator in cluster mode, set up `cluster.addresses` +To begin deploying Phorge in cluster mode, set up `cluster.addresses` in your configuration. This option should contain a list of network address blocks which are considered @@ -65,8 +65,8 @@ hosts in the cluster, so this list should be as small as possible. See "Cluster Whitelist Security" below for discussion. If you are deploying hardware in EC2, a reasonable approach is to launch a -dedicated Phabricator VPC, whitelist the whole VPC as a Phabricator cluster, -and then deploy only Phabricator services into that VPC. +dedicated Phorge VPC, whitelist the whole VPC as a Phorge cluster, +and then deploy only Phorge services into that VPC. If you have additional auxiliary hosts which run builds and tests via Drydock, you should //not// include them in the cluster address definition. For more @@ -84,7 +84,7 @@ When you configure `cluster.addresses`, you should keep the list of trusted cluster hosts as small as possible. Hosts on this list gain additional capabilities, including these: -**Trusted HTTP Headers**: Normally, Phabricator distrusts the load balancer +**Trusted HTTP Headers**: Normally, Phorge distrusts the load balancer HTTP headers `X-Forwarded-For` and `X-Forwarded-Proto` because they may be client-controlled and can be set to arbitrary values by an attacker if no load balancer is deployed. In particular, clients can set `X-Forwarded-For` to any @@ -134,7 +134,7 @@ highest impact on availability and resistance to data loss. This is usually the most important service to make redundant if your focus is on availability and disaster recovery. -Configuring replicas allows Phabricator to run in read-only mode if you lose +Configuring replicas allows Phorge to run in read-only mode if you lose the master and to quickly promote the replica as a replacement. For details, see @{article:Cluster: Databases}. @@ -147,7 +147,7 @@ Configuring multiple repository hosts is complex, but is required before you can add multiple daemon or web hosts. Repository replicas are important for availability if you host repositories -on Phabricator, but less important if you host repositories elsewhere +on Phorge, but less important if you host repositories elsewhere (instead, you should focus on making that service more available). The distributed nature of Git and Mercurial tend to mean that they are @@ -243,7 +243,7 @@ Cluster: Fulltext Search Configuring search services is relatively simple and has no pre-requisites. -By default, Phabricator uses MySQL as a fulltext search engine, so deploying +By default, Phorge uses MySQL as a fulltext search engine, so deploying multiple database hosts will effectively also deploy multiple fulltext search hosts. @@ -257,7 +257,7 @@ Overlaying Services =================== Although hosts can run a single dedicated service type, certain groups of -services work well together. Phabricator clusters usually do not need to be +services work well together. Phorge clusters usually do not need to be very large, so deploying a small number of hosts with multiple services is a good place to start. diff --git a/src/docs/user/cluster/cluster_daemons.diviner b/src/docs/user/cluster/cluster_daemons.diviner index 8cde3e7b7d..53495280ff 100644 --- a/src/docs/user/cluster/cluster_daemons.diviner +++ b/src/docs/user/cluster/cluster_daemons.diviner @@ -1,7 +1,7 @@ @title Cluster: Daemons @group cluster -Configuring Phabricator to use multiple daemon hosts. +Configuring Phorge to use multiple daemon hosts. Overview ======== @@ -38,7 +38,7 @@ Dedicated Daemon Hosts You can launch additional daemon hosts without any special configuration. Daemon hosts must be able to reach other hosts on the network, but do not need -to run any services (like HTTP or SSH). Simply deploy the Phabricator software +to run any services (like HTTP or SSH). Simply deploy the Phorge software and configuration and start the daemons. Normally, there is little reason to deploy dedicated daemon hosts. They can diff --git a/src/docs/user/cluster/cluster_databases.diviner b/src/docs/user/cluster/cluster_databases.diviner index 51bf14d925..c13cc2eba4 100644 --- a/src/docs/user/cluster/cluster_databases.diviner +++ b/src/docs/user/cluster/cluster_databases.diviner @@ -1,12 +1,12 @@ @title Cluster: Databases @group cluster -Configuring Phabricator to use multiple database hosts. +Configuring Phorge to use multiple database hosts. Overview ======== -You can deploy Phabricator with multiple database hosts, configured as a master +You can deploy Phorge with multiple database hosts, configured as a master and a set of replicas. The advantages of doing this are: - faster recovery from disasters by promoting a replica; @@ -15,24 +15,24 @@ and a set of replicas. The advantages of doing this are: This configuration is complex, and many installs do not need to pursue it. -If you lose the master, Phabricator can degrade automatically into read-only +If you lose the master, Phorge can degrade automatically into read-only mode and remain available, but can not fully recover without operational intervention unless the master recovers on its own. -Phabricator will not currently send read traffic to replicas unless the master +Phorge will not currently send read traffic to replicas unless the master has failed, so configuring a replica will not currently spread any load away -from the master. Future versions of Phabricator are expected to be able to +from the master. Future versions of Phorge are expected to be able to distribute some read traffic to replicas. -Phabricator can not currently be configured into a multi-master mode, nor can +Phorge can not currently be configured into a multi-master mode, nor can it be configured to automatically promote a replica to become the new master. There are no current plans to support multi-master mode or autonomous failover, although this may change in the future. -Phabricator applications //can// be partitioned across multiple database +Phorge applications //can// be partitioned across multiple database masters. This does not provide redundancy and generally does not increase resilience or resistance to data loss, but can help you scale and operate -Phabricator. For details, see +Phorge. For details, see @{article:Cluster: Partitioning and Advanced Configuration}. @@ -44,32 +44,32 @@ To begin, set up a replica database server and configure MySQL replication. If you aren't sure how to do this, refer to the MySQL manual for instructions. The MySQL documentation is comprehensive and walks through the steps and options in good detail. You should understand MySQL replication before -deploying it in production: Phabricator layers on top of it, and does not +deploying it in production: Phorge layers on top of it, and does not attempt to abstract it away. -Some useful notes for configuring replication for Phabricator: +Some useful notes for configuring replication for Phorge: -**Binlog Format**: Phabricator issues some queries which MySQL will detect as +**Binlog Format**: Phorge issues some queries which MySQL will detect as unsafe if you use the `STATEMENT` binlog format (the default). Instead, use `MIXED` (recommended) or `ROW` as the `binlog_format`. -**Grant `REPLICATION CLIENT` Privilege**: If you give the user that Phabricator +**Grant `REPLICATION CLIENT` Privilege**: If you give the user that Phorge will use to connect to the replica database server the `REPLICATION CLIENT` -privilege, Phabricator's status console can give you more information about +privilege, Phorge's status console can give you more information about replica health and state. -**Copying Data to Replicas**: Phabricator currently uses a mixture of MyISAM +**Copying Data to Replicas**: Phorge currently uses a mixture of MyISAM and InnoDB tables, so it can be difficult to guarantee that a dump is wholly consistent and suitable for loading into a replica because MySQL uses different consistency mechanisms for the different storage engines. An approach you may want to consider to limit downtime but still produce a -consistent dump is to leave Phabricator running but configured in read-only +consistent dump is to leave Phorge running but configured in read-only mode while dumping: - Stop all the daemons. - Set `cluster.read-only` to `true` and deploy the new configuration. The - web UI should now show that Phabricator is in "Read Only" mode. + web UI should now show that Phorge is in "Read Only" mode. - Dump the database. You can do this with `bin/storage dump --for-replica` to add the `--master-data` flag to the underlying command and include a `CHANGE MASTER ...` statement in the dump. @@ -81,18 +81,18 @@ binary logs on startup with the `expire_logs_days` option. If you do not configure this and do not explicitly purge old logs with `PURGE BINARY LOGS`, the binary logs on disk will grow unboundedly and relatively quickly. -Once you have a working replica, continue below to tell Phabricator about it. +Once you have a working replica, continue below to tell Phorge about it. Configuring Replicas ==================== -Once your replicas are in working order, tell Phabricator about them by +Once your replicas are in working order, tell Phorge about them by configuring the `cluster.databases` option. This option must be configured from -the command line or in configuration files because Phabricator needs to read +the command line or in configuration files because Phorge needs to read it //before// it can connect to databases. -This option value will list all of the database hosts that you want Phabricator +This option value will list all of the database hosts that you want Phorge to interact with: your master and all your replicas. Each entry in the list should have these keys: @@ -105,7 +105,7 @@ should have these keys: host. If omitted, the default from `mysql.user` will be used. - `pass`: //Optional string.// The password to use to connect to this host. If omitted, the default from `mysql.pass` will be used. - - `disabled`: //Optional bool.// If set to `true`, Phabricator will not + - `disabled`: //Optional bool.// If set to `true`, Phorge will not connect to this host. You can use this to temporarily take a host out of service. @@ -113,7 +113,7 @@ When `cluster.databases` is configured the `mysql.host` option is not used. The other MySQL connection configuration options (`mysql.port`, `mysql.user`, `mysql.pass`) are used only to provide defaults. -Once you've configured this option, restart Phabricator for the changes to take +Once you've configured this option, restart Phorge for the changes to take effect, then continue to "Monitoring Replicas" to verify the configuration. @@ -131,7 +131,7 @@ request//. If you are recovering from a disaster, the view this page shows may be partial or misleading, and two requests served by different servers may see different views of the cluster. -**Connection**: Phabricator tries to connect to each configured database, then +**Connection**: Phorge tries to connect to each configured database, then shows the result in this column. If it fails, a brief diagnostic message with details about the error is shown. If it succeeds, the column shows a rough measurement of latency from the current webserver to the database. @@ -141,7 +141,7 @@ things are properly configured and stable, the replicas should be actively replicating and no more than a few seconds behind master, and the master should //not// be replicating from another database. -To report this status, the user Phabricator is connecting as must have the +To report this status, the user Phorge is connecting as must have the `REPLICATION CLIENT` privilege (or the `SUPER` privilege) so it can run the `SHOW SLAVE STATUS` command. The `REPLICATION CLIENT` privilege only enables the user to run diagnostic commands so it should be reasonable to grant it in @@ -163,12 +163,12 @@ see an older view of the world which could be confusing for users: it may appear that their data has been lost, even if it is safe and just hasn't replicated yet. -Phabricator will attempt to prevent clients from seeing out-of-date views, but +Phorge will attempt to prevent clients from seeing out-of-date views, but sometimes sending traffic to a delayed replica is the best available option (for example, if the master can not be reached). **Health**: This column shows the result of recent health checks against the -server. After several checks in a row fail, Phabricator will mark the server +server. After several checks in a row fail, Phorge will mark the server as unhealthy and stop sending traffic to it until several checks in a row later succeed. @@ -189,12 +189,12 @@ To test that your configuration can survive a disaster, turn off the master database. Do this with great ceremony, making a cool explosion sound as you run the `mysqld stop` command. -If things have been set up properly, Phabricator should degrade to a temporary +If things have been set up properly, Phorge should degrade to a temporary read-only mode immediately. After a brief period of unresponsiveness, it will degrade further into a longer-term read-only mode. For details on how this works internally, see "Unreachable Masters" below. -Once satisfied, turn the master back on. After a brief delay, Phabricator +Once satisfied, turn the master back on. After a brief delay, Phorge should recognize that the master is healthy again and recover fully. Throughout this process, the {nav Database Servers} console will show a @@ -202,7 +202,7 @@ current view of the world from the perspective of the web server handling the request. You can use it to monitor state. You can perform a more narrow test by enabling `cluster.read-only` in -configuration. This will put Phabricator into read-only mode immediately +configuration. This will put Phorge into read-only mode immediately without turning off any databases. You can use this mode to understand which capabilities will and will not be @@ -211,7 +211,7 @@ accessible in a disaster (like wiki pages or contact information) is really accessible. See the next section, "Degradation to Read Only Mode", for more details about -when, why, and how Phabricator degrades. +when, why, and how Phorge degrades. If you run custom code or extensions, they may not accommodate read-only mode properly. You should specifically test that they function correctly in @@ -221,14 +221,14 @@ read-only mode and do not prevent you from accessing important information. Degradation to Read-Only Mode ============================= -Phabricator will degrade to read-only mode when any of these conditions occur: +Phorge will degrade to read-only mode when any of these conditions occur: - you turn it on explicitly; - you configure cluster mode, but don't set up any masters; - the master can not be reached while handling a request; or - recent attempts to connect to the master have consistently failed. -When Phabricator is running in read-only mode, users can still read data and +When Phorge is running in read-only mode, users can still read data and browse and clone repositories, but they can not edit, update, or push new changes. For example, users can still read disaster recovery information on the wiki or emergency contact information on user profiles. @@ -239,14 +239,14 @@ reasons you might want to do this include: - to test that the mode works like you expect it to; - to make sure that information you need will be available; - to prevent new writes while performing database maintenance; or - - to permanently archive a Phabricator install. + - to permanently archive a Phorge install. You can also enable this mode implicitly by configuring `cluster.databases` but disabling the master, or by not specifying any host as a master. This may be more convenient than turning it on explicitly during the course of operations work. -If Phabricator is unable to reach the master database, it will degrade into +If Phorge is unable to reach the master database, it will degrade into read-only mode automatically. See "Unreachable Masters" below for details on how this process works. @@ -258,12 +258,12 @@ the new master. See the next section, "Promoting a Replica", for details. Promoting a Replica =================== -If you lose access to the master database, Phabricator will degrade into +If you lose access to the master database, Phorge will degrade into read-only mode. This is described in greater detail below. The easiest way to get out of read-only mode is to restore the master database. If the database recovers on its own or operations staff can revive it, -Phabricator will return to full working order after a few moments. +Phorge will return to full working order after a few moments. If you can't restore the master or are unsure you will be able to restore the master quickly, you can promote a replica to become the new master instead. @@ -295,16 +295,16 @@ redundancy. Unreachable Masters =================== -This section describes how Phabricator determines that a master has been lost, +This section describes how Phorge determines that a master has been lost, marks it unreachable, and degrades into read-only mode. -Phabricator degrades into read-only mode automatically in two ways: very +Phorge degrades into read-only mode automatically in two ways: very briefly in response to a single connection failure, or more permanently in response to a series of connection failures. In the first case, if a request needs to connect to the master but is not able -to, Phabricator will temporarily degrade into read-only mode for the remainder -of that request. The alternative is to fail abruptly, but Phabricator can +to, Phorge will temporarily degrade into read-only mode for the remainder +of that request. The alternative is to fail abruptly, but Phorge can sometimes degrade successfully and still respond to the user's request, so it makes an effort to finish serving the request from replicas. @@ -314,19 +314,19 @@ if it was a read that did not actually need to use the master it may succeed. This temporary mode is intended to recover as gracefully as possible from brief interruptions in service (a few seconds), like a server being restarted, a network link becoming temporarily unavailable, or brief periods of load-related -disruption. If the anomaly is temporary, Phabricator should recover immediately +disruption. If the anomaly is temporary, Phorge should recover immediately (on the next request once service is restored). This mode can be slow for users (they need to wait on connection attempts to the master which fail) and does not reduce load on the master (requests still attempt to connect to it). -The second way Phabricator degrades is by running periodic health checks +The second way Phorge degrades is by running periodic health checks against databases, and marking them unhealthy if they fail over a longer period of time. This mechanism is very similar to the health checks that most HTTP load balancers perform against web servers. -If a database fails several health checks in a row, Phabricator will mark it as +If a database fails several health checks in a row, Phorge will mark it as unhealthy and stop sending all traffic (except for more health checks) to it. This improves performance during a service interruption and reduces load on the master, which may help it recover from load problems. @@ -336,13 +336,13 @@ console. The "Health" column shows how many checks have run recently and how many have succeeded. Health checks run every 3 seconds, and 5 checks in a row must fail or succeed -before Phabricator marks the database as healthy or unhealthy, so it will +before Phorge marks the database as healthy or unhealthy, so it will generally take about 15 seconds for a database to change state after it goes down or comes up. -If all of the recent checks fail, Phabricator will mark the database as +If all of the recent checks fail, Phorge will mark the database as unhealthy and stop sending traffic to it. If the master was the database that -was marked as unhealthy, Phabricator will actively degrade into read-only mode +was marked as unhealthy, Phorge will actively degrade into read-only mode until it recovers. This mode only attempts to connect to the unhealthy database once every few @@ -350,7 +350,7 @@ seconds to see if it is recovering, so performance will be better on average (users rarely need to wait for bad connections to fail or time out) and the database will receive less load. -Once all of the recent checks succeed, Phabricator will mark the database as +Once all of the recent checks succeed, Phorge will mark the database as healthy again and continue sending traffic to it. Health checks are tracked individually for each web server, so some web servers @@ -397,7 +397,7 @@ might be quick) without needing to restore backups (which might be very slow). Delayed replication is outside the scope of this document, but may be worth considering as an additional data security step on top of backup snapshots depending on your resources and needs. If you configure a delayed replica, do -not add it to the `cluster.databases` configuration: Phabricator should never +not add it to the `cluster.databases` configuration: Phorge should never send traffic to it, and does not need to know about it. diff --git a/src/docs/user/cluster/cluster_devices.diviner b/src/docs/user/cluster/cluster_devices.diviner index c90aa220c4..af29978345 100644 --- a/src/docs/user/cluster/cluster_devices.diviner +++ b/src/docs/user/cluster/cluster_devices.diviner @@ -6,7 +6,7 @@ Guide to configuring hosts to act as cluster devices. Cluster Context =============== -This document describes a step in configuring Phabricator to run on +This document describes a step in configuring Phorge to run on multiple hosts in a cluster configuration. This is an advanced feature. For more information on clustering, see @{article:Clustering Introduction}. @@ -38,7 +38,7 @@ Today, this is primarily necessary when configuring repository clusters. Using Almanac ============= -The tool Phabricator uses to manage cluster devices is the **Almanac** +The tool Phorge uses to manage cluster devices is the **Almanac** application, and most configuration will occur through the application's web UI. If you are not familiar with it, see @{article:Almanac User Guide} first. This document assumes you are familiar with Almanac concepts. @@ -52,7 +52,7 @@ remainder of this document walks through these points in more detail. - Create an Almanac device record for each device. - Generate, add, and trust SSH keys if necessary. - - Install Phabricator on the host. + - Install Phorge on the host. - Use `bin/almanac register` from the host to register it as a device. See below for guidance on each of these steps. @@ -76,7 +76,7 @@ setting up a small cluster (2-3 devices) for the first time. Using **shared keys** makes key management easier but safety checks won't be able to catch a few kinds of mistakes. This may be a better choice if you are setting up a larger cluster, plan to expand the cluster later, or have -experience with Phabricator clustering. +experience with Phorge clustering. Because all cluster keys are all-powerful, there is no material difference between these methods from a security or trust viewpoint. Unique keys are just @@ -87,11 +87,11 @@ easier at larger scales. Create Almanac Device Records ============================= -For each host you plan to make part of a Phabricator cluster, go to the +For each host you plan to make part of a Phorge cluster, go to the {nav Almanac} application and create a **device** record. For guidance on this application, see @{article:Almanac User Guide}. -Add **interfaces** to each device record so Phabricator can tell how to +Add **interfaces** to each device record so Phorge can tell how to connect to these hosts. Normally, you'll add one HTTP interface (usually on port 80) and one SSH interface (by default, on port 2222) to each device: @@ -107,9 +107,9 @@ up with records that look like these: Note that these hosts will normally run two `sshd` ports: the standard `sshd` which you connect to to operate and administrate the host, and the special -Phabricator `sshd` that you connect to to clone and push repositories. +Phorge `sshd` that you connect to to clone and push repositories. -You should specify the Phabricator `sshd` port, **not** the standard `sshd` +You should specify the Phorge `sshd` port, **not** the standard `sshd` port. If you're using **unique** SSH keys for each device, continue to the next step. @@ -141,8 +141,8 @@ in the web UI and run this command from the command line for each key, to mark each key as trusted: ``` -phabricator/ $ ./bin/almanac trust-key --id -phabricator/ $ ./bin/almanac trust-key --id +phorge/ $ ./bin/almanac trust-key --id +phorge/ $ ./bin/almanac trust-key --id ... ``` @@ -153,27 +153,27 @@ policy checks without requiring additional credentials. Guard them carefully! If you need to revoke trust for a key later, use `untrust-key`: ``` -phabricator/ $ ./bin/almanac untrust-key --id +phorge/ $ ./bin/almanac untrust-key --id ``` Once the keys are trusted, continue to the next step. -Install Phabricator +Install Phorge =================== -If you haven't already, install Phabricator on each device you plan to enroll +If you haven't already, install Phorge on each device you plan to enroll in the cluster. Cluster repository devices must provide services over both HTTP and SSH, so you need to install and configure both a webserver and a -Phabricator `sshd` on these hosts. +Phorge `sshd` on these hosts. Generally, you will follow whatever process you otherwise use when installing -Phabricator. +Phorge. NOTE: Do not start the daemons on the new devices yet. They won't work properly until you've finished configuring things. -Once Phabricator is installed, you can enroll the devices in the cluster by +Once Phorge is installed, you can enroll the devices in the cluster by registering them. @@ -223,7 +223,7 @@ with the trusted key. The `--identify-as` flag allows several different hosts to share the same key but still identify as different devices. The overall effect of the `bin/almanac` command is to copy identity and key -files into `phabricator/conf/keys/`. You can inspect the results by examining +files into `phorge/conf/keys/`. You can inspect the results by examining that directory. The helper script just catches potential mistakes and makes sure the process is completed correctly. diff --git a/src/docs/user/cluster/cluster_notifications.diviner b/src/docs/user/cluster/cluster_notifications.diviner index 79c89769fc..2893aefa91 100644 --- a/src/docs/user/cluster/cluster_notifications.diviner +++ b/src/docs/user/cluster/cluster_notifications.diviner @@ -1,7 +1,7 @@ @title Cluster: Notifications @group cluster -Configuring Phabricator to use multiple notification servers. +Configuring Phorge to use multiple notification servers. Overview ======== @@ -29,7 +29,7 @@ never authoritative and never the only way for users to learn about events. For example, if a notification about a task update is not delivered, the next page you load will still show the notification in your notification menu. -Generally, Phabricator works fine without notifications configured at all, so +Generally, Phorge works fine without notifications configured at all, so clustering assumes that losing some messages during a disruption is acceptable. @@ -88,7 +88,7 @@ A simple example with two servers might look like this: ``` -Configuring Phabricator +Configuring Phorge ======================= To configure clustering on the client side, add every service you run to diff --git a/src/docs/user/cluster/cluster_partitioning.diviner b/src/docs/user/cluster/cluster_partitioning.diviner index 20ae11d6a6..cfac0d02ac 100644 --- a/src/docs/user/cluster/cluster_partitioning.diviner +++ b/src/docs/user/cluster/cluster_partitioning.diviner @@ -1,12 +1,12 @@ @title Cluster: Partitioning and Advanced Configuration @group cluster -Guide to partitioning Phabricator applications across multiple database hosts. +Guide to partitioning Phorge applications across multiple database hosts. Overview ======== -You can partition Phabricator's applications across multiple databases. For +You can partition Phorge's applications across multiple databases. For example, you can move an application like Files or Maniphest to a dedicated database host. @@ -18,7 +18,7 @@ The advantages of doing this are: operating the cluster easier. This configuration is complex, and very few installs will benefit from pursuing -it. Phabricator will normally run comfortably with a single database master +it. Phorge will normally run comfortably with a single database master even for large organizations. Partitioning generally does not do much to increase resilience or make it @@ -41,10 +41,10 @@ See "Advanced Configuration", below, for additional discussion. What Partitioning Does ====================== -When you partition Phabricator, you move all of the data for one or more +When you partition Phorge, you move all of the data for one or more applications (like Maniphest) to a new master database host. This is possible -because Phabricator stores data for each application in its own logical -database (like `phabricator_maniphest`) and performs no joins between databases. +because Phorge stores data for each application in its own logical +database (like `phorge_maniphest`) and performs no joins between databases. If you're running into scale limits on a single master database, you can move one or more of your most commonly-used applications to a second database host @@ -78,7 +78,7 @@ each `replica` database follows. Here's a simple example config: { "host": "db001.corporation.com", "role": "master", - "user": "phabricator", + "user": "phorge", "pass": "hunter2!trustno1", "port": 3306, "partition": [ @@ -88,7 +88,7 @@ each `replica` database follows. Here's a simple example config: { "host": "db002.corporation.com", "role": "replica", - "user": "phabricator", + "user": "phorge", "pass": "hunter2!trustno1", "port": 3306, "master": "db001.corporation.com:3306" @@ -96,7 +96,7 @@ each `replica` database follows. Here's a simple example config: { "host": "db003.corporation.com", "role": "master", - "user": "phabricator", + "user": "phorge", "pass": "hunter2!trustno1", "port": 3306, "partition": [ @@ -108,7 +108,7 @@ each `replica` database follows. Here's a simple example config: { "host": "db004.corporation.com", "role": "replica", - "user": "phabricator", + "user": "phorge", "pass": "hunter2!trustno1", "port": 3306, "master": "db003.corporation.com:3306" @@ -137,7 +137,7 @@ configuration. To commit the configuration, run this command: ``` -phabricator/ $ ./bin/storage partition +phorge/ $ ./bin/storage partition ``` Run this command after making any partition or clustering changes. Webservers @@ -154,16 +154,16 @@ To add a new partition, follow these steps: - Add the new database to `cluster.databases`, but keep its "partition" configuration empty (just an empty list). If this is the first time you are partitioning, you will need to configure your existing master as the - new "default". This will let Phabricator interact with it, but won't send + new "default". This will let Phorge interact with it, but won't send any traffic to it yet. - Run `bin/storage partition`. - Run `bin/storage upgrade` to initialize the schemata on the new hosts. - - Stop writes to the applications you want to move by putting Phabricator + - Stop writes to the applications you want to move by putting Phorge in read-only mode, or shutting down the webserver and daemons, or telling everyone not to touch anything. - Dump the data from the application databases on the old master. - Load the data into the application databases on the new master. - - Reconfigure the "partition" setup so that Phabricator knows the databases + - Reconfigure the "partition" setup so that Phorge knows the databases have moved. - Run `bin/storage partition`. - While still in read-only mode, check that all the data appears to be @@ -178,7 +178,7 @@ end-to-end before performing a larger, higher-stakes migration. How Partitioning Works ====================== -If you have multiple masters, Phabricator keeps the entire set of schemata up +If you have multiple masters, Phorge keeps the entire set of schemata up to date on all of them. When you run `bin/storage upgrade` or other storage management commands, they generally affect all masters (if they do not, they will prompt you to be more specific). @@ -197,7 +197,7 @@ There are some exceptions to this rule. For example, all masters keep track of which patches have been applied to that particular master so that `bin/storage upgrade` can upgrade hosts correctly. -Phabricator does not perform joins across logical databases, so there are no +Phorge does not perform joins across logical databases, so there are no meaningful differences in runtime behavior if two applications are on the same physical host or different physical hosts. @@ -212,7 +212,7 @@ only one master. `persistent` //(bool)// Enables persistent connections. Defaults to off. -With persistent connections enabled, Phabricator will keep a pool of database +With persistent connections enabled, Phorge will keep a pool of database connections open between web requests and reuse them when serving subsequent requests. @@ -224,7 +224,7 @@ because requests are unable to bind to an outbound port, enabling this option is likely to fix the issue. This option may also slightly increase performance. The cost of using persistent connections is that you may need to raise the -MySQL `max_connections` setting: although Phabricator will make far fewer +MySQL `max_connections` setting: although Phorge will make far fewer connections, the connections it does make will be longer-lived. Raising this setting will increase MySQL memory requirements and may run into other limits, like `open_files_limit`, which may also need to be raised. diff --git a/src/docs/user/cluster/cluster_repositories.diviner b/src/docs/user/cluster/cluster_repositories.diviner index a2e5fd5b68..2160a73fef 100644 --- a/src/docs/user/cluster/cluster_repositories.diviner +++ b/src/docs/user/cluster/cluster_repositories.diviner @@ -1,12 +1,12 @@ @title Cluster: Repositories @group cluster -Configuring Phabricator to use multiple repository hosts. +Configuring Phorge to use multiple repository hosts. Overview ======== -If you use Git, you can deploy Phabricator with multiple repository hosts, +If you use Git, you can deploy Phorge with multiple repository hosts, configured so that each host is readable and writable. The advantages of doing this are: @@ -22,11 +22,11 @@ This configuration is not currently supported with Subversion or Mercurial. How Reads and Writes Work ========================= -Phabricator repository replicas are multi-master: every node is readable and +Phorge repository replicas are multi-master: every node is readable and writable, and a cluster of nodes can (almost always) survive the loss of any arbitrary subset of nodes so long as at least one node is still alive. -Phabricator maintains an internal version for each repository, and increments +Phorge maintains an internal version for each repository, and increments it when the repository is mutated. Before responding to a read, replicas make sure their version of the repository @@ -77,7 +77,7 @@ similar agents of other rogue nations is beyond the scope of this document. Repository Hosts ================ -Repository hosts must run a complete, fully configured copy of Phabricator, +Repository hosts must run a complete, fully configured copy of Phorge, including a webserver. They must also run a properly configured `sshd`. If you are converting existing hosts into cluster hosts, you may need to @@ -123,7 +123,7 @@ Almanac: - First, register at least one device according to the device clustering instructions. - - Create a new service of type **Phabricator Cluster: Repository** in + - Create a new service of type **Phorge Cluster: Repository** in Almanac. - Bind this service to all the interfaces on the device or devices. - For each binding, add a `protocol` key with one of these values: @@ -170,11 +170,11 @@ To migrate a repository back off a service, use this command: $ ./bin/repository clusterize --remove-service ``` -This command only changes how Phabricator connects to the repository; it does +This command only changes how Phorge connects to the repository; it does not move any data or make any complex structural changes. -When Phabricator needs information about a non-clustered repository, it just -runs a command like `git log` directly on disk. When Phabricator needs +When Phorge needs information about a non-clustered repository, it just +runs a command like `git log` directly on disk. When Phorge needs information about a clustered repository, it instead makes a service call to another server, asking that server to run `git log` instead. @@ -213,9 +213,9 @@ To expand an existing cluster, follow these general steps: For instructions on configuring and registering devices, see @{article:Cluster: Devices}. -As soon as you add active bindings to a service, Phabricator will begin +As soon as you add active bindings to a service, Phorge will begin synchronizing repositories and sending traffic to the new device. You do not -need to copy any repository data to the device: Phabricator will automatically +need to copy any repository data to the device: Phorge will automatically synchronize it. If you have a large amount of repository data, you may want to help this @@ -297,7 +297,7 @@ Configuration}. This screen shows all the configured devices which are hosting the repository and the available version on that device. -**Version**: When a repository is mutated by a push, Phabricator increases +**Version**: When a repository is mutated by a push, Phorge increases an internal version number for the repository. This column shows which version is on disk on the corresponding device. @@ -335,9 +335,9 @@ There are three major cluster failure modes: are reachable. - **Ambiguous Leaders**: The internal state of the repository is unclear. -Phabricator can detect these issues, and responds by freezing the repository +Phorge can detect these issues, and responds by freezing the repository (usually preventing all reads and writes) until the issue is resolved. These -conditions are normally rare and very little data is at risk, but Phabricator +conditions are normally rare and very little data is at risk, but Phorge errs on the side of caution and requires decisions which may result in data loss to be confirmed by a human. @@ -357,13 +357,13 @@ in a brief window during and immediately after a write. This looks like this: - During or immediately after the write, lightning strikes the server and destroys it. -Phabricator can not commit changes to a working copy (stored on disk) and to +Phorge can not commit changes to a working copy (stored on disk) and to the global state (stored in a database) atomically, so there is necessarily a narrow window between committing these two different states when some tragedy can befall a server, leaving the global and local views of the repository state possibly divergent. -In these cases, Phabricator fails into a frozen state where further writes +In these cases, Phorge fails into a frozen state where further writes are not permitted until the failure is investigated and resolved. When a repository is frozen in this way it remains readable. @@ -391,7 +391,7 @@ was complete on disk. To demote the device and release the write lock, run this command: ``` -phabricator/ $ ./bin/repository thaw --demote +phorge/ $ ./bin/repository thaw --demote ``` {icon exclamation-triangle, color="yellow"} Any committed but unacknowledged @@ -411,7 +411,7 @@ this: and destroys it. Here, all of the "leader" devices with the most up-to-date copy of the -repository have been lost. Phabricator will freeze the repository refuse to +repository have been lost. Phorge will freeze the repository refuse to serve requests because it can not serve reads consistently and can not accept new writes without data loss. @@ -424,7 +424,7 @@ quickly, you can use the monitoring console to review which changes are present on the leaders but not present on the followers by examining the push logs. -If you are comfortable discarding these changes, you can instruct Phabricator +If you are comfortable discarding these changes, you can instruct Phorge that it can forget about the leaders by doing this: - Disable the service bindings to all of the leader devices so they are no @@ -434,7 +434,7 @@ that it can forget about the leaders by doing this: To demote a device, run this command: ``` -phabricator/ $ ./bin/repository thaw rXYZ --demote repo002.corp.net +phorge/ $ ./bin/repository thaw rXYZ --demote repo002.corp.net ``` {icon exclamation-triangle, color="red"} Any data which is only present on @@ -450,7 +450,7 @@ devices by using `--demote ` and `--all-repositories`. **This is dangerous and discards all unreplicated data in any repository on any device.** ``` -phabricator/ $ ./bin/repository thaw --demote repo.corp.net --all-repositories +phorge/ $ ./bin/repository thaw --demote repo.corp.net --all-repositories ``` After you do this, continue below to promote a leader and restore the cluster @@ -474,15 +474,15 @@ error, like these: If you are moving repositories into cluster services, you can also reach this state if you use `clusterize` to associate a repository with a service that is -bound to multiple active devices. In this case, Phabricator will not know which +bound to multiple active devices. In this case, Phorge will not know which device or devices have up-to-date information. -When Phabricator can not tell which device in a cluster is a leader, it freezes +When Phorge can not tell which device in a cluster is a leader, it freezes the cluster because it is possible that some devices have less data and others have more, and if it chooses a leader arbitrarily it may destroy some data which you would prefer to retain. -To resolve this, you need to tell Phabricator which device has the most +To resolve this, you need to tell Phorge which device has the most up-to-date data and promote that device to become a leader. If you know all devices have the same data, you are free to promote any device. @@ -496,7 +496,7 @@ Once you have identified a device which has data you're happy with, use device will become authoritative: ``` -phabricator/ $ ./bin/repository thaw rXYZ --promote repo002.corp.net +phorge/ $ ./bin/repository thaw rXYZ --promote repo002.corp.net ``` {icon exclamation-triangle, color="red"} Any data which is only present on @@ -514,7 +514,7 @@ If something issues a `--force` push that destroys branch heads, the mutation will propagate to the replicas. You may be able to manually restore the branches by using tools like the -Phabricator push log or the Git reflog so it is less important to retain +Phorge push log or the Git reflog so it is less important to retain repository snapshots than database snapshots, but it is still possible for data to be lost permanently, especially if you don't notice the problem for some time. @@ -543,8 +543,8 @@ changes may either encounter conflicts or encounter problems with change propagation. You can encounter conflicts because directly modifying the working copy on disk -won't prevent users or Phabricator itself from performing writes to the same -working copy at the same time. Phabricator does not compromise the lower-level +won't prevent users or Phorge itself from performing writes to the same +working copy at the same time. Phorge does not compromise the lower-level locks provided by the VCS so this is theoretically safe -- and this rarely causes any significant problems in practice -- but doesn't make things any simpler or easier. diff --git a/src/docs/user/cluster/cluster_search.diviner b/src/docs/user/cluster/cluster_search.diviner index 25c35aa34a..4bbd1c2fbd 100644 --- a/src/docs/user/cluster/cluster_search.diviner +++ b/src/docs/user/cluster/cluster_search.diviner @@ -4,10 +4,10 @@ Overview ======== -You can configure Phabricator to connect to one or more fulltext search +You can configure Phorge to connect to one or more fulltext search services. -By default, Phabricator will use MySQL for fulltext search. This is suitable +By default, Phorge will use MySQL for fulltext search. This is suitable for most installs. However, alternate engines are supported. @@ -33,10 +33,10 @@ like this: ] ``` -When a user makes a change to a document, Phabricator writes the updated +When a user makes a change to a document, Phorge writes the updated document into every configured, writable fulltext service. -When a user issues a query, Phabricator tries configured, readable services +When a user issues a query, Phorge tries configured, readable services in order until it is able to execute the query successfully. These options are supported by all service types: @@ -141,7 +141,7 @@ After adding new search services, you will need to rebuild document indexes on them. To do this, first initialize the services: ``` -phabricator/ $ ./bin/search init +phorge/ $ ./bin/search init ``` This will perform index setup steps and other one-time configuration. @@ -149,14 +149,14 @@ This will perform index setup steps and other one-time configuration. To populate documents in all indexes, run this command: ``` -phabricator/ $ ./bin/search index --force --background --type all +phorge/ $ ./bin/search index --force --background --type all ``` This initiates an exhaustive rebuild of the document indexes. To get a more detailed list of indexing options available, run: ``` -phabricator/ $ ./bin/search help index +phorge/ $ ./bin/search help index ``` @@ -166,7 +166,7 @@ Advanced Example This is a more advanced example which shows a configuration with multiple different services in different roles. In this example: - - Phabricator is using an Elasticsearch 2 service as its primary fulltext + - Phorge is using an Elasticsearch 2 service as its primary fulltext service. - An Elasticsearch 5 service is online, but only receiving writes. - The MySQL service is serving as a backup if Elasticsearch fails. diff --git a/src/docs/user/cluster/cluster_ssh.diviner b/src/docs/user/cluster/cluster_ssh.diviner index bdd41776f5..57551309a6 100644 --- a/src/docs/user/cluster/cluster_ssh.diviner +++ b/src/docs/user/cluster/cluster_ssh.diviner @@ -1,12 +1,12 @@ @title Cluster: SSH Servers @group cluster -Configuring Phabricator to use multiple SSH servers. +Configuring Phorge to use multiple SSH servers. Overview ======== -You can run Phabricator on multiple SSH servers. The advantages of doing this +You can run Phorge on multiple SSH servers. The advantages of doing this are: - you can completely survive the loss of multiple SSH hosts. @@ -24,14 +24,14 @@ Adding SSH Hosts After configuring repositories in cluster mode, you can add more web hosts at any time. -First, deploy the Phabricator software and configuration to a host, then +First, deploy the Phorge software and configuration to a host, then register the host as a cluster device if it is not already registered (for help, see @{article:Cluster: Devices}.) Once the host is registered, start the SSH server, and then add the host to the SSH load balancer pool. -Phabricator SSH servers are stateless, so you can pull them in and out of +Phorge SSH servers are stateless, so you can pull them in and out of production freely. You may also want to run web services on these hosts, since the service is very diff --git a/src/docs/user/cluster/cluster_webservers.diviner b/src/docs/user/cluster/cluster_webservers.diviner index 7c7c3e8b1f..7483e8f6cd 100644 --- a/src/docs/user/cluster/cluster_webservers.diviner +++ b/src/docs/user/cluster/cluster_webservers.diviner @@ -1,12 +1,12 @@ @title Cluster: Web Servers @group cluster -Configuring Phabricator to use multiple web servers. +Configuring Phorge to use multiple web servers. Overview ======== -You can run Phabricator on multiple web servers. The advantages of doing this +You can run Phorge on multiple web servers. The advantages of doing this are: - you can completely survive the loss of multiple web hosts; and @@ -22,14 +22,14 @@ Adding Web Hosts After configuring repositories in cluster mode, you can add more web hosts at any time. -First, deploy the Phabricator software and configuration to a host, then +First, deploy the Phorge software and configuration to a host, then register the host as a cluster device if it is not already registered (for help, see @{article:Cluster: Devices}.) Once the host is registered, start the web server, and then add the host to the load balancer pool. -Phabricator web servers are stateless, so you can pull them in and out of +Phorge web servers are stateless, so you can pull them in and out of production freely. You may also want to run SSH services on these hosts, since the service is very diff --git a/src/docs/user/configuration/advanced_configuration.diviner b/src/docs/user/configuration/advanced_configuration.diviner index 5721ffd597..8c1389d992 100644 --- a/src/docs/user/configuration/advanced_configuration.diviner +++ b/src/docs/user/configuration/advanced_configuration.diviner @@ -1,21 +1,21 @@ @title Configuration User Guide: Advanced Configuration @group config -Configuring Phabricator for multiple environments. +Configuring Phorge for multiple environments. = Overview = -Phabricator reads configuration from multiple sources. This document explains +Phorge reads configuration from multiple sources. This document explains the configuration stack and how to set up advanced configuration sources, which may be useful for deployments with multiple environments (e.g., development and production). This is a complicated topic for advanced users. You do not need to understand -this topic to install Phabricator. +this topic to install Phorge. = Configuration Sources = -Phabricator supports the following configuration sources, from highest priority +Phorge supports the following configuration sources, from highest priority to lowest priority: - **Database**: Values are stored in the database and edited from the web UI @@ -26,14 +26,14 @@ to lowest priority: - **Config Files**: Values are stored in a config file in `conf/`. The file to use is selected by writing to `conf/local/ENVIRONMENT`, or setting the `PHABRICATOR_ENV` configuration variable. See below for more information. - - **Defaults**: Defaults hard-coded in the Phabricator source, which can not + - **Defaults**: Defaults hard-coded in the Phorge source, which can not be edited. They have the lowest priority, and all other settings override them. -Normally, you install and configure Phabricator by writing enough configuration +Normally, you install and configure Phorge by writing enough configuration into the local config to get access to the database configuration (e.g., the MySQL username, password, and hostname), then use the web interface to further -configure Phabricator. +configure Phorge. = Configuration Files = @@ -52,7 +52,7 @@ examples below. First, write an `exampleconfig.conf.php` file here (rename it according to the name you chose): - phabricator/conf/custom/exampleconfig.conf.php + phorge/conf/custom/exampleconfig.conf.php Its contents should look like this: @@ -77,14 +77,14 @@ a config like this: == Selecting a Configuration File == To select a configuration file, write the name of the file (relative to -`phabricator/conf/`) to `phabricator/conf/local/ENVIRONMENT`. For example, to -select `phabricator/conf/custom/exampleconfig.conf.php`, you would write -"custom/exampleconfig" to `phabricator/conf/local/ENVIRONMENT`: +`phorge/conf/`) to `phorge/conf/local/ENVIRONMENT`. For example, to +select `phorge/conf/custom/exampleconfig.conf.php`, you would write +"custom/exampleconfig" to `phorge/conf/local/ENVIRONMENT`: - phabricator/ $ echo custom/exampleconfig > conf/local/ENVIRONMENT - phabricator/ $ cat conf/local/ENVIRONMENT + phorge/ $ echo custom/exampleconfig > conf/local/ENVIRONMENT + phorge/ $ cat conf/local/ENVIRONMENT custom/exampleconfig - phabricator/ $ + phorge/ $ You can also set the environmental variable `PHABRICATOR_ENV`. This is more involved but may be easier in some deployment environments. Note that this needs @@ -107,8 +107,8 @@ setenv.add-environment = ( ) ``` -After creating and selecting a configuration file, restart Phabricator (for -help, see @{article:Restarting Phabricator}). Any configuration you set should +After creating and selecting a configuration file, restart Phorge (for +help, see @{article:Restarting Phorge}). Any configuration you set should take effect immediately, and your file should be visible in the Config application when examining configuration. diff --git a/src/docs/user/configuration/configuration_guide.diviner b/src/docs/user/configuration/configuration_guide.diviner index 8b221bda41..7798ff0e0b 100644 --- a/src/docs/user/configuration/configuration_guide.diviner +++ b/src/docs/user/configuration/configuration_guide.diviner @@ -1,7 +1,7 @@ @title Configuration Guide @group config -This document contains basic configuration instructions for Phabricator. +This document contains basic configuration instructions for Phorge. = Prerequisites = @@ -11,7 +11,7 @@ If you haven't, see @{article:Installation Guide}. The next steps are: - Configure your webserver (Apache, nginx, or lighttpd). - - Access Phabricator with your browser. + - Access Phorge with your browser. - Follow the instructions to complete setup. = Webserver: Configuring Apache = @@ -24,8 +24,8 @@ documentation for help. Make sure `mod_php` and `mod_rewrite` are enabled, and `mod_ssl` if you intend to set up SSL. If you haven't already, set up a domain name to point to the host you're -installing on. You can either install Phabricator on a subdomain (like -phabricator.example.com) or an entire domain, but you can not install it in +installing on. You can either install Phorge on a subdomain (like +phorge.example.com) or an entire domain, but you can not install it in some subdirectory of an existing website. Navigate to whatever domain you're going to use and make sure Apache serves you something to verify that DNS is correctly configured. @@ -33,32 +33,32 @@ is correctly configured. NOTE: The domain must contain a dot ('.'), i.e. not be just a bare name like 'http://example/'. Some web browsers will not set cookies otherwise. -Now create a VirtualHost entry for Phabricator. It should look something like +Now create a VirtualHost entry for Phorge. It should look something like this: name=httpd.conf # Change this to the domain which points to your host. - ServerName phabricator.example.com + ServerName phorge.example.com - # Change this to the path where you put 'phabricator' when you checked it - # out from GitHub when following the Installation Guide. + # Change this to the path where you put 'phorge' when you checked it + # out from the upstream when following the Installation Guide. # # Make sure you include "/webroot" at the end! - DocumentRoot /path/to/phabricator/webroot + DocumentRoot /path/to/phorge/webroot RewriteEngine on RewriteRule ^(.*)$ /index.php?__path__=$1 [B,L,QSA] If Apache isn't currently configured to serve documents out of the directory -where you put Phabricator, you may also need to add `` section. The +where you put Phorge, you may also need to add `` section. The syntax for this section depends on which version of Apache you're running. (If you don't know, you can usually figure this out by running `httpd -v`.) For Apache versions older than 2.4, use this: name="Apache Older Than 2.4" - + Order allow,deny Allow from all @@ -66,7 +66,7 @@ For Apache versions older than 2.4, use this: For Apache versions 2.4 and newer, use this: name="Apache 2.4 and Newer" - + Require all granted @@ -81,8 +81,8 @@ For nginx, use a configuration like this: name=nginx.conf server { - server_name phabricator.example.com; - root /path/to/phabricator/webroot; + server_name phorge.example.com; + root /path/to/phorge/webroot; location / { index index.php; @@ -121,8 +121,8 @@ up to their sections. For lighttpd, add a section like this to your lighttpd.conf: - $HTTP["host"] =~ "phabricator(\.example\.com)?" { - server.document-root = "/path/to/phabricator/webroot" + $HTTP["host"] =~ "phorge(\.example\.com)?" { + server.document-root = "/path/to/phorge/webroot" url.rewrite-once = ( # This simulates QSA ("query string append") mode in apache "^(/[^?]*)\?(.*)" => "/index.php?__path__=$1&$2", @@ -167,22 +167,22 @@ no one else will be able to sign up or log in. For more information, see During setup, you'll need to configure MySQL. To do this, get MySQL running and verify you can connect to it. Consult the MySQL documentation for help. When -MySQL works, you need to load the Phabricator schemata into it. To do this, run: +MySQL works, you need to load the Phorge schemata into it. To do this, run: - phabricator/ $ ./bin/storage upgrade + phorge/ $ ./bin/storage upgrade If your configuration uses an unprivileged user to connect to the database, you may have to override the default user so the schema changes can be applied with root or some other admin user: - phabricator/ $ ./bin/storage upgrade --user --password + phorge/ $ ./bin/storage upgrade --user --password You can avoid the prompt the script issues by passing the `--force` flag (for example, if you are scripting the upgrade process). - phabricator/ $ ./bin/storage upgrade --force + phorge/ $ ./bin/storage upgrade --force -NOTE: When you update Phabricator, run `storage upgrade` again to apply any +NOTE: When you update Phorge, run `storage upgrade` again to apply any new updates. = Next Steps = @@ -200,7 +200,7 @@ Continue by: @{article:Configuring a Preamble Script}; or - configuring where uploaded files and attachments will be stored with @{article:Configuring File Storage}; or - - configuring Phabricator so it can send mail with + - configuring Phorge so it can send mail with @{article:Configuring Outbound Email}; or - configuring inbound mail with @{article:Configuring Inbound Email}; or - importing repositories with @{article:Diffusion User Guide}; or @@ -209,4 +209,4 @@ Continue by: @{article:Notifications User Guide: Setup and Configuration}; or - configuring backups with @{article:Configuring Backups and Performing Migrations}; or - - contributing to Phabricator with @{article:Contributor Introduction}. + - contributing to Phorge with @{article:Contributor Introduction}. diff --git a/src/docs/user/configuration/configuration_locked.diviner b/src/docs/user/configuration/configuration_locked.diviner index 57ed76c5c7..19b06ea424 100644 --- a/src/docs/user/configuration/configuration_locked.diviner +++ b/src/docs/user/configuration/configuration_locked.diviner @@ -24,7 +24,7 @@ Locked Configuration can edit it from the CLI instead, with `bin/config`: ``` -phabricator/ $ ./bin/config set +phorge/ $ ./bin/config set ``` Some configuration options take complicated values which can be difficult @@ -42,7 +42,7 @@ file: Then, set it with `--stdin` like this: ``` -phabricator/ $ ./bin/config set --stdin < config.json +phorge/ $ ./bin/config set --stdin < config.json ``` A few settings have alternate CLI tools. Refer to the setting page for @@ -57,13 +57,13 @@ locked include: **Required for bootstrapping**: Some options, like `mysql.host`, must be -available before Phabricator can read configuration from the database. +available before Phorge can read configuration from the database. -If you stored `mysql.host` only in the database, Phabricator would not know how +If you stored `mysql.host` only in the database, Phorge would not know how to connect to the database in order to read the value in the first place. These options must be provided in a configuration source which is read earlier -in the bootstrapping process, before Phabricator connects to the database. +in the bootstrapping process, before Phorge connects to the database. **Errors could not be fixed from the web UI**: Some options, like @@ -83,7 +83,7 @@ attacker who has gained access to an administrator account in order to gain greater access. For example, an attacker who could modify `cluster.mailers` (and other -similar options), could potentially reconfigure Phabricator to send mail +similar options), could potentially reconfigure Phorge to send mail through an evil server they controlled, then trigger password resets on other user accounts to compromise them. @@ -105,8 +105,8 @@ administrator accounts) from reading them. You can review (and edit) hidden configuration from the CLI: ``` -phabricator/ $ ./bin/config get -phabricator/ $ ./bin/config set +phorge/ $ ./bin/config get +phorge/ $ ./bin/config set ``` @@ -117,12 +117,12 @@ Locked Configuration With Database Values You may receive a setup issue warning you that a locked configuration key has a value set in the database. Most commonly, this is because: - - In some earlier version of Phabricator, this configuration was not locked. + - In some earlier version of Phorge, this configuration was not locked. - In the past, you or some other administrator used the web UI to set a value. This value was written to the database. - In a later version of the software, the value became locked. -When Phabricator was originally released, locked configuration did not yet +When Phorge was originally released, locked configuration did not yet exist. Locked configuration was introduced later, and then configuration options were gradually locked for a long time after that. @@ -134,10 +134,10 @@ to lock the value. Locking values was more common in the past, and it is now relatively rare for an unlocked value to become locked: when new values are introduced, they are generally locked or hidden appropriately. In most cases, this setup issue only -affects installs that have used Phabricator for a long time. +affects installs that have used Phorge for a long time. -At time of writing (February 2019), Phabricator currently respects these old -database values. However, some future version of Phabricator will refuse to +At time of writing (February 2019), Phorge currently respects these old +database values. However, some future version of Phorge will refuse to read locked configuration from the database, because this improves security if an attacker manages to find a way to bypass restrictions on editing locked configuration from the web UI. @@ -147,19 +147,19 @@ you should move these configuration values from the database to a local config file. Usually, you'll do this by first copying the value from the database: ``` -phabricator/ $ ./bin/config get +phorge/ $ ./bin/config get ``` ...into local configuration: ``` -phabricator/ $ ./bin/config set +phorge/ $ ./bin/config set ``` ...and then removing the database value: ``` -phabricator/ $ ./bin/config delete --database +phorge/ $ ./bin/config delete --database ``` See @{Configuration User Guide: Advanced Configuration} for some more detailed diff --git a/src/docs/user/configuration/configuring_accounts_and_registration.diviner b/src/docs/user/configuration/configuring_accounts_and_registration.diviner index e703c46402..5d28b3342c 100644 --- a/src/docs/user/configuration/configuring_accounts_and_registration.diviner +++ b/src/docs/user/configuration/configuring_accounts_and_registration.diviner @@ -1,12 +1,12 @@ @title Configuring Accounts and Registration @group config -Describes how to configure user access to Phabricator. +Describes how to configure user access to Phorge. Overview ======== -Phabricator supports a number of login systems. You can enable or disable these +Phorge supports a number of login systems. You can enable or disable these systems to configure who can register for and access your install, and how users with existing accounts can login. @@ -18,7 +18,7 @@ support logging in with other credentials. For example: - **LDAP:** Users use LDAP credentials to log in or register. - **OAuth:** Users use accounts on a supported OAuth2 provider (like GitHub, Facebook, or Google) to log in or register. - - **Other Providers:** More providers are available, and Phabricator + - **Other Providers:** More providers are available, and Phorge can be extended with custom providers. See the "Auth" application for a list of available providers. @@ -26,14 +26,14 @@ By default, no providers are enabled. You must use the "Auth" application to add one or more providers after you complete the installation process. After you add a provider, you can link it to existing accounts (for example, -associate an existing Phabricator account with a GitHub OAuth account) or users +associate an existing Phorge account with a GitHub OAuth account) or users can use it to register new accounts (assuming you enable these options). Recovering Inaccessible Accounts ================================ -If you accidentally lock yourself out of Phabricator (for example, by disabling +If you accidentally lock yourself out of Phorge (for example, by disabling all authentication providers), you can normally use the "send a login link" action from the login screen to email yourself a login link and regain access to your account. @@ -43,7 +43,7 @@ can use the `bin/auth` script to recover access to an account. To recover access, run: ``` -phabricator/ $ ./bin/auth recover +phorge/ $ ./bin/auth recover ``` ...where `` is the account username you want to recover access diff --git a/src/docs/user/configuration/configuring_backups.diviner b/src/docs/user/configuration/configuring_backups.diviner index 0e851d01ac..e4e088aa8e 100644 --- a/src/docs/user/configuration/configuring_backups.diviner +++ b/src/docs/user/configuration/configuring_backups.diviner @@ -1,21 +1,21 @@ @title Configuring Backups and Performing Migrations @group config -Advice for backing up Phabricator, or migrating from one machine to another. +Advice for backing up Phorge, or migrating from one machine to another. Overview ======== -Phabricator does not currently have a comprehensive backup system, but creating -backups is not particularly difficult and Phabricator does have a few basic +Phorge does not currently have a comprehensive backup system, but creating +backups is not particularly difficult and Phorge does have a few basic tools which can help you set up a reasonable process. In particular, the things which needs to be backed up are: - the MySQL databases; - hosted repositories; - uploaded files; and - - your Phabricator configuration files. + - your Phorge configuration files. This document discusses approaches for backing up this data. @@ -24,25 +24,25 @@ same steps you would if you were creating a backup and then restoring it, you will just backup the old machine and then restore the data onto the new machine. -WARNING: You need to restart Phabricator after restoring data. +WARNING: You need to restart Phorge after restoring data. -Restarting Phabricator after performing a restore makes sure that caches are +Restarting Phorge after performing a restore makes sure that caches are flushed properly. For complete instructions, see -@{article:Restarting Phabricator}. +@{article:Restarting Phorge}. Backup: MySQL Databases ======================= -Most of Phabricator's data is stored in MySQL, and it's the most important thing +Most of Phorge's data is stored in MySQL, and it's the most important thing to back up. You can run `bin/storage dump` to get a dump of all the MySQL databases. This is a convenience script which just runs a normal `mysqldump`, -but will only dump databases Phabricator owns. +but will only dump databases Phorge owns. Since most of this data is compressible, it may be helpful to run it through gzip prior to storage. For example: - phabricator/ $ ./bin/storage dump --compress --output backup.sql.gz + phorge/ $ ./bin/storage dump --compress --output backup.sql.gz Then store the backup somewhere safe, like in a box buried under an old tree stump. No one will ever think to look for it there. @@ -59,7 +59,7 @@ to uncompress it first, if you compressed it prior to storage.) Backup: Hosted Repositories =========================== -If you host repositories in Phabricator, you should back them up. You can use +If you host repositories in Phorge, you should back them up. You can use `bin/repository list-paths` to show the local paths on disk for each repository. To back them up, copy them elsewhere. @@ -87,11 +87,11 @@ need to do any additional work. **Amazon S3**: If you use Amazon S3, redundancy and backups are built in to the service. This is probably sufficient for most installs. If you trust Amazon with your data //except not really//, you can backup your S3 bucket outside of -Phabricator. +Phorge. **Local Disk**: If you use the local disk storage engine, you'll need to back up files manually. You can do this by creating a copy of the root directory where -you told Phabricator to put files (the `storage.local-disk.path` configuration +you told Phorge to put files (the `storage.local-disk.path` configuration setting). For more information about configuring how files are stored, see @@ -108,15 +108,15 @@ Backup: Configuration Files =========================== You should also backup your configuration files, and any scripts you use to -deploy or administrate Phabricator (like a customized upgrade script). The best +deploy or administrate Phorge (like a customized upgrade script). The best way to do this is to check them into a private repository somewhere and just use whatever backup process you already have in place for repositories. Just copying them somewhere will work fine too, of course. -In particular, you should backup this configuration file which Phabricator +In particular, you should backup this configuration file which Phorge creates: - phabricator/conf/local/local.json + phorge/conf/local/local.json This file contains all of the configuration settings that have been adjusted by using `bin/config set `. @@ -126,14 +126,14 @@ Restore: Configuration Files ============================ To restore configuration files, just copy them into the right locations. Copy -your backup of `local.json` to `phabricator/conf/local/local.json`. +your backup of `local.json` to `phorge/conf/local/local.json`. Security ======== -MySQL dumps have no builtin encryption and most data in Phabricator is stored in +MySQL dumps have no builtin encryption and most data in Phorge is stored in a raw, accessible form, so giving a user access to backups is a lot like giving -them shell access to the machine Phabricator runs on. In particular, a user who +them shell access to the machine Phorge runs on. In particular, a user who has the backups can: - read data that policies do not permit them to see; @@ -141,7 +141,7 @@ has the backups can: - read other users' session and conduit tokens and impersonate them. Some of this information is durable, so disclosure of even a very old backup may -present a risk. If you restrict access to the Phabricator host or database, you +present a risk. If you restrict access to the Phorge host or database, you should also restrict access to the backups. diff --git a/src/docs/user/configuration/configuring_encryption.diviner b/src/docs/user/configuration/configuring_encryption.diviner index 36315506e6..e237573432 100644 --- a/src/docs/user/configuration/configuring_encryption.diviner +++ b/src/docs/user/configuration/configuring_encryption.diviner @@ -6,14 +6,14 @@ Setup guide for configuring encryption. Overview ======== -Phabricator supports at-rest encryption of uploaded file data stored in the +Phorge supports at-rest encryption of uploaded file data stored in the "Files" application. Configuring at-rest file data encryption does not encrypt any other data or resources. In particular, it does not encrypt the database and does not encrypt Passphrase credentials. -Attackers who compromise a Phabricator host can read the master key and decrypt +Attackers who compromise a Phorge host can read the master key and decrypt the data. In most configurations, this does not represent a significant barrier above and beyond accessing the file data. Thus, configuring at-rest encryption is primarily useful for two types of installs: @@ -81,7 +81,7 @@ Format: Raw Data The `raw` storage format is automatically selected for all newly uploaded file data if no key is marked as the `default` key in the keyring. This is -the behavior of Phabricator if you haven't configured anything. +the behavior of Phorge if you haven't configured anything. This format stores raw data without modification. @@ -104,7 +104,7 @@ length, then base64 encoded when represented in `keyring`. You can generate a valid, properly encoded AES256 master key with this command: ``` -phabricator/ $ ./bin/files generate-key --type aes-256-cbc +phorge/ $ ./bin/files generate-key --type aes-256-cbc ``` This mode is generally similar to the default server-side encryption mode @@ -134,7 +134,7 @@ default. To change the format of an individual file, run this command: ``` -phabricator/ $ ./bin/files encode --as F123 [--key ] +phorge/ $ ./bin/files encode --as F123 [--key ] ``` This will change the storage format of the specified file. @@ -167,7 +167,7 @@ to leave the old key in place for now so existing data can be decrypted. To cycle an individual file, run this command: ``` -phabricator/ $ ./bin/files cycle F123 +phorge/ $ ./bin/files cycle F123 ``` Verify that cycling worked properly by examining the command output and @@ -177,7 +177,7 @@ can cycle additional files to gain additional confidence. You can cycle all files with this command: ``` -phabricator/ $ ./bin/files cycle --all +phorge/ $ ./bin/files cycle --all ``` Once all files have been cycled, remove the old master key from the keyring. diff --git a/src/docs/user/configuration/configuring_file_domain.diviner b/src/docs/user/configuration/configuring_file_domain.diviner index 6f7c410435..a54e850eb1 100644 --- a/src/docs/user/configuration/configuring_file_domain.diviner +++ b/src/docs/user/configuration/configuring_file_domain.diviner @@ -6,7 +6,7 @@ Setup guide for an alternate file domain or CDN. Overview ======== -Serving files that users upload from the same domain that Phabricator runs on +Serving files that users upload from the same domain that Phorge runs on is a security risk. In general, doing this creates a risk that users who have permission to upload @@ -17,7 +17,7 @@ history of security issues). The attacker can then trick another user into executing the file and gain access to their session. The best way to mitigate this threat is to serve files from a separate domain. -For example, if Phabricator is hosted at `https://phabricator.example.com/`, +For example, if Phorge is hosted at `https://phorge.example.com/`, you can serve files from `https://files.exampleusercontent.com/`. The alternate file domain should be a completely different domain from your @@ -48,7 +48,7 @@ CloudFront is a CDN service that's part of Amazon Web Services. It makes particular sense to use if you're hosting your install in AWS. To configure it, set up a new CloudFront distribution which is pointed at -your Phabricator install as an origin (make sure you point it at the primary +your Phorge install as an origin (make sure you point it at the primary domain name of your install, not just a load balancer or instance). You do not need to set up a new domain name, which makes setup a bit more straightforward. @@ -57,10 +57,10 @@ the **Allowed HTTP Methods** setting from `GET, HEAD` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE`. Once configured, accessing the distribution's domain name should return a -Phabricator error page indicating that Phabricator does not recognize the +Phorge error page indicating that Phorge does not recognize the domain. If you see this page, it means you've configured things correctly. -Continue to "Configuring Phabricator", below. +Continue to "Configuring Phorge", below. Approach: CloudFlare ======== @@ -68,7 +68,7 @@ Approach: CloudFlare WARNING: You should review all your CloudFlare settings, and be very sure to turn off all JavaScript, HTML, CSS minification and optimization features, including systems like "Rocket Loader". These -features will break Phabricator in strange and mysterious ways that +features will break Phorge in strange and mysterious ways that are unpredictable. Only allow CloudFlare to cache files, and never optimize them. @@ -76,23 +76,23 @@ optimize them. To set up CloudFlare, you'll need to register a second domain and go through their enrollment process to host the alternate domain on their servers. Use a -CNAME record to forward a subdomain to your Phabricator install. +CNAME record to forward a subdomain to your Phorge install. CloudFlare will automatically generate SSL certificates for hosted domains, which can significantly reduce the cost and complexity of setup. Once configured, accessing the CNAME-forwarded subdomain should return a -Phabricator error page indicating that Phabricator does not recognize the +Phorge error page indicating that Phorge does not recognize the domain. If you see this page, it means you've configured things correctly. -Continue to "Configuring Phabricator", below. +Continue to "Configuring Phorge", below. Approach: Self Hosted ======== To do this, just set up a second domain exactly like your primary domain is -set up. When setup is complete, visiting the domain should return a Phabricator -error page indicating that Phabricator does not recognize the domain. This +set up. When setup is complete, visiting the domain should return a Phorge +error page indicating that Phorge does not recognize the domain. This means that you've configured things correctly. Note that if you use SSL (which you should), you'll also need to get a @@ -102,18 +102,18 @@ You can also configure a self-hosted domain to route through a caching server to provide some of the performance benefits of a CDN, but this is advanced and outside the scope of this documentation. -Continue to "Configuring Phabricator", below. +Continue to "Configuring Phorge", below. -Configuring Phabricator +Configuring Phorge ======== -After you've set up a CDN or an alternate domain, configure Phabricator to +After you've set up a CDN or an alternate domain, configure Phorge to recognize the domain. Run this command, providing the domain you have configured in place of the `` token. You should include the protocol, -so an example domain might be `https://cdn.phabcdn.net/`. +so an example domain might be `https://cdn.examplecdn.com/`. - phabricator/ $ ./bin/config set security.alternate-file-domain + phorge/ $ ./bin/config set security.alternate-file-domain -Phabricator should now serve CSS, JS, images, profile pictures, and user +Phorge should now serve CSS, JS, images, profile pictures, and user content through the file domain. You can verify this with "View Source" or by downloading a file and checking the URL. diff --git a/src/docs/user/configuration/configuring_file_storage.diviner b/src/docs/user/configuration/configuring_file_storage.diviner index d6abb22c13..6d7fb247b9 100644 --- a/src/docs/user/configuration/configuring_file_storage.diviner +++ b/src/docs/user/configuration/configuring_file_storage.diviner @@ -6,8 +6,8 @@ Setup file storage and support for large files. Overview ======== -This document describes how to configure Phabricator to support large file -uploads, and how to choose where Phabricator stores files. +This document describes how to configure Phorge to support large file +uploads, and how to choose where Phorge stores files. There are two major things to configure: @@ -17,16 +17,16 @@ There are two major things to configure: The following sections will guide you through this configuration. -How Phabricator Stores Files +How Phorge Stores Files ============================ -Phabricator stores files in "storage engines", which are modular backends +Phorge stores files in "storage engines", which are modular backends that implement access to some storage system (like MySQL, the filesystem, or a cloud storage service like Amazon S3). -Phabricator stores large files by breaking them up into many chunks (a few +Phorge stores large files by breaking them up into many chunks (a few megabytes in size) and storing the chunks in an underlying storage engine. -This makes it easier to implement new storage engines and gives Phabricator +This makes it easier to implement new storage engines and gives Phorge more flexibility in managing file data. The first section of this document discusses configuring your install so that @@ -45,7 +45,7 @@ of the stack. Generally, the minimum value of all the limits is the effective one. To upload large files, you need to increase all the limits to at least -**32MB**. This will allow you to upload file chunks, which will let Phabricator +**32MB**. This will allow you to upload file chunks, which will let Phorge store arbitrarily large files. The settings which limit file uploads are: @@ -62,20 +62,20 @@ somewhat larger than the desired maximum filesize. - **lighttpd**: lighttpd limits requests with the lighttpd `server.max-request-size` directive. -Set the applicable limit to at least **32MB**. Phabricator can not read these +Set the applicable limit to at least **32MB**. Phorge can not read these settings, so it can not raise setup warnings if they are misconfigured. **PHP**: PHP has several directives which limit uploads. These directives are found in `php.ini`. - **post_max_size**: Maximum POST request size PHP will accept. If you - exceed this, Phabricator will give you a useful error. This often defaults - to `8M`. Set this to at least `32MB`. Phabricator will give you a setup + exceed this, Phorge will give you a useful error. This often defaults + to `8M`. Set this to at least `32MB`. Phorge will give you a setup warning about this if it is set too low. - **memory_limit**: For some uploads, file data will be read into memory - before Phabricator can adjust the memory limit. If you exceed this, PHP + before Phorge can adjust the memory limit. If you exceed this, PHP may give you a useful error, depending on your configuration. It is - recommended that you set this to `-1` to disable it. Phabricator will + recommended that you set this to `-1` to disable it. Phorge will give you a setup warning about this if it is set too low. You may also want to configure these PHP options: @@ -87,7 +87,7 @@ You may also want to configure these PHP options: - **upload_max_filesize**: Maximum file size PHP will accept in a raw file upload. This is not normally used when uploading files via drag-and-drop, but affects some other kinds of file uploads. If you exceed this, - Phabricator will give you a useful error. This often defaults to `2M`. Set + Phorge will give you a useful error. This often defaults to `2M`. Set this to at least `32MB`. Once you've adjusted all this configuration, your server will be able to @@ -98,7 +98,7 @@ enable you to store arbitrarily large files. Storage Engines =============== -Phabricator supports several different file storage engines: +Phorge supports several different file storage engines: | Engine | Setup | Cost | Notes | |--------|-------|------|-------| @@ -110,7 +110,7 @@ Phabricator supports several different file storage engines: You can review available storage engines and their configuration by navigating to {nav Applications > Files > Help/Options > Storage Engines} in the web UI. -By default, Phabricator is configured to store files up to 1MB in MySQL, and +By default, Phorge is configured to store files up to 1MB in MySQL, and reject files larger than 1MB. To store larger files, you can either: - increase the MySQL limit to at least 8MB; or @@ -172,7 +172,7 @@ Testing Storage Engines ======================= You can test that things are correctly configured by dragging and dropping -a file onto the Phabricator home page. If engines have been configured +a file onto the Phorge home page. If engines have been configured properly, the file should upload. Migrating Files Between Engines @@ -183,11 +183,11 @@ script to perform migrations. For example, suppose you previously used MySQL but recently set up S3 and want to migrate all your files there. First, migrate one file to make sure things work: - phabricator/ $ ./bin/files migrate --engine amazon-s3 F12345 + phorge/ $ ./bin/files migrate --engine amazon-s3 F12345 If that works properly, you can then migrate everything: - phabricator/ $ ./bin/files migrate --engine amazon-s3 --all + phorge/ $ ./bin/files migrate --engine amazon-s3 --all You can use `--dry-run` to show which migrations would be performed without taking any action. Run `bin/files help` for more options and information. diff --git a/src/docs/user/configuration/configuring_inbound_email.diviner b/src/docs/user/configuration/configuring_inbound_email.diviner index b1ad08b7dd..00861cc93e 100644 --- a/src/docs/user/configuration/configuring_inbound_email.diviner +++ b/src/docs/user/configuration/configuring_inbound_email.diviner @@ -2,15 +2,15 @@ @group config This document contains instructions for configuring inbound email, so users -may interact with some Phabricator applications via email. +may interact with some Phorge applications via email. Preamble ======== -Phabricator can process inbound mail in two general ways: +Phorge can process inbound mail in two general ways: **Handling Replies**: When users reply to email notifications about changes, -Phabricator can turn email into comments on the relevant discussion thread. +Phorge can turn email into comments on the relevant discussion thread. **Creating Objects**: You can configure an address like `bugs@yourcompany.com` to create new objects (like tasks) when users send email. @@ -21,7 +21,7 @@ broader set of changes to objects beyond commenting. (For example, you can use To configure inbound mail, you will generally: - - Configure some mail domain to submit mail to Phabricator for processing. + - Configure some mail domain to submit mail to Phorge for processing. - For handling replies, set `metamta.reply-handler-domain` in your configuration. - For handling email that creates objects, configure inbound addresses in the @@ -34,11 +34,11 @@ Configuration Overview ====================== Usually, the most challenging part of configuring inbound mail is getting mail -delivered to Phabricator for processing. This step can be made much easier if -you use a third-party mail service which can submit mail to Phabricator via +delivered to Phorge for processing. This step can be made much easier if +you use a third-party mail service which can submit mail to Phorge via webhooks. -Some available approaches for delivering mail to Phabricator are: +Some available approaches for delivering mail to Phorge are: | Receive Mail With | Setup | Cost | Notes | |--------|-------|------|-------| @@ -47,37 +47,37 @@ Some available approaches for delivering mail to Phabricator are: | SendGrid | Easy | Cheap | | | Local MTA | Difficult | Free | Discouraged | -The remainder of this document walks through configuring Phabricator to +The remainder of this document walks through configuring Phorge to receive mail, and then configuring your chosen transport to deliver mail -to Phabricator. +to Phorge. Configuring "Reply" Email ========================= -By default, Phabricator uses a `noreply@phabricator.example.com` email address +By default, Phorge uses a `noreply@phorge.example.com` email address as the "From" address when it sends mail. The exact address it uses can be configured with `metamta.default-address`. -When a user takes an action that generates mail, Phabricator sets the +When a user takes an action that generates mail, Phorge sets the "Reply-To" addresss for the mail to that user's name and address. This means that users can reply to email to discuss changes, but: the conversation won't -be recorded in Phabricator; and users will not be able to use email commands +be recorded in Phorge; and users will not be able to use email commands to take actions or make edits. -To change this behavior so that users can interact with objects in Phabricator +To change this behavior so that users can interact with objects in Phorge over email, change the configuration key `metamta.reply-handler-domain` to some domain you configure according to the instructions below, e.g. -`phabricator.example.com`. Once you set this key, email will use a -"Reply-To" like `T123+273+af310f9220ad@phabricator.example.com`, which -- when +`phorge.example.com`. Once you set this key, email will use a +"Reply-To" like `T123+273+af310f9220ad@phorge.example.com`, which -- when configured correctly, according to the instructions below -- will parse incoming email and allow users to interact with Differential revisions, Maniphest tasks, etc. over email. -If you don't want Phabricator to take up an entire domain (or subdomain) you +If you don't want Phorge to take up an entire domain (or subdomain) you can configure a general prefix so you can use a single mailbox to receive mail on. To make use of this set `metamta.single-reply-handler-prefix` to the -prefix of your choice, and Phabricator will prepend this to the "Reply-To" +prefix of your choice, and Phorge will prepend this to the "Reply-To" mail address. This works because everything up to the first (optional) '+' character in an email address is considered the receiver, and everything after is essentially ignored. @@ -87,7 +87,7 @@ Configuring "Create" Email ========================== You can set up application email addresses to allow users to create objects via -email. For example, you could configure `bugs@phabricator.example.com` to +email. For example, you could configure `bugs@phorge.example.com` to create a Maniphest task out of any email which is sent to it. You can find application email settings for each application at: @@ -104,7 +104,7 @@ with the `[ Content source ]` and/or `[ Receiving email address ]` fields to route or handle objects based on which address mail was sent to. You'll also need to configure the actual mail domain to submit mail to -Phabricator by following the instructions below. Phabricator will let you add +Phorge by following the instructions below. Phorge will let you add any address as an application address, but can only process mail which is actually delivered to it. @@ -130,7 +130,7 @@ authenticating senders in the general case (e.g., where you are an open source project and need to interact with users whose email accounts you have no control over). -You can also set `metamta.public-replies`, which will change how Phabricator +You can also set `metamta.public-replies`, which will change how Phorge delivers email. Instead of sending each recipient a unique mail with a personal reply-to address, it will send a single email to everyone with a public reply-to address. This decreases security because anyone who can spoof a "From" address @@ -142,7 +142,7 @@ not received an email about an object can not blindly interact with it. If you enable application email addresses, those addresses also use the weaker "From" authentication mechanism. -NOTE: Phabricator does not currently attempt to verify "From" addresses because +NOTE: Phorge does not currently attempt to verify "From" addresses because this is technically complex, seems unreasonably difficult in the general case, and no installs have had a need for it yet. If you have a specific case where a reasonable mechanism exists to provide sender verification (e.g., DKIM @@ -155,17 +155,17 @@ Testing and Debugging Inbound Email =================================== You can use the `bin/mail` utility to test and review inbound mail. This can -help you determine if mail is being delivered to Phabricator or not: +help you determine if mail is being delivered to Phorge or not: - phabricator/ $ ./bin/mail list-inbound # List inbound messages. - phabricator/ $ ./bin/mail show-inbound # Show details about a message. + phorge/ $ ./bin/mail list-inbound # List inbound messages. + phorge/ $ ./bin/mail show-inbound # Show details about a message. You can also test receiving mail, but note that this just simulates receiving the mail and doesn't send any information over the network. It is primarily aimed at developing email handlers: it will still work properly if your inbound email configuration is incorrect or even disabled. - phabricator/ $ ./bin/mail receive-test # Receive test message. + phorge/ $ ./bin/mail receive-test # Receive test message. Run `bin/mail help ` for detailed help on using these commands. @@ -179,7 +179,7 @@ like this: - Configure a mail domain according to Mailgun's instructions. - Add a Mailgun route with a `catch_all()` rule which takes the action - `forward("https://phabricator.example.com/mail/mailgun/")`. Replace the + `forward("https://phorge.example.com/mail/mailgun/")`. Replace the example domain with your actual domain. - Configure a mailer in `cluster.mailers` with your Mailgun API key. @@ -191,7 +191,7 @@ To process inbound mail from Postmark, configure this URI as your inbound webhook URI in the Postmark control panel: ``` -https:///mail/postmark/ +https:///mail/postmark/ ``` See also the Postmark section in @{article:Configuring Outbound Email} for @@ -206,21 +206,21 @@ To use SendGrid, you need a SendGrid account with access to the "Parse API" for inbound email. Provided you have such an account, configure it like this: - Configure an MX record according to SendGrid's instructions, i.e. add - `phabricator.example.com MX 10 mx.sendgrid.net.` or similar. + `phorge.example.com MX 10 mx.sendgrid.net.` or similar. - Go to the "Parse Incoming Emails" page on SendGrid () and add the domain as the "Hostname". - - Add the URL `https://phabricator.example.com/mail/sendgrid/` as the "Url", + - Add the URL `https://phorge.example.com/mail/sendgrid/` as the "Url", using your domain (and HTTP instead of HTTPS if you are not configured with SSL). - If you get an error that the hostname "can't be located or verified", it means your MX record is either incorrectly configured or hasn't propagated yet. - - Set `metamta.reply-handler-domain` to `phabricator.example.com` + - Set `metamta.reply-handler-domain` to `phorge.example.com` (whatever you configured the MX record for). That's it! If everything is working properly you should be able to send email -to `anything@phabricator.example.com` and it should appear in +to `anything@phorge.example.com` and it should appear in `bin/mail list-inbound` within a few seconds. @@ -264,10 +264,10 @@ probably means something like this: - add your host to /etc/mail/local-host-names; and - restart sendmail. -Now, you can actually configure sendmail to deliver to Phabricator. In +Now, you can actually configure sendmail to deliver to Phorge. In `/etc/aliases`, add an entry like this: - phabricator: "| /path/to/phabricator/scripts/mail/mail_handler.php" + phorge: "| /path/to/phorge/scripts/mail/mail_handler.php" If you use the `PHABRICATOR_ENV` environmental variable to select a configuration, you can pass the value to the script as an argument: @@ -280,12 +280,12 @@ without an argument. After making this change, run `sudo newaliases`. Now you likely need to symlink this script into `/etc/smrsh/`: - sudo ln -s /path/to/phabricator/scripts/mail/mail_handler.php /etc/smrsh/ + sudo ln -s /path/to/phorge/scripts/mail/mail_handler.php /etc/smrsh/ Finally, edit `/etc/mail/virtusertable` and add an entry like this: - @yourdomain.com phabricator@localhost + @yourdomain.com phorge@localhost -That will forward all mail to @yourdomain.com to the Phabricator processing +That will forward all mail to @yourdomain.com to the Phorge processing script. Run `sudo /etc/mail/make` or similar and then restart sendmail with `sudo /etc/init.d/sendmail restart`. diff --git a/src/docs/user/configuration/configuring_outbound_email.diviner b/src/docs/user/configuration/configuring_outbound_email.diviner index 736d4f625c..327bf974ac 100644 --- a/src/docs/user/configuration/configuring_outbound_email.diviner +++ b/src/docs/user/configuration/configuring_outbound_email.diviner @@ -1,17 +1,17 @@ @title Configuring Outbound Email @group config -Instructions for configuring Phabricator to send email and other types of +Instructions for configuring Phorge to send email and other types of messages, like text messages. Overview ======== -Phabricator sends outbound messages through "mailers". Most mailers send +Phorge sends outbound messages through "mailers". Most mailers send email and most messages are email messages, but mailers may also send other types of messages (like text messages). -Phabricator can send outbound messages through multiple different mailers, +Phorge can send outbound messages through multiple different mailers, including a local mailer or various third-party services. Options include: | Send Mail With | Setup | Cost | Inbound | Media | Notes | @@ -41,7 +41,7 @@ options. If you have some internal mail or messaging service you'd like to use you can also write a custom mailer, but this requires digging into the code. -Phabricator sends mail in the background, so the daemons need to be running for +Phorge sends mail in the background, so the daemons need to be running for it to be able to deliver mail. You should receive setup warnings if they are not. For more information on using daemons, see @{article:Managing Daemons with phd}. @@ -50,10 +50,10 @@ not. For more information on using daemons, see Outbound "From" and "To" Addresses ================================== -When Phabricator sends outbound mail, it must select some "From" address to +When Phorge sends outbound mail, it must select some "From" address to send mail from, since mailers require this. -When mail only has "CC" recipients, Phabricator generates a dummy "To" address, +When mail only has "CC" recipients, Phorge generates a dummy "To" address, since some mailers require this and some users write mail rules that depend on whether they appear in the "To" or "CC" line. @@ -65,12 +65,12 @@ contrast, if the address is a real user address, that user will receive a lot of mail they probably don't want. If you plan to configure //inbound// mail later, you usually don't need to do -anything. Phabricator will automatically create a `noreply@` mailbox which +anything. Phorge will automatically create a `noreply@` mailbox which works the right way (accepts and discards all mail it receives) and automatically use it when generating addresses. If you don't plan to configure inbound mail, you may need to configure an -address for Phabricator to use. You can do this by setting +address for Phorge to use. You can do this by setting `metamta.default-address`. @@ -131,7 +131,7 @@ It also supports these local mailers: - `smtp`: Connect directly to an SMTP server. - `test`: Internal mailer for testing. Does not send mail. -You can also write your own mailer by extending `PhabricatorMailAdapter`. +You can also write your own mailer by extending `PhorgeMailAdapter`. The `media` field supports these values: @@ -169,7 +169,7 @@ tricky because of shell escaping. The easiest way to do it is to use the Then set the value like this: ``` -phabricator/ $ ./bin/config set --stdin cluster.mailers < mailers.json +phorge/ $ ./bin/config set --stdin cluster.mailers < mailers.json ``` For alternatives and more information on configuration, see @@ -310,7 +310,7 @@ SendGrid is a third-party email delivery service. You can learn more at . You can configure SendGrid in two ways: you can send via SMTP or via the REST -API. To use SMTP, configure Phabricator to use an `smtp` mailer. +API. To use SMTP, configure Phorge to use an `smtp` mailer. To use the REST API mailer, set `type` to `sendgrid`, then configure these `options`: @@ -343,7 +343,7 @@ do any of this, strongly consider using Postmark or Mailgun instead. To use this mailer, set `type` to `sendmail`, then configure these `options`: - - `message-id`: Optional bool. Set to `false` if Phabricator will not be + - `message-id`: Optional bool. Set to `false` if Phorge will not be able to select a custom "Message-ID" header when sending mail via this mailer. See "Message-ID Headers" below. @@ -365,7 +365,7 @@ To use this mailer, set `type` to `smtp`, then configure these `options`: - `password`: Optional string. Password for authentication. - `protocol`: Optional string. Set to `tls` or `ssl` if necessary. Use `ssl` for Gmail. - - `message-id`: Optional bool. Set to `false` if Phabricator will not be + - `message-id`: Optional bool. Set to `false` if Phorge will not be able to select a custom "Message-ID" header when sending mail via this mailer. See "Message-ID Headers" below. @@ -390,9 +390,9 @@ Testing and Debugging Outbound Email You can use the `bin/mail` utility to test, debug, and examine outbound mail. In particular: - phabricator/ $ ./bin/mail list-outbound # List outbound mail. - phabricator/ $ ./bin/mail show-outbound # Show details about messages. - phabricator/ $ ./bin/mail send-test # Send test messages. + phorge/ $ ./bin/mail list-outbound # List outbound mail. + phorge/ $ ./bin/mail show-outbound # Show details about messages. + phorge/ $ ./bin/mail send-test # Send test messages. Run `bin/mail help ` for more help on using these commands. @@ -406,12 +406,12 @@ You can monitor daemons using the Daemon Console (`/daemon/`, or click Priorities ========== -By default, Phabricator will try each mailer in order: it will try the first +By default, Phorge will try each mailer in order: it will try the first mailer first. If that fails (for example, because the service is not available at the moment) it will try the second mailer, and so on. If you want to load balance between multiple mailers instead of using one as -a primary, you can set `priority`. Phabricator will start with mailers in the +a primary, you can set `priority`. Phorge will start with mailers in the highest priority group and go through them randomly, then fall back to the next group. @@ -441,14 +441,14 @@ like this: } ``` -Phabricator will start with servers in the highest priority group (the group +Phorge will start with servers in the highest priority group (the group with the **largest** `priority` number). In this example, the highest group is `300`, which has the two SMTP servers. They'll be tried in random order first. -If both fail, Phabricator will move on to the next priority group. In this +If both fail, Phorge will move on to the next priority group. In this example, there are no other priority groups. -If it still hasn't sent the mail, Phabricator will try servers which are not +If it still hasn't sent the mail, Phorge will try servers which are not in any priority group, in the configured order. In this example there is only one such server, so it will try to send via Mailgun. @@ -457,14 +457,14 @@ Message-ID Headers ================== Email has a "Message-ID" header which is important for threading messages -correctly in mail clients. Normally, Phabricator is free to select its own +correctly in mail clients. Normally, Phorge is free to select its own "Message-ID" header values for mail it sends. However, some mailers (including Amazon SES) do not allow selection of custom "Message-ID" values and will ignore or replace the "Message-ID" in mail that is submitted through them. -When Phabricator adds other mail headers which affect threading, like +When Phorge adds other mail headers which affect threading, like "In-Reply-To", it needs to know if its "Message-ID" headers will be respected or not to select header values which will produce good threading behavior. If we guess wrong and think we can set a "Message-ID" header when we can't, you @@ -493,11 +493,11 @@ we can set a "Message-ID" header. If the outbound pathway does not actually allow selection of a "Message-ID" header, you can set the `message-id` option on the mailer to `false` to tell -Phabricator that it should not assume it can select a value for this header. +Phorge that it should not assume it can select a value for this header. For example, if you are sending mail via a local Postfix server which then forwards the mail to Amazon SES (a service which does not allow selection of -a "Message-ID" header), your `smtp` configuration in Phabricator should +a "Message-ID" header), your `smtp` configuration in Phorge should specify `"message-id": false`. diff --git a/src/docs/user/configuration/configuring_preamble.diviner b/src/docs/user/configuration/configuring_preamble.diviner index 6b6b9da149..15ae20e75b 100644 --- a/src/docs/user/configuration/configuring_preamble.diviner +++ b/src/docs/user/configuration/configuring_preamble.diviner @@ -6,14 +6,14 @@ Adjust environmental settings (SSL, remote IPs) using a preamble script. Overview ======== -If Phabricator is deployed in an environment where HTTP headers behave oddly +If Phorge is deployed in an environment where HTTP headers behave oddly (usually, because it is behind a load balancer), it may not be able to detect some environmental features (like the client's IP, or the presence of SSL) correctly. You can use a special preamble script to make arbitrary adjustments to the -environment and some parts of Phabricator's configuration in order to fix these -problems and set up the environment which Phabricator expects. +environment and some parts of Phorge's configuration in order to fix these +problems and set up the environment which Phorge expects. Creating a Preamble Script @@ -21,15 +21,15 @@ Creating a Preamble Script To create a preamble script, write a file to: - phabricator/support/preamble.php + phorge/support/preamble.php -(This file is in Phabricator's `.gitignore`, so you do not need to worry about +(This file is in Phorge's `.gitignore`, so you do not need to worry about colliding with `git` or interacting with updates.) This file should be a valid PHP script. If you aren't very familiar with PHP, you can check for syntax errors with `php -l`: - phabricator/ $ php -l support/preamble.php + phorge/ $ php -l support/preamble.php No syntax errors detected in support/preamble.php If present, this script will be executed at the very beginning of each web @@ -40,14 +40,14 @@ examples, see the next sections. Adjusting Client IPs ==================== -If your install is behind a load balancer, Phabricator may incorrectly detect +If your install is behind a load balancer, Phorge may incorrectly detect all requests as originating from the load balancer, rather than from the correct client IPs. In common cases where networks are configured like this, the `X-Forwarded-For` header will have trustworthy information about the real client IP. You can use the function `preamble_trust_x_forwarded_for_header()` in your -preamble to tell Phabricator that you expect to receive requests from a +preamble to tell Phorge that you expect to receive requests from a load balancer or proxy which modifies this header: ```name="Trust X-Forwarded-For Header", lang=php @@ -89,9 +89,9 @@ if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { Adjusting SSL ============= -If your install is behind an SSL terminating load balancer, Phabricator may +If your install is behind an SSL terminating load balancer, Phorge may detect requests as HTTP when the client sees them as HTTPS. This can cause -Phabricator to generate links with the wrong protocol, issue cookies without +Phorge to generate links with the wrong protocol, issue cookies without the SSL-only flag, or reject requests outright. To fix this, you can set `$_SERVER['HTTPS']` explicitly: @@ -103,7 +103,7 @@ name=Explicitly Configure SSL Availability $_SERVER['HTTPS'] = true; ``` -You can also set this value to `false` to explicitly tell Phabricator that a +You can also set this value to `false` to explicitly tell Phorge that a request is not an SSL request. diff --git a/src/docs/user/configuration/custom_fields.diviner b/src/docs/user/configuration/custom_fields.diviner index 75d83fc8ba..5879931317 100644 --- a/src/docs/user/configuration/custom_fields.diviner +++ b/src/docs/user/configuration/custom_fields.diviner @@ -5,7 +5,7 @@ How to add custom fields to applications which support them. = Overview = -Several Phabricator applications allow the configuration of custom fields. These +Several Phorge applications allow the configuration of custom fields. These fields allow you to add more information to objects, and in some cases reorder or remove builtin fields. @@ -134,7 +134,7 @@ The `strings` value supports different strings per control type. They are: - **search.default** Text for the search interface, defaults to "(Any)". - **search.require** Text for the search interface, defaults to "Require". -Internally, Phabricator implements some additional custom field types and +Internally, Phorge implements some additional custom field types and options. These are not intended for general use and are subject to abrupt change, but are documented here for completeness: @@ -149,7 +149,7 @@ change, but are documented here for completeness: If you want custom fields to have advanced behaviors (sophisticated rendering, advanced validation, complicated controls, interaction with other systems, etc), -you can write a custom field as an extension and add it to Phabricator. +you can write a custom field as an extension and add it to Phorge. NOTE: This API is somewhat new and fairly large. You should expect that there will be occasional changes to the API requiring minor updates in your code. @@ -167,9 +167,9 @@ want to add a field to: | Projects | @{class:PhabricatorProjectCustomField} | The easiest way to get started is to drop your subclass into -`phabricator/src/extensions/`. If Phabricator is configured in development +`phorge/src/extensions/`. If Phorge is configured in development mode, the class should immediately be available in the UI. If not, you can -restart Phabricator (for help, see @{article:Restarting Phabricator}). +restart Phorge (for help, see @{article:Restarting Phorge}). For example, this is a simple template which adds a custom field to Maniphest: @@ -211,6 +211,6 @@ integrations, see the base class for your application and Continue by: - - learning more about extending Phabricator with custom code in - @{article@phabcontrib:Adding New Classes}; + - learning more about extending Phorge with custom code in + @{article@contrib:Adding New Classes}; - or returning to the @{article: Configuration Guide}. diff --git a/src/docs/user/configuration/managing_caches.diviner b/src/docs/user/configuration/managing_caches.diviner index e873b99d8c..c4e209fc25 100644 --- a/src/docs/user/configuration/managing_caches.diviner +++ b/src/docs/user/configuration/managing_caches.diviner @@ -1,17 +1,17 @@ @title Managing Caches @group config -Discusses Phabricator caches and cache management. +Discusses Phorge caches and cache management. Overview ======== -Phabricator uses various caches to improve performance, similar to the caches +Phorge uses various caches to improve performance, similar to the caches a web browser uses to improve web performance. In particular, blocks of text which are expensive to render (like formatted text and syntax highlighted code) are often cached after they're rendered for -the first time. When they're rendered again, Phabricator can read the cache +the first time. When they're rendered again, Phorge can read the cache instead of recomputing the result. Because text is cached, you may continue to see the old result even after you @@ -28,20 +28,20 @@ you can just ignore the out of date caches and they'll fix themselves eventually (usually within 30 days). If you don't want to wait, you can purge the caches. This will remove any -cached data and force Phabricator to recompute the results. +cached data and force Phorge to recompute the results. Purging Caches ============== -If you need to purge Phabricator's caches, you can use the CLI tool. Run it +If you need to purge Phorge's caches, you can use the CLI tool. Run it with the `--help` flag to see options: - phabricator/ $ ./bin/cache purge --help + phorge/ $ ./bin/cache purge --help This tool can purge caches in a granular way, but it's normally easiest to just purge all of the caches: - phabricator/ $ ./bin/cache purge --all + phorge/ $ ./bin/cache purge --all You can purge caches safely. The data they contain can always be rebuilt from -other data if Phabricator needs it. +other data if Phorge needs it. diff --git a/src/docs/user/configuration/managing_daemons.diviner b/src/docs/user/configuration/managing_daemons.diviner index cf2ba85ea2..0e56a95995 100644 --- a/src/docs/user/configuration/managing_daemons.diviner +++ b/src/docs/user/configuration/managing_daemons.diviner @@ -1,11 +1,11 @@ @title Managing Daemons with phd @group config -Explains Phabricator daemons and the daemon control program `phd`. +Explains Phorge daemons and the daemon control program `phd`. = Overview = -Phabricator uses daemons (background processing scripts) to handle a number of +Phorge uses daemons (background processing scripts) to handle a number of tasks: - tracking repositories, discovering new commits, and importing and parsing @@ -16,19 +16,19 @@ tasks: Daemons are started and stopped with **phd** (the **Ph**abricator **D**aemon launcher). Daemons can be monitored via a web console. -You do not need to run daemons for most parts of Phabricator to work, but some +You do not need to run daemons for most parts of Phorge to work, but some features (principally, repository tracking with Diffusion) require them and several features will benefit in performance or stability if you configure daemons. = phd = -**phd** is a command-line script (located at `phabricator/bin/phd`). To get +**phd** is a command-line script (located at `phorge/bin/phd`). To get a list of commands, run `phd help`: - phabricator/ $ ./bin/phd help + phorge/ $ ./bin/phd help NAME - phd - phabricator daemon launcher + phd - phorge daemon launcher ... Generally, you will use: @@ -43,7 +43,7 @@ If you want finer-grained control, you can use: - **phd launch** to launch individual daemons; and - **phd debug** to debug problems with daemons. -NOTE: When you upgrade Phabricator or change configuration, you should restart +NOTE: When you upgrade Phorge or change configuration, you should restart the daemons by running `phd restart`. = Daemon Console = @@ -81,12 +81,12 @@ launch custom daemons, or launch special daemons like the IRC bot. To debug a daemon, use `phd debug`: - phabricator/bin/ $ ./phd debug + phorge/bin/ $ ./phd debug You can pass arguments like this (normal arguments are passed to the daemon control mechanism, not to the daemon itself): - phabricator/bin/ $ ./phd debug -- --flavor apple + phorge/bin/ $ ./phd debug -- --flavor apple In debug mode, daemons do not daemonize, and they print additional debugging output to the console. This should make it easier to debug problems. You can @@ -94,7 +94,7 @@ terminate the daemon with `^C`. To launch a nonstandard daemon, use `phd launch`: - phabricator/bin/ $ ./phd launch + phorge/bin/ $ ./phd launch This daemon will daemonize and run normally. @@ -109,7 +109,7 @@ This daemon will daemonize and run normally. just those started with `phd start`. If you're writing a restart script, have it launch any custom daemons explicitly after `phd restart`. - You can write your own daemons and manage them with `phd` by extending - @{class:PhabricatorDaemon}. See @{article@phabcontrib:Adding New Classes}. + @{class:PhabricatorDaemon}. See @{article@contrib:Adding New Classes}. - See @{article:Diffusion User Guide} for details about tuning the repository daemon. @@ -128,4 +128,4 @@ Continue by: - learning about the repository daemon with @{article:Diffusion User Guide}; or - - writing your own daemons with @{article@phabcontrib:Adding New Classes}. + - writing your own daemons with @{article@contrib:Adding New Classes}. diff --git a/src/docs/user/configuration/managing_garbage.diviner b/src/docs/user/configuration/managing_garbage.diviner index 0b18bd2a0a..5c5ef87762 100644 --- a/src/docs/user/configuration/managing_garbage.diviner +++ b/src/docs/user/configuration/managing_garbage.diviner @@ -6,7 +6,7 @@ Understanding and configuring garbage collection. Overview ======== -Phabricator generates various logs and caches during normal operation. Some of +Phorge generates various logs and caches during normal operation. Some of these logs and caches are usually of very little use after some time has passed, so they are deleted automatically (often after a month or two) in a process called "garbage collection". @@ -35,7 +35,7 @@ You can review the current retention policies in `bin/garbage set-policy` to select a new policy: ``` -phabricator/ $ ./bin/garbage set-policy --collector cache.markup --days 7 +phorge/ $ ./bin/garbage set-policy --collector cache.markup --days 7 ``` You can use `--days` to select how long data is retained for. You can also use @@ -53,7 +53,7 @@ Troubleshooting You can manually run a collector with `bin/garbage collect`. ``` -phabricator/ $ ./bin/garbage collect --collector cache.general +phorge/ $ ./bin/garbage collect --collector cache.general ``` By using the `--trace` flag, you can inspect the operation of the collector diff --git a/src/docs/user/configuration/notifications.diviner b/src/docs/user/configuration/notifications.diviner index 13d93317f9..bd1835c294 100644 --- a/src/docs/user/configuration/notifications.diviner +++ b/src/docs/user/configuration/notifications.diviner @@ -6,11 +6,11 @@ Guide to setting up notifications. Overview ======== -By default, Phabricator delivers information about events (like users creating +By default, Phorge delivers information about events (like users creating tasks or commenting on code reviews) through email and in-application notifications. -Phabricator can also be configured to deliver notifications in real time, by +Phorge can also be configured to deliver notifications in real time, by popping up a message in any open browser windows if something has happened or an object has been updated. @@ -44,8 +44,8 @@ To install Node.js, follow the instructions on You will also need to install the `ws` module for Node. This needs to be installed into the notification server directory: - phabricator/ $ cd support/aphlict/server/ - phabricator/support/aphlict/server/ $ npm install ws + phorge/ $ cd support/aphlict/server/ + phorge/support/aphlict/server/ $ npm install ws Once Node.js and the `ws` module are installed, you're ready to start the server. @@ -57,21 +57,21 @@ Running the Aphlict Server After installing Node.js, you can control the notification server with the `bin/aphlict` command. To start the server: - phabricator/ $ bin/aphlict start + phorge/ $ bin/aphlict start By default, the server must be able to listen on port `22280`. If you're using a host firewall (like a security group in EC2), make sure traffic can reach the server. The server configuration is controlled by a configuration file, which is -separate from Phabricator's configuration settings. The default file can -be found at `phabricator/conf/aphlict/aphlict.default.json`. +separate from Phorge's configuration settings. The default file can +be found at `phorge/conf/aphlict/aphlict.default.json`. To make adjustments to the default configuration, either copy this file to create `aphlict.custom.json` in the same directory (this file will be used if it exists) or specify a configuration file explicitly with the `--config` flag: - phabricator/ $ bin/aphlict start --config path/to/config.json + phorge/ $ bin/aphlict start --config path/to/config.json The configuration file has these settings: @@ -118,12 +118,12 @@ installs. For more information on how to configure a cluster, see The defaults are appropriate for simple cases, but you may need to adjust them if you are running a more complex configuration. -Configuring Phabricator +Configuring Phorge ======================= -After starting the server, configure Phabricator to connect to it by adjusting +After starting the server, configure Phorge to connect to it by adjusting `notification.servers`. This configuration option should have a list of servers -that Phabricator should interact with. +that Phorge should interact with. Normally, you'll list one client server and one admin server, like this: @@ -131,7 +131,7 @@ Normally, you'll list one client server and one admin server, like this: [ { "type": "client", - "host": "phabricator.mycompany.com", + "host": "phorge.mycompany.com", "port": 22280, "protocol": "https" }, @@ -165,7 +165,7 @@ Troubleshooting You can run `aphlict` in the foreground to get output to your console: - phabricator/ $ ./bin/aphlict debug + phorge/ $ ./bin/aphlict debug Because the notification server uses WebSockets, your browser error console may also have information that is useful in figuring out what's wrong. @@ -178,10 +178,10 @@ information that is useful in resolving issues. SSL and HTTPS ============= -If you serve Phabricator over HTTPS, you must also serve websockets over HTTPS. +If you serve Phorge over HTTPS, you must also serve websockets over HTTPS. Browsers will refuse to connect to `ws://` websockets from HTTPS pages. -If a client connects to Phabricator over HTTPS, Phabricator will automatically +If a client connects to Phorge over HTTPS, Phorge will automatically select an appropriate HTTPS service from `notification.servers` and instruct the browser to open a websocket connection with `wss://`. @@ -241,10 +241,10 @@ upstream websocket_pool { } ``` -```lang=nginx, name=/etc/nginx/sites-enabled/phabricator.example.com.conf +```lang=nginx, name=/etc/nginx/sites-enabled/phorge.example.com.conf server { - server_name phabricator.example.com; - root /path/to/phabricator/webroot; + server_name phorge.example.com; + root /path/to/phorge/webroot; // ... @@ -260,10 +260,10 @@ server { With this approach, you should make these additional adjustments: -**Phabricator Configuration**: The entry in `notification.servers` with type +**Phorge Configuration**: The entry in `notification.servers` with type `"client"` should have these adjustments made: - - Set `host` to the Phabricator host. + - Set `host` to the Phorge host. - Set `port` to the standard HTTPS port (usually `443`). - Set `protocol` to `"https"`. - Set `path` to `/ws/`, so it matches the special `location` in your diff --git a/src/docs/user/configuration/storage_adjust.diviner b/src/docs/user/configuration/storage_adjust.diviner index 2403cd3fed..e460ddd041 100644 --- a/src/docs/user/configuration/storage_adjust.diviner +++ b/src/docs/user/configuration/storage_adjust.diviner @@ -6,13 +6,13 @@ Explains how to apply storage adjustments to the MySQL schemata. Overview ======== -Phabricator uses a workflow called //storage adjustment// to make some minor +Phorge uses a workflow called //storage adjustment// to make some minor kinds of changes to the MySQL schema. This workflow compliments the //storage upgrade// workflow, which makes major changes. You can perform storage adjustment by running: - phabricator/ $ ./bin/storage adjust + phorge/ $ ./bin/storage adjust This document describes what adjustments are, how they relate to storage upgrades, how to perform them, and how to troubleshoot issues with storage @@ -22,7 +22,7 @@ adjustment. Understanding Adjustments =================== -Storage adjustments make minor changes to the Phabricator MySQL schemata to +Storage adjustments make minor changes to the Phorge MySQL schemata to improve consistency, unicode handling, and performance. Changes covered by adjustment include: @@ -62,7 +62,7 @@ adjustment workflow can resolve. You can also review adjustments from the CLI, by running: - phabricator/ $ ./bin/storage adjust + phorge/ $ ./bin/storage adjust Before you're prompted to actually apply adjustments, you'll be given a list of available adjustments. You can then make a choice to apply them. @@ -73,11 +73,11 @@ Performing Adjustments To perform adjustments, run the `adjust` workflow: - phabricator/ $ ./bin/storage adjust + phorge/ $ ./bin/storage adjust For details about flags, use: - phabricator/ $ ./bin/storage help adjust + phorge/ $ ./bin/storage help adjust You do not normally need to run this workflow manually: it will be run automatically after you run the `upgrade` workflow. @@ -97,7 +97,7 @@ set can safely store 4-byte unicode characters. The adjustment workflow allows us to alter the schema to primarily use `binary` character sets on older MySQL, and primarily use `utf8mb4` character -sets on newer MySQL. The net effect is that Phabricator works consistently and +sets on newer MySQL. The net effect is that Phorge works consistently and can store 4-byte unicode characters regardless of the MySQL version. Under newer MySQL, we can also take advantage of the better collation rules the `utf8mb4` character set offers. @@ -143,7 +143,7 @@ upstream. In general, adjustments are not critical. If you run into issues applying adjustments, it is safe to file a task in the upstream describing the problem -you've encountered and continue using Phabricator normally until the issue can +you've encountered and continue using Phorge normally until the issue can be resolved. Surplus Schemata @@ -154,13 +154,13 @@ After performing adjustment, you may receive an error that a table or column is | Target | Error | | --- | --- | -| phabricator_example.example_table | Surplus | +| phorge_example.example_table | Surplus | -Generally, "Surplus" means that Phabricator does not expect the table or column +Generally, "Surplus" means that Phorge does not expect the table or column to exist. These surpluses usually exist because you (or someone else with database access) added the table or column manually. Rarely, they can also exist for other reasons. They are usually safe to delete, but because -deleting them destroys data and Phabricator can not be sure that the table or +deleting them destroys data and Phorge can not be sure that the table or column doesn't have anything important in it, it does not delete them automatically. @@ -174,18 +174,18 @@ message): ```lang=sql CREATE DATABASE my_backups; -RENAME TABLE phabricator_example.example_table +RENAME TABLE phorge_example.example_table TO my_backups.example_table; ``` -Phabricator will ignore tables that aren't in databases it owns, so you can -safely move anything you aren't sure about outside of the Phabricator databases. +Phorge will ignore tables that aren't in databases it owns, so you can +safely move anything you aren't sure about outside of the Phorge databases. If you're sure you don't need a table, use `DROP TABLE` to destroy it, specifying the correct table name (the one given in the error message): ```lang=sql -DROP TABLE phabricator_example.example_table; +DROP TABLE phorge_example.example_table; ``` This will destroy the table permanently. diff --git a/src/docs/user/configuration/troubleshooting_https.diviner b/src/docs/user/configuration/troubleshooting_https.diviner index bdc3439d7d..b3aae81035 100644 --- a/src/docs/user/configuration/troubleshooting_https.diviner +++ b/src/docs/user/configuration/troubleshooting_https.diviner @@ -5,7 +5,7 @@ Detailed instructions for troubleshooting HTTPS connection problems. = Overview = -If you're having trouble connecting to an HTTPS install of Phabricator, and +If you're having trouble connecting to an HTTPS install of Phorge, and particularly if you're receiving a "There was an error negotiating the SSL connection." error, this document may be able to help you diagnose and resolve the problem. @@ -17,7 +17,7 @@ Connection negotiation can fail for several reasons. The major ones are: certificates). - The SSL certificate is signed for the wrong domain. For example, a certificate signed for `www.example.com` will not work for - `phabricator.example.com`. + `phorge.example.com`. - The server rejects TLSv1 SNI connections for the domain (this is complicated, see below). @@ -50,7 +50,7 @@ with: $ openssl x509 -text -in If the certificate was accidentally generated for, e.g. `www.example.com` but -you installed Phabricator on `phabricator.example.com`, you need to generate a +you installed Phorge on `phorge.example.com`, you need to generate a new certificate for the right domain. = SNI Problems = @@ -73,7 +73,7 @@ an error in `SSL23_GET_SERVER_HELLO` with `reason(1112)`, like this: /SourceCache/OpenSSL098/OpenSSL098-44/src/ssl/s23_clnt.c:602: ...it indicates server is misconfigured. The most common cause of this problem -is an Apache server that does not explicitly name the Phabricator domain as a +is an Apache server that does not explicitly name the Phorge domain as a valid VirtualHost. This error occurs only for some versions of the OpenSSL client library diff --git a/src/docs/user/feedback.diviner b/src/docs/user/feedback.diviner deleted file mode 100644 index 0998002b10..0000000000 --- a/src/docs/user/feedback.diviner +++ /dev/null @@ -1,7 +0,0 @@ -@title Give Feedback! Get Support! -@short Feedback/Support -@group cellar - -Deprecated. - -This article has moved to @{article:Support Resources}. diff --git a/src/docs/user/field/conduit_changes.diviner b/src/docs/user/field/conduit_changes.diviner index 96a890600a..b2246310e3 100644 --- a/src/docs/user/field/conduit_changes.diviner +++ b/src/docs/user/field/conduit_changes.diviner @@ -25,7 +25,7 @@ Methods have one of three statuses: them before they stabilize. - **Stable**: This is an established method which generally will not change. - **Deprecated**: This method will be removed in a future version of - Phabricator and callers should cease using it. + Phorge and callers should cease using it. Normally, a method is deprecated only when it is obsolete or a new, more powerful method is available to replace it. diff --git a/src/docs/user/field/darkconsole.diviner b/src/docs/user/field/darkconsole.diviner index 065be2d8f1..494b175416 100644 --- a/src/docs/user/field/darkconsole.diviner +++ b/src/docs/user/field/darkconsole.diviner @@ -6,9 +6,9 @@ Enabling and using the built-in debugging and performance console. Overview ======== -DarkConsole is a debugging console built into Phabricator which exposes +DarkConsole is a debugging console built into Phorge which exposes configuration, performance and error information. It can help you detect, -understand and resolve bugs and performance problems in Phabricator +understand and resolve bugs and performance problems in Phorge applications. diff --git a/src/docs/user/field/exit_codes.diviner b/src/docs/user/field/exit_codes.diviner index 7c69b3509b..40f1d281e3 100644 --- a/src/docs/user/field/exit_codes.diviner +++ b/src/docs/user/field/exit_codes.diviner @@ -1,7 +1,7 @@ @title Command Line Exit Codes @group fieldmanual -Explains the use of exit codes in Phabricator command line scripts. +Explains the use of exit codes in Phorge command line scripts. Overview ======== @@ -21,7 +21,7 @@ like `cmdx && cmdy` operate on exit codes. The code `0` means success. Other codes signal some sort of error or status condition, depending on the system and command. -With rare exception, Phabricator uses //all other codes// to signal +With rare exception, Phorge uses //all other codes// to signal **catastrophic failure**. This is an explicit architectural decision and one we are unlikely to deviate @@ -35,10 +35,10 @@ they are not appropriate for communicating application state in a modern operational environment. This document explains the reasoning behind our use of exit codes in more detail. -In particular, this approach is informed by a focus on operating Phabricator +In particular, this approach is informed by a focus on operating Phorge clusters at scale. This is not a common deployment scenario, but we consider it the most important one. Our use of exit codes makes it easier to deploy and -operate a Phabricator cluster at larger scales. It makes it slightly harder to +operate a Phorge cluster at larger scales. It makes it slightly harder to deploy and operate a small cluster or single host by gluing together `bash` scripts. We are willingly trading the small scale away for advantages at larger scales. @@ -56,11 +56,10 @@ operations environment faces different forces than the interactive shell did in the 1970s, particularly at scale. We consider correctness to be very important to modern operations environments. -In particular, we manage a Phabricator cluster (Phacility) and believe that -having reliable, repeatable processes for provisioning, configuration and -deployment is critical to maintaining and scaling our operations. Our use of -exit codes makes it easier to implement processes that are correct and reliable -on top of Phabricator management scripts. +In particular, we believe that having reliable, repeatable processes for +provisioning, configuration and deployment is critical to maintaining and +scaling our operations. Our use of exit codes makes it easier to implement +processes that are correct and reliable on top of Phorge management scripts. Exit codes as signals for application state are problematic because they are ambiguous: you can't use them to distinguish between dissimilar failure states @@ -228,13 +227,13 @@ particular, ease of use in a `bash` environment is not a compelling motivation. We are broadly willing to make output machine parseable or provide an explicit machine output mode (often a `--json` flag) if there is a reasonable use case -for it. However, we operate a large production cluster of Phabricator instances +for it. However, we operate a large production cluster of Phorge instances with the tools available in the upstream, so the lack of machine parseable output is not sufficient to motivate adding such output on its own: we also need to understand the problem you're facing, and why it isn't a problem we face. A simpler or cleaner approach to the problem may already exist. -If you just want to write `bash` scripts on top of Phabricator scripts and you +If you just want to write `bash` scripts on top of Phorge scripts and you are unswayed by these concerns, you can often just build a composite command to get roughly the same effect that you'd get out of an exit code. diff --git a/src/docs/user/field/performance.diviner b/src/docs/user/field/performance.diviner index c5980acd0e..09e7023fb9 100644 --- a/src/docs/user/field/performance.diviner +++ b/src/docs/user/field/performance.diviner @@ -23,18 +23,18 @@ Performance and the Upstream ============================ Performance issues and hangs will often require upstream involvement to fully -resolve. The intent is for Phabricator to perform well in all reasonable cases, +resolve. The intent is for Phorge to perform well in all reasonable cases, not require tuning for different workloads (as long as those workloads are generally reasonable). Poor performance with a reasonable workload is likely a bug, not a configuration problem. -However, some pages are slow because Phabricator legitimately needs to do a lot +However, some pages are slow because Phorge legitimately needs to do a lot of work to generate them. For example, if you write a 100MB wiki document, -Phabricator will need substantial time to process it, it will take a long time +Phorge will need substantial time to process it, it will take a long time to download over the network, and your browser will probably not be able to render it especially quickly. -We may be able to improve performance in some cases, but Phabricator is not +We may be able to improve performance in some cases, but Phorge is not magic and can not wish away real complexity. The best solution to these problems is usually to find another way to solve your problem: for example, maybe the 100MB document can be split into several smaller documents. diff --git a/src/docs/user/field/permanently_destroying_data.diviner b/src/docs/user/field/permanently_destroying_data.diviner index 04907fc0be..8f5d667252 100644 --- a/src/docs/user/field/permanently_destroying_data.diviner +++ b/src/docs/user/field/permanently_destroying_data.diviner @@ -6,7 +6,7 @@ How to permanently destroy data and manage leaked secrets. Overview ======== -Phabricator intentionally makes it difficult to permanently destroy data, but +Phorge intentionally makes it difficult to permanently destroy data, but provides a command-line tool for destroying objects if you're certain that you want to destroy something. @@ -24,7 +24,7 @@ Destroying Data To permanently destroy an object, run this command from the command line: ``` -phabricator/ $ ./bin/remove destroy +phorge/ $ ./bin/remove destroy ``` The `` may be an object monogram or PHID. For instance, you can use @@ -36,7 +36,7 @@ The `` may be an object monogram or PHID. For instance, you can use CLI Access Required =================== -In almost all cases, Phabricator requires operational access from the CLI to +In almost all cases, Phorge requires operational access from the CLI to permanently destroy data. One major reason for this requirement is that it limits the reach of an attacker who compromises a privileged account. @@ -56,9 +56,9 @@ Sometimes you may want to destroy an object because it has leaked a secret, like an API key or another credential. For example, an engineer might accidentally send a change for review which includes a sensitive private key. -No Phabricator command can rewind time, and once data is written to Phabricator +No Phorge command can rewind time, and once data is written to Phorge the cat is often out of the bag: it has often been transmitted to external -systems which Phabricator can not interact with via email, webhooks, API calls, +systems which Phorge can not interact with via email, webhooks, API calls, repository mirroring, CDN caching, and so on. You can try to clean up the mess, but you're generally already too late. @@ -66,7 +66,7 @@ The `bin/remove destroy` command will make a reasonable attempt to completely destroy objects, but this is just an attempt. It can not unsend email or uncall the API, and no command can rewind time and undo a leak. -**Revoking Credentials**: If Phabricator credentials were accidentally +**Revoking Credentials**: If Phorge credentials were accidentally disclosed, you can revoke them so they no longer function. See @{article:Revoking Credentials} for more information. @@ -75,7 +75,7 @@ Preventing Leaks ================ Because time can not be rewound, it is best to prevent sensitive data from -leaking in the first place. Phabricator supports some technical measures that +leaking in the first place. Phorge supports some technical measures that can make it more difficult to accidentally disclose secrets: **Differential Diff Herald Rules**: You can write "Differential Diff" rules diff --git a/src/docs/user/field/repository_hints.diviner b/src/docs/user/field/repository_hints.diviner index d618fac4aa..9c50243a9a 100644 --- a/src/docs/user/field/repository_hints.diviner +++ b/src/docs/user/field/repository_hints.diviner @@ -6,7 +6,7 @@ Dealing with rewrites of published repositories and other unusual problems. Overview ======== -Some repositories have unusual commits. You can provide "hints" to Phabricator +Some repositories have unusual commits. You can provide "hints" to Phorge about these commits to improve behavior. Supported hints are: @@ -17,7 +17,7 @@ Supported hints are: new pages. - **Unreadable Commits**: If some commits are not readable (which is rare, but can happen in some cases if they are generated with an external tool) - you can provide hints so that Phabricator doesn't try to read them. + you can provide hints so that Phorge doesn't try to read them. The remainder of this document explains how to create and remove hints, and how to specify each type of hint. @@ -28,7 +28,7 @@ Creating Hints To create hints, pipe a JSON list of hints to `bin/repository hint`: ``` -phabricator/ $ cat hints.json | ./bin/repository hint +phorge/ $ cat hints.json | ./bin/repository hint ``` The hints should be a list of objects like this: @@ -76,7 +76,7 @@ For example, use a hint specification like this: ] ``` -Phabricator won't treat commits without any hint specially. +Phorge won't treat commits without any hint specially. Hint: Rewritten Commits @@ -104,13 +104,13 @@ For example, a hint might look like this: ] ``` -Phabricator will show users that the commit was rewritten in the web UI. +Phorge will show users that the commit was rewritten in the web UI. Hint: Unreadable Commits ======================== -The `"unreadable"` hint allows you to tell Phabricator that it should not +The `"unreadable"` hint allows you to tell Phorge that it should not bother trying to read the changes associated with a particular commit. In some rare cases, repositories can contain commits which aren't readable (for example, if they were created by external tools during an import or @@ -130,5 +130,5 @@ For example, a hint might look like this: ] ``` -Phabricator won't try to read, parse, import, or display the changes associated +Phorge won't try to read, parse, import, or display the changes associated with this commit. diff --git a/src/docs/user/field/repository_imports.diviner b/src/docs/user/field/repository_imports.diviner index 262874186c..c5fba58dd7 100644 --- a/src/docs/user/field/repository_imports.diviner +++ b/src/docs/user/field/repository_imports.diviner @@ -7,7 +7,7 @@ Overview ======== When you first import an external source code repository (or push new commits to -a hosted repository), Phabricator imports those commits in the background. +a hosted repository), Phorge imports those commits in the background. While a repository is initially importing, some features won't work. While individual commits are importing, some of their metadata won't be available in @@ -20,7 +20,7 @@ help you understand the import process and troubleshoot problems with it. Understanding the Import Pipeline ================================= -Phabricator first performs commit discovery on repositories. This examines +Phorge first performs commit discovery on repositories. This examines a repository and identifies all the commits in it at a very shallow level, then creates stub objects for them. These stub objects primarily serve to assign various internal IDs to each commit. @@ -54,7 +54,7 @@ the import process is stuck. First, to identify which commits have missing import steps, run this command: ``` -phabricator/ $ ./bin/repository importing rXYZ +phorge/ $ ./bin/repository importing rXYZ ``` That will show what work remains to be done. Each line shows a commit which @@ -69,7 +69,7 @@ sections are "Queued Tasks" (work waiting in queue) and "Leased Tasks" Third, run this command to look at the daemon logs: ``` -phabricator/ $ ./bin/phd log +phorge/ $ ./bin/phd log ``` This can show you any errors the daemons have encountered recently. @@ -104,14 +104,14 @@ importing` and use this command to re-run any missing steps manually in the foreground: ``` -phabricator/ $ ./bin/repository reparse --importing --trace rXYZabcdef012... +phorge/ $ ./bin/repository reparse --importing --trace rXYZabcdef012... ``` This command is always safe to run, no matter what the actual root cause of the problem is. If this fails with an error, you've likely identified a problem with -Phabricator. Collect as much information as you can about what makes the commit +Phorge. Collect as much information as you can about what makes the commit special and file a bug in the upstream by following the instructions in @{article:Contributing Bug Reports}. @@ -132,7 +132,7 @@ wait that long. In the Daemon console, temporarily failures usually look like tasks in the "Leased Tasks" column with a large "Expires" value but a low "Failures" count -(usually 0 or 1). The "Expires" column is showing how long Phabricator is +(usually 0 or 1). The "Expires" column is showing how long Phorge is waiting to retry these tasks. In the daemon log, these temporary failures might have created log entries, but @@ -150,7 +150,7 @@ daemons, all task leases are immediately expired, so any tasks waiting for a long time will run right away: ``` -phabricator/ $ ./bin/phd restart +phorge/ $ ./bin/phd restart ``` This command is always safe to run, no matter what the actual root cause of @@ -172,7 +172,7 @@ been too ambitious with running manual SQL commands and deleted a bunch of extra things they shouldn't have. There is no normal set of conditions under which this should occur, but you can -force Phabricator to re-queue the tasks to recover from it if it does occur. +force Phorge to re-queue the tasks to recover from it if it does occur. This will look like missing steps in `repository importing`, but nothing in the "Queued Tasks" or "Leased Tasks" sections of the daemon console. The daemon @@ -182,12 +182,12 @@ To re-queue parse tasks for a repository, run this command, which will queue up all of the missing work in `repository importing`: ``` -phabricator/ $ ./bin/repository reparse --importing --all rXYZ +phorge/ $ ./bin/repository reparse --importing --all rXYZ ``` This command may cause duplicate work to occur if you have misdiagnosed the problem and the tasks aren't actually lost. For example, it could queue a -second task to perform publishing, which could cause Phabricator to send a +second task to perform publishing, which could cause Phorge to send a second copy of email about the commit. Other than that, it is safe to run even if this isn't the problem. @@ -215,7 +215,7 @@ To mark a repository as imported even though it really isn't, run this command: ``` -phabricator/ $ ./bin/repository mark-imported rXYZ +phorge/ $ ./bin/repository mark-imported rXYZ ``` If you do this by mistake, you can reverse it later by using the @@ -231,7 +231,7 @@ to any command to get more details about what it is doing. For any command, you can use `help` to learn more about what it does and which flag it takes: ``` -phabricator/ $ bin/repository help +phorge/ $ bin/repository help ``` In particular, you can use flags with the `repository reparse` command to diff --git a/src/docs/user/field/restarting.diviner b/src/docs/user/field/restarting.diviner index 5bdd92da78..638e1f1490 100644 --- a/src/docs/user/field/restarting.diviner +++ b/src/docs/user/field/restarting.diviner @@ -1,14 +1,14 @@ -@title Restarting Phabricator +@title Restarting Phorge @group fieldmanual Instructions on how to restart HTTP and PHP servers to reload configuration -changes in Phabricator. +changes in Phorge. Overview ======== -Phabricator's setup and configuration instructions sometimes require you to +Phorge's setup and configuration instructions sometimes require you to restart your server processes, particularly after making configuration changes. This document explains how to restart them properly. diff --git a/src/docs/user/field/revoking_credentials.diviner b/src/docs/user/field/revoking_credentials.diviner index b1e18bcd97..9d3c046fbe 100644 --- a/src/docs/user/field/revoking_credentials.diviner +++ b/src/docs/user/field/revoking_credentials.diviner @@ -21,7 +21,7 @@ specified targets. For example, if you believe `@alice` may have had her SSH key compromised, you can revoke her keys like this: ``` -phabricator/ $ ./bin/auth revoke --type ssh --from @alice +phorge/ $ ./bin/auth revoke --type ssh --from @alice ``` The flag `--everything` revokes all credential types. @@ -41,13 +41,13 @@ third-party credential types if you have extensions installed. To list all revokable credential types: ``` -phabricator/ $ ./bin/auth revoke --list +phorge/ $ ./bin/auth revoke --list ``` To get details about exactly how a specific revoker works: ``` -phabricator/ $ ./bin/auth revoke --list --type ssh +phorge/ $ ./bin/auth revoke --list --type ssh ``` @@ -79,10 +79,10 @@ are normally sent over the network. You can revoke these credentials by running these commands: ``` -phabricator/ $ ./bin/auth revoke --type password --everywhere -phabricator/ $ ./bin/auth revoke --type conduit --everywhere -phabricator/ $ ./bin/auth revoke --type session --everywhere -phabricator/ $ ./bin/auth revoke --type temporary --everywhere +phorge/ $ ./bin/auth revoke --type password --everywhere +phorge/ $ ./bin/auth revoke --type conduit --everywhere +phorge/ $ ./bin/auth revoke --type session --everywhere +phorge/ $ ./bin/auth revoke --type temporary --everywhere ``` Depending on the nature of the compromise you may also consider revoking `ssh` @@ -97,5 +97,5 @@ credentials without affecting other users. You can revoke all credentials for a user by running this command: ``` -phabricator/ $ ./bin/auth revoke --everything --from @alice +phorge/ $ ./bin/auth revoke --everything --from @alice ``` diff --git a/src/docs/user/field/worker_queue.diviner b/src/docs/user/field/worker_queue.diviner index b88a2ea40b..d53b0771de 100644 --- a/src/docs/user/field/worker_queue.diviner +++ b/src/docs/user/field/worker_queue.diviner @@ -6,7 +6,7 @@ Advanced guide to managing the background worker task queue. Overview ======== -Phabricator uses daemonized worker processes to execute some tasks (like +Phorge uses daemonized worker processes to execute some tasks (like importing repositories and sending mail) in the background. In most cases, this queue will automatically execute tasks in an appropriate @@ -16,7 +16,7 @@ which tasks execute, when, and at what priority. Reference: Priority Levels ========================== -Tasks queued by Phabricator use these default priority levels: +Tasks queued by Phorge use these default priority levels: | Priority | Name | Tasks | |---|---|---| @@ -33,7 +33,7 @@ with priority 2000). Any positive integer is a valid priority level, and if you adjust the priority of tasks with `bin/worker priority` you may select any level even if -Phabricator would never naturally queue tasks at that level. For example, you +Phorge would never naturally queue tasks at that level. For example, you may adjust tasks to priority `5678`, which will make them execute after all other types of natural tasks. @@ -48,7 +48,7 @@ The most common case where you may want to make an adjustment to the default behavior of the worker queue is when importing a very large repository like the Linux kernel. -Although Phabricator will automatically process imports of new repositories at +Although Phorge will automatically process imports of new repositories at a lower priority level than all other non-import tasks, you may still run into issues like these: @@ -63,7 +63,7 @@ import at a lower priority than all other tasks (including other imports of new repositories), you can run a command like this: ``` -phabricator/ $ ./bin/worker priority --priority 5000 --container R123 +phorge/ $ ./bin/worker priority --priority 5000 --container R123 ``` This means: set all tasks associated with container `R123` (in this example, @@ -75,7 +75,7 @@ schedule tasks to execute at night or over the weekend. For example, to pause an import for 6 hours, run a command like this: ``` -phabricator/ $ ./bin/worker delay --until "6 hours" --container R123 +phorge/ $ ./bin/worker delay --until "6 hours" --container R123 ``` The selected tasks will not execute until 6 hours from the time this command diff --git a/src/docs/user/field/xhprof.diviner b/src/docs/user/field/xhprof.diviner index ad6fe82cb8..a1cfd36558 100644 --- a/src/docs/user/field/xhprof.diviner +++ b/src/docs/user/field/xhprof.diviner @@ -7,7 +7,7 @@ Overview ======== XHProf is a profiling tool which will let you understand application -performance in Phabricator. +performance in Phorge. After you install XHProf, you can use it from the web UI and the CLI to generate detailed performance profiles. It is the most powerful tool available @@ -60,7 +60,7 @@ Using XHProf: CLI From the command line, use the `--xprofile ` flag to generate a profile of any script. -You can then upload this file to Phabricator (using `arc upload` may be easiest) +You can then upload this file to Phorge (using `arc upload` may be easiest) and view it in the web UI. diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index 269b20b62a..0073dc6afb 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -1,18 +1,18 @@ @title Installation Guide @group intro -This document contains basic install instructions to get Phabricator up and +This document contains basic install instructions to get Phorge up and running. Overview ======== -Phabricator is a LAMP (Linux, Apache, MySQL, PHP) application. To install -Phabricator, you will need: +Phorge is a LAMP (Linux, Apache, MySQL, PHP) application. To install +Phorge, you will need: - a normal computer to install it on (shared hosts and unusual environments are not supported) running some flavor of Linux or a similar OS; - - a domain name (like `phabricator.mycompany.com`); + - a domain name (like `phorge.example.com`); - basic sysadmin skills; - Apache, nginx, or another webserver; - PHP, MySQL, and Git. @@ -25,32 +25,32 @@ Installation Requirements You will need **a computer**. Options include: - **A Normal Computer**: This is strongly recommended. Many installs use a VM - in EC2. Phabricator installs properly and works well on a normal computer. + in EC2. Phorge installs properly and works well on a normal computer. - **A Shared Host**: This may work, but is not recommended. Many shared - hosting environments have restrictions which prevent some of Phabricator's + hosting environments have restrictions which prevent some of Phorge's features from working. Consider using a normal computer instead. We do not support shared hosts. - **A SAN Appliance, Network Router, Gaming Console, Raspberry Pi, etc.**: - Although you may be able to install Phabricator on specialized hardware, it + Although you may be able to install Phorge on specialized hardware, it is unlikely to work well and will be difficult for us to support. Strongly consider using a normal computer instead. We do not support specialized hardware. - **A Toaster, Car, Firearm, Thermostat, etc.**: Yes, many modern devices now have embedded computing capability. We live in interesting times. However, - you should not install Phabricator on these devices. Instead, install it on + you should not install Phorge on these devices. Instead, install it on a normal computer. We do not support installing on noncomputing devices. -To install the Phabricator server software, you will need an **operating +To install the Phorge server software, you will need an **operating system** on your normal computer which is **not Windows**. Note that the command line interface //does// work on Windows, and you can //use// -Phabricator from any operating system with a web browser. However, the server +Phorge from any operating system with a web browser. However, the server software does not run on Windows. It does run on most other operating systems, so choose one of these instead: - **Linux**: Most installs use Linux. - **Mac OS X**: Mac OS X is an acceptable flavor of Linux. - **FreeBSD**: While FreeBSD is certainly not a flavor of Linux, it is a fine - operating system possessed of many desirable qualities, and Phabricator will + operating system possessed of many desirable qualities, and Phorge will install and run properly on FreeBSD. - **Solaris, etc.**: Other systems which look like Linux and quack like Linux will generally work fine, although we may suffer a reduced ability to @@ -64,8 +64,8 @@ Beyond an operating system, you will need **a webserver**. works fine. - **Other**: Other webservers which can run PHP are also likely to work fine, although these installation instructions will not cover how to set them up. - - **PHP Builtin Server**: Phabricator will not work with the builtin - webserver because Phabricator depends on making requests to itself on some + - **PHP Builtin Server**: Phorge will not work with the builtin + webserver because Phorge depends on making requests to itself on some workflows, and the builtin webserver is single-threaded. You will also need: @@ -76,14 +76,14 @@ You will also need: You'll probably also need a **domain name**. In particular, you should read this note: -NOTE: Phabricator must be installed on an entire domain. You can not install it -to a path on an existing domain, like `example.com/phabricator/`. Instead, -install it to an entire domain or subdomain, like `phabricator.example.com`. +NOTE: Phorge must be installed on an entire domain. You can not install it +to a path on an existing domain, like `example.com/phorge/`. Instead, +install it to an entire domain or subdomain, like `phorge.example.com`. Level Requirements ================== -To install and administrate Phabricator, you'll need to be comfortable with +To install and administrate Phorge, you'll need to be comfortable with common system administration skills. For example, you should be familiar with using the command line, installing software on your operating system of choice, working with the filesystem, managing processes, dealing with permissions, @@ -95,7 +95,7 @@ need to know. However, if you aren't very familiar or comfortable with using this set of skills to troubleshoot and resolve problems, you may encounter issues which you have substantial difficulty working through. -We assume users installing and administrating Phabricator are comfortable with +We assume users installing and administrating Phorge are comfortable with common system administration skills and concepts. If you aren't, proceed at your own risk and expect that your skills may be tested. @@ -107,10 +107,10 @@ scripts available which should handle most of the things discussed in this document for you: - **RedHat Derivatives**: - [[ https://secure.phabricator.com/diffusion/P/browse/master/scripts/install/install_rhel-derivs.sh + [[ https://we.phorge.it/diffusion/P/browse/master/scripts/install/install_rhel-derivs.sh | install_rhel-derivs.sh ]] - **Ubuntu**: - [[ https://secure.phabricator.com/diffusion/P/browse/master/scripts/install/install_ubuntu.sh + [[ https://we.phorge.it/diffusion/P/browse/master/scripts/install/install_ubuntu.sh | install_ubuntu.sh ]] If those work for you, you can skip directly to the @@ -131,18 +131,18 @@ If you already have LAMP setup, you've probably already got everything you need. It may also be helpful to refer to the install scripts above, even if they don't work for your system. -Now that you have all that stuff installed, grab Phabricator and its +Now that you have all that stuff installed, grab Phorge and its dependencies: $ cd somewhere/ # pick some install directory - somewhere/ $ git clone https://github.com/phacility/arcanist.git - somewhere/ $ git clone https://github.com/phacility/phabricator.git + somewhere/ $ git clone https://we.phorge.it/source/arcanist.git + somewhere/ $ git clone https://we.phorge.it/source/phorge.git Next Steps ========== Continue by: - - configuring Phabricator with the @{article:Configuration Guide}; or - - learning how to keep Phabricator up to date with - @{article:Upgrading Phabricator}. + - configuring Phorge with the @{article:Configuration Guide}; or + - learning how to keep Phorge up to date with + @{article:Upgrading Phorge}. diff --git a/src/docs/user/introduction.diviner b/src/docs/user/introduction.diviner index cbd739fc8d..a591aae160 100644 --- a/src/docs/user/introduction.diviner +++ b/src/docs/user/introduction.diviner @@ -1,33 +1,34 @@ @title Introduction @group intro -This document provides a high-level overview of the Phabricator project. +This document provides a high-level overview of the Phorge project. -= What is Phabricator? = += What is Phorge? = -**Phabricator** (pronounced like the word //fabricator//) is a suite of web +**Phorge** (pronounced like the word //forge//) is a suite of web applications which make it easier to build software, particularly when working -with teams. Phabricator is largely based on Facebook's internal tools. +with teams. Phorge is a fork of Phabricator, which in turn is largely based on +Facebook's internal tools. -The major components of Phabricator are: +The major components of Phorge are: - **Differential**, a code review tool; and - **Diffusion**, a repository browser; and - **Maniphest**, a bug tracker; and - **Phriction**, a wiki. -Phabricator also includes a number of smaller tools. +Phorge also includes a number of smaller tools. -= Why use Phabricator? = += Why use Phorge? = -Phabricator gives you a box of solid tools for a comparatively small setup cost. +Phorge gives you a box of solid tools for a comparatively small setup cost. The tools all work together and are richly integrated. The whole thing is free -and open source. You own all your data. Phabricator is extremely fast and proven +and open source. You own all your data. Phorge is extremely fast and proven to scale both to large datasets (Facebook has 500,000+ commits across many repositories) and large organizations (Facebook has 500+ fulltime engineers). -Phabricator's tools are easy to learn, understand, and use. +Phorge's tools are easy to learn, understand, and use. -However, Phabricator may also not be a good solution for you: +However, Phorge may also not be a good solution for you: - If you develop primarily on Windows, you are likely to find integration with the toolsets you use lacking. @@ -40,4 +41,4 @@ However, Phabricator may also not be a good solution for you: Continue by: - - installing Phabricator with the @{article:Installation Guide}. + - installing Phorge with the @{article:Installation Guide}. diff --git a/src/docs/user/reporting_security.diviner b/src/docs/user/reporting_security.diviner index c74fc40e1a..98293964b1 100644 --- a/src/docs/user/reporting_security.diviner +++ b/src/docs/user/reporting_security.diviner @@ -1,36 +1,17 @@ @title Reporting Security Vulnerabilities @group intro -Describes how to report security vulnerabilities in Phabricator. +Describes how to report security vulnerabilities in Phorge. Overview ======== -Phabricator runs a disclosure and award program through -[[ https://www.hackerone.com/ | HackerOne ]]. This program is the best way to -submit security issues to us, and awards responsible disclosure of -vulnerabilities with cash bounties. You can find our project page -here: +Phorge accepts bug reports on the upstream install. Please use the +[[https://we.phorge.it/maniphest/task/edit/form/1/ | security reporting form]] +to report security vulnerabilities. -(NOTE) https://hackerone.com/phabricator +If you aren't sure if something qualifies, you can submit the issue as a normal + bug report. For instructions, see @{article:Contributing Bug Reports}. -The project page has detailed information about the scope of the program and -how to participate. - -We have a 24 hour response timeline, and are usually able to respond to (and, -very often, fix) issues more quickly than that. - - -Other Channels -============== - -If you aren't sure if something qualifies or don't want to report via -HackerOne, you can submit the issue as a normal bug report. For instructions, -see @{article:Contributing Bug Reports}. - - -Get Updated -=========== - -General information about security changes is reported weekly in the -[[ https://secure.phabricator.com/w/changelog/ | Changelog ]]. +General information about security changes is reported in the +[[ https://we.phorge.it/w/changelog/ | Changelog ]]. diff --git a/src/docs/user/support.diviner b/src/docs/user/support.diviner index 20c4b241f0..a4d7cbafca 100644 --- a/src/docs/user/support.diviner +++ b/src/docs/user/support.diviner @@ -2,4 +2,35 @@ @short Support @group intro -Effective June 1, 2021: Phabricator is no longer actively supported. +Resources for reporting bugs, requesting features, and getting support. + +Overview +======== + +The upstream provides free support for a range of problems. + + +Reporting Security Vulnerabilities +================================== + +The upstream accepts, fixes, and awards bounties for reports of material +security issues with the software. + +To report security issues, see @{article:Reporting Security Vulnerabilities}. + + +Reporting Bugs +============== + +The upstream will accept **reproducible** bug reports in modern, first-party +production code running in reasonable environments. Before submitting a bug +report you **must update** to the latest version of Phorge. + +To report bugs, see @{article:Contributing Bug Reports}. + + +Contributing +============ + +If you'd like to contribute to Phorge, start with +@{article:Contributor Introduction}. diff --git a/src/docs/user/upgrading.diviner b/src/docs/user/upgrading.diviner index 705b38e4ee..4b11317dee 100644 --- a/src/docs/user/upgrading.diviner +++ b/src/docs/user/upgrading.diviner @@ -1,12 +1,12 @@ -@title Upgrading Phabricator +@title Upgrading Phorge @group intro -This document contains instructions for keeping Phabricator up to date. +This document contains instructions for keeping Phorge up to date. Overview ======== -Phabricator is under active development, and new features are released +Phorge is under active development, and new features are released continuously. Staying up to date will keep your install secure. We recommend installs upgrade regularly (every 1-2 weeks). Upgrades usually go @@ -18,8 +18,8 @@ to a useful new feature or an important security change. Staying On Top of Changes ========================= -We release a weekly [[https://secure.phabricator.com/w/changelog | Changelog]], -which describes changes in the previous week. You can look at the changelogs +We release a [[https://we.phorge.it/w/changelog | Changelog]], +which describes changes over time. You can look at the changelogs for an idea of what new features are available, upcoming changes, security information, and warnings about compatibility issues or migrations. @@ -27,21 +27,18 @@ information, and warnings about compatibility issues or migrations. Stable Branch ============= -You can either run the `master` or `stable` branch of Phabricator. The `stable` -branch is run in the [[ https://phacility.com | Phacility Cluster ]], and lags -about a week behind `master`. +You can either run the `master` or `stable` branch of Phorge. The `stable` +branch is a little more stable than `master`, and may be helpful if you +administrate a larger install. -The `stable` branch is a little more stable than `master`, and may be helpful -if you administrate a larger install. - -We promote `master` to `stable` about once a week, then publish the changelog -and deploy the cluster. During the week, major bugfixes are cherry-picked to -the `stable` branch. The changelog lists the `stable` hashes for that week, -as well as any fixes which were cherry-picked. +We promote `master` to `stable` frequently, then publish the changelog. During +the week, major bugfixes are cherry-picked to the `stable` branch. The changelog + lists the `stable` hashes for that week, as well as any fixes which were + cherry-picked. To switch to `stable`, check the branch out in each working copy: - phabricator/ $ git checkout stable + phorge/ $ git checkout stable arcanist/ $ git checkout stable You can now follow the upgrade process normally. @@ -50,26 +47,26 @@ You can now follow the upgrade process normally. Upgrade Process =============== -IMPORTANT: You **MUST** restart Phabricator after upgrading. For help, see -@{article:Restarting Phabricator}. +IMPORTANT: You **MUST** restart Phorge after upgrading. For help, see +@{article:Restarting Phorge}. -IMPORTANT: You **MUST** upgrade `arcanist` and `phabricator` at the same time. +IMPORTANT: You **MUST** upgrade `arcanist` and `phorge` at the same time. -Phabricator runs on many different systems, with many different webservers. +Phorge runs on many different systems, with many different webservers. Given this diversity, we don't currently maintain a comprehensive upgrade script which can work on any system. However, the general steps are the same on every system: - Stop the webserver (including `php-fpm`, if you use it). - - Stop the daemons, with `phabricator/bin/phd stop`. - - Run `git pull` in `arcanist/` and `phabricator/`. - - Run `phabricator/bin/storage upgrade`. - - Start the daemons, with `phabricator/bin/phd start`. + - Stop the daemons, with `phorge/bin/phd stop`. + - Run `git pull` in `arcanist/` and `phorge/`. + - Run `phorge/bin/storage upgrade`. + - Start the daemons, with `phorge/bin/phd start`. - Restart the webserver (and `php-fpm`, if you stopped it earlier). For some more discussion details, see @{article:Configuration Guide}. -This template script roughly outlines the steps required to upgrade Phabricator. +This template script roughly outlines the steps required to upgrade Phorge. You'll need to adjust paths and commands a bit for your particular system: ```lang=sh @@ -78,23 +75,23 @@ You'll need to adjust paths and commands a bit for your particular system: set -e set -x -# This is an example script for updating Phabricator, similar to the one used to -# update . It might not work perfectly on your +# This is an example script for updating Phorge, similar to the one used to +# update . It might not work perfectly on your # system, but hopefully it should be easy to adapt. This script is not intended # to work without modifications. # NOTE: This script assumes you are running it from a directory which contains -# arcanist/, and phabricator/. +# arcanist/, and phorge/. ROOT=`pwd` # You can hard-code the path here instead. ### STOP WEB SERVER AND DAEMONS ############################################### # Stop daemons. -$ROOT/phabricator/bin/phd stop +$ROOT/phorge/bin/phd stop # If running the notification server, stop it. -# $ROOT/phabricator/bin/aphlict stop +# $ROOT/phorge/bin/aphlict stop # Stop the webserver (apache, nginx, lighttpd, etc). This command will differ # depending on which system and webserver you are running: replace it with an @@ -108,20 +105,20 @@ sudo /etc/init.d/httpd stop cd $ROOT/arcanist git pull -cd $ROOT/phabricator +cd $ROOT/phorge git pull # Upgrade the database schema. You may want to add the "--force" flag to allow # this script to run noninteractively. -$ROOT/phabricator/bin/storage upgrade +$ROOT/phorge/bin/storage upgrade # Restart the webserver. As above, this depends on your system and webserver. # NOTE: If you're running php-fpm, restart it here too. sudo /etc/init.d/httpd start # Restart daemons. -$ROOT/phabricator/bin/phd start +$ROOT/phorge/bin/phd start # If running the notification server, start it. -# $ROOT/phabricator/bin/aphlict start +# $ROOT/phorge/bin/aphlict start ``` diff --git a/src/docs/user/userguide/almanac.diviner b/src/docs/user/userguide/almanac.diviner index 9250724c03..5b13a9f80a 100644 --- a/src/docs/user/userguide/almanac.diviner +++ b/src/docs/user/userguide/almanac.diviner @@ -11,7 +11,7 @@ lists of //devices// and //services// that humans and other applications can use to keep track of what is running where. Almanac is an infrastructure application that will normally be used by -administrators to configure advanced Phabricator features. In most cases, +administrators to configure advanced Phorge features. In most cases, normal users will very rarely interact with Almanac directly. At a very high level, Almanac can be thought of as a bit like a DNS server. @@ -19,11 +19,11 @@ Callers ask it for information about services, and it responds with details about which devices host those services. However, it can respond to a broader range of queries and provide more detailed responses than DNS alone can. -Today, the primary use cases for Almanac are internal to Phabricator: +Today, the primary use cases for Almanac are internal to Phorge: - Providing a list of build servers to Drydock so it can run build and integration tasks. - - Configuring Phabricator to operate in a cluster setup. + - Configuring Phorge to operate in a cluster setup. Beyond internal uses, Almanac is a general-purpose service and device inventory application and can be used to configure and manage other types of service and diff --git a/src/docs/user/userguide/amazon_rds.diviner b/src/docs/user/userguide/amazon_rds.diviner index 442d499013..aab2058140 100644 --- a/src/docs/user/userguide/amazon_rds.diviner +++ b/src/docs/user/userguide/amazon_rds.diviner @@ -6,7 +6,7 @@ Discusses using Amazon RDS as a database. Overview ======== -Phabricator works with Amazon RDS. However, most of our documentation and setup +Phorge works with Amazon RDS. However, most of our documentation and setup checks assume you are using local MySQL, and upstream support is less available for RDS. diff --git a/src/docs/user/userguide/arcanist.diviner b/src/docs/user/userguide/arcanist.diviner index 0de18a9358..943c8a2193 100644 --- a/src/docs/user/userguide/arcanist.diviner +++ b/src/docs/user/userguide/arcanist.diviner @@ -1,9 +1,9 @@ @title Arcanist User Guide @group userguide -Guide to Arcanist, a command-line interface to Phabricator. +Guide to Arcanist, a command-line interface to Phorge. -Arcanist provides command-line access to many Phabricator tools (like +Arcanist provides command-line access to many Phorge tools (like Differential, Files, and Paste), integrates with static analysis ("lint") and unit tests, and manages common workflows like getting changes into Differential for review. @@ -12,7 +12,7 @@ A detailed command reference is available by running `arc help`. This document provides an overview of common workflows and installation. Arcanist has technical, contributor-focused documentation here: - + = Quick Start = @@ -143,7 +143,7 @@ three sources, in order: # User configuration is read from `~/.arcconfig`. This file is JSON, and can be updated using `arc set-config`. # Host configuration is read from `/etc/arcconfig` (on Windows, the path - is `C:\ProgramData\Phabricator\Arcanist\config`). + is `C:\ProgramData\Phorge\Arcanist\config`). Arcanist uses the first definition it encounters as the runtime setting. diff --git a/src/docs/user/userguide/arcanist_coverage.diviner b/src/docs/user/userguide/arcanist_coverage.diviner index cb25c0cc74..b1b449a7db 100644 --- a/src/docs/user/userguide/arcanist_coverage.diviner +++ b/src/docs/user/userguide/arcanist_coverage.diviner @@ -1,7 +1,7 @@ @title Arcanist User Guide: Code Coverage @group userguide -Explains code coverage features in Arcanist and Phabricator. +Explains code coverage features in Arcanist and Phorge. This is a configuration guide that helps you set up advanced features. If you're just getting started, you don't need to look at this yet. Instead, start with @@ -27,9 +27,9 @@ to `src/some/file.php` and give you a detailed coverage report. If the test engine enables coverage by default, it will be uploaded to Differential and displayed in the right gutter when viewing diffs. -= Enabling Coverage for Arcanist and Phabricator = += Enabling Coverage for Arcanist and Phorge = -If you're contributing, Arcanist and Phabricator support coverage if +If you're contributing, Arcanist and Phorge support coverage if you install Xdebug: http://xdebug.org/ diff --git a/src/docs/user/userguide/arcanist_diff.diviner b/src/docs/user/userguide/arcanist_diff.diviner index 4140deb537..7d5a0161d9 100644 --- a/src/docs/user/userguide/arcanist_diff.diviner +++ b/src/docs/user/userguide/arcanist_diff.diviner @@ -14,7 +14,7 @@ information. = Overview = While `arc` has a large number of commands that interface with various -Phabricator applications, the primary use of `arc` is to send changes for +Phorge applications, the primary use of `arc` is to send changes for review in Differential (for more information on Differential, see @{article:Differential User Guide}). If you aren't familiar with Differential, it may be instructive to read that article first to understand the big picture @@ -167,7 +167,7 @@ You can use `arc help ` for detailed help with any of these. Differential will make a guess about a next step on accepted revisions, but it may not be the best next step for your workflow. -Phabricator will also automatically close revisions if the changes are pushed +Phorge will also automatically close revisions if the changes are pushed to a repository that is tracked in Diffusion. Specifically, it will close revisions based on commit and tree hashes, and `Differential Revision` identifiers in commit messages. diff --git a/src/docs/user/userguide/arcanist_lint_unit.diviner b/src/docs/user/userguide/arcanist_lint_unit.diviner index 6ecc9aaa3e..dc7f336fef 100644 --- a/src/docs/user/userguide/arcanist_lint_unit.diviner +++ b/src/docs/user/userguide/arcanist_lint_unit.diviner @@ -38,7 +38,7 @@ make this work, you need to do three things: If you haven't created a library for the class to live in yet, you need to do that first. Follow the instructions in -@{article@phabcontrib:Adding New Classes}, then make the library loadable by +@{article@contrib:Adding New Classes}, then make the library loadable by adding it to your `.arcconfig` like this: { diff --git a/src/docs/user/userguide/arcanist_new_project.diviner b/src/docs/user/userguide/arcanist_new_project.diviner index a8e8e49202..86e57c0c86 100644 --- a/src/docs/user/userguide/arcanist_new_project.diviner +++ b/src/docs/user/userguide/arcanist_new_project.diviner @@ -15,11 +15,11 @@ An `.arcconfig` file is a JSON file which you check into your project's root. Arcanist uses `.arcconfig` files to customize a number of things about its behavior. The first thing you're likely to want to configure is the URI -for your Phabricator install. A simple, valid file looks something like this: +for your Phorge install. A simple, valid file looks something like this: name=.arcconfig { - "phabricator.uri" : "https://phabricator.example.com/" + "phabricator.uri" : "https://phorge.example.com/" } For details on available options, see below. @@ -31,7 +31,7 @@ configuration, not user configuration. Common options are: - - **phabricator.uri**: the URI for the Phabricator install that `arc` should + - **phabricator.uri**: the URI for the Phorge install that `arc` should connect to when run in this project. This option was previously called `conduit_uri`. - **repository.callsign**: The callsign of this repository in Diffusion. @@ -47,7 +47,7 @@ Other options include: - **load**: list of additional Phutil libraries to load at startup. See below for details about path resolution, or see - @{article@phabcontrib:Adding New Classes} for a general introduction to + @{article@contrib:Adding New Classes} for a general introduction to libphutil libraries. - **https.cabundle**: specifies the path to an alternate certificate bundle for use when making HTTPS connections. @@ -62,9 +62,9 @@ Other options include: These options are supported, but their use is discouraged: - **http.basicauth.user**: specify an HTTP basic auth username for use when - connecting to Phabricator. + connecting to Phorge. - **http.basicauth.pass**: specify an HTTP basic auth password for use when - connecting to Phabricator. + connecting to Phorge. - **https.blindly-trust-domains**: a list of domains to trust blindly over HTTPS, even if their certificates are invalid. This is a brute force solution to certificate validity problems, and is discouraged. Instead, @@ -182,7 +182,7 @@ some features won't work unless you set up an `.arcconfig` file. Without `.arcconfig`: - - You will need to set a default Phabricator URI with + - You will need to set a default Phorge URI with `arc set-config default `, or specify an explicit URI with `--conduit-uri` each time you run a command. - You will not be able to run linters through arc unless you pass `--engine` diff --git a/src/docs/user/userguide/arcanist_quick_start.diviner b/src/docs/user/userguide/arcanist_quick_start.diviner index 25847ab8a6..2326add155 100644 --- a/src/docs/user/userguide/arcanist_quick_start.diviner +++ b/src/docs/user/userguide/arcanist_quick_start.diviner @@ -40,17 +40,17 @@ Create a `.arcconfig` file in your project's working copy: yourproject/ $ $EDITOR .arcconfig yourproject/ $ cat .arcconfig { - "phabricator.uri" : "https://phabricator.example.com/" + "phabricator.uri" : "https://phorge.example.com/" } -Set `phabricator.uri` to the URI for your Phabricator install (where `arc` +Set `phabricator.uri` to the URI for your Phorge install (where `arc` should send changes to). NOTE: You should **commit this file** to the repository. = Install Arcanist Credentials = -Credentials allow you to authenticate. You must have an account on Phabricator +Credentials allow you to authenticate. You must have an account on Phorge before you can perform this step. $ cd yourproject/ @@ -58,7 +58,7 @@ before you can perform this step. ... Follow the instructions. This will link your user account on your local machine -to your Phabricator account. +to your Phorge account. = Send Changes For Review = diff --git a/src/docs/user/userguide/audit.diviner b/src/docs/user/userguide/audit.diviner index 5223f6c969..c179a9e61f 100644 --- a/src/docs/user/userguide/audit.diviner +++ b/src/docs/user/userguide/audit.diviner @@ -1,13 +1,13 @@ @title Audit User Guide @group userguide -Guide to using Phabricator to audit published commits. +Guide to using Phorge to audit published commits. Overview ======== -Phabricator supports two code review workflows, "review" (pre-publish) and +Phorge supports two code review workflows, "review" (pre-publish) and "audit" (post-publish). To understand the differences between the two, see @{article:User Guide: Review vs Audit}. @@ -59,14 +59,14 @@ a typical audit workflow: - Alice publishes a commit containing some Javascript. - This triggers an audit request to Bailey, the Javascript technical lead on the project (see below for a description of trigger mechanisms). - - Later, Bailey logs into Phabricator and sees the audit request. She ignores + - Later, Bailey logs into Phorge and sees the audit request. She ignores it for the moment, since it isn't blocking anything. At the end of the week she looks through her open requests to see what the team has been up to. - Bailey notices a few minor problems with Alice's commit. She leaves comments describing improvements and uses "Raise Concern" to send the commit back into Alice's queue. - - Later, Alice logs into Phabricator and sees that Bailey has raised a + - Later, Alice logs into Phorge and sees that Bailey has raised a concern (usually, Alice will also get an email). She resolves the issue somehow, maybe by making a followup commit with fixes. - After the issues have been dealt with, she uses "Request Verification" to @@ -163,7 +163,7 @@ The `bin/audit` command allows you to perform several maintenance operations. Get more information about a command by running: ``` -phabricator/ $ ./bin/audit help +phorge/ $ ./bin/audit help ``` Supported operations are: diff --git a/src/docs/user/userguide/calendar.diviner b/src/docs/user/userguide/calendar.diviner index 5c09a753b7..14fb825b59 100644 --- a/src/docs/user/userguide/calendar.diviner +++ b/src/docs/user/userguide/calendar.diviner @@ -29,7 +29,7 @@ Reminders are sent 15 minutes before events begin. Availability ============ -Across all applications, Phabricator shows a red dot next to usernames if the +Across all applications, Phorge shows a red dot next to usernames if the user is currently attending an event. This provides a hint that they may be in a meeting (or on vacation) and could take a while to get back to you about a revision or task. diff --git a/src/docs/user/userguide/calendar_exports.diviner b/src/docs/user/userguide/calendar_exports.diviner index 487de559a4..1d6d6bc1da 100644 --- a/src/docs/user/userguide/calendar_exports.diviner +++ b/src/docs/user/userguide/calendar_exports.diviner @@ -9,15 +9,15 @@ Overview IMPORTANT: Calendar is a prototype application. See @{article:User Guide: Prototype Applications}. -You can export events from Phabricator to other calendar applications like +You can export events from Phorge to other calendar applications like **Google Calendar** or **Calendar.app**. This document will guide you through -how to export event data from Phabricator. +how to export event data from Phorge. When you export events into another application, they generally will not be editable from that application. Exporting events allows you to create one calendar that shows all the events you care about in whatever application you prefer (so you can keep track of everything you need to do), but does not let -you edit Phabricator events from another application. +you edit Phorge events from another application. When exporting events, you can either export individual events one at a time or export an entire group of events (for example, all events you are attending). @@ -75,7 +75,7 @@ The **policy modes** for exports are: information, as though they were logged in with your account. WARNING: Anyone who learns the URI for an export can see the data you choose -to export, even if they don't have a Phabricator account! Be careful about how +to export, even if they don't have a Phorge account! Be careful about how much data you export and treat the URI as a secret. If you accidentally share a URI, you can disable the export. diff --git a/src/docs/user/userguide/calendar_imports.diviner b/src/docs/user/userguide/calendar_imports.diviner index a8fbc6ff09..bebfb84ed2 100644 --- a/src/docs/user/userguide/calendar_imports.diviner +++ b/src/docs/user/userguide/calendar_imports.diviner @@ -9,14 +9,14 @@ Overview IMPORTANT: Calendar is a prototype application. See @{article:User Guide: Prototype Applications}. -You can import events into Phabricator to other calendar applications or from +You can import events into Phorge to other calendar applications or from `.ics` files. This document will guide you through how to import event data -into Phabricator. +into Phorge. When you import events from another application, they can not be edited in -Phabricator. Importing events allows you to share events or keep track of +Phorge. Importing events allows you to share events or keep track of events from different sources, but does not let you edit events from other -applications in Phabricator. +applications in Phorge. Import Policies @@ -36,18 +36,18 @@ Importing `.ics` Files event or an entire event calendar. If you have an event or calendar in `.ics` format, you can import it into -Phabricator in two ways: +Phorge in two ways: - Navigate to {nav Calendar > Imports > Import Events > Import .ics File}. - Drag and drop the file onto a Calendar. -This will create a copy of the event in Phabricator. +This will create a copy of the event in Phorge. If you want to update an imported event later, just repeat this process. The event will be updated with the latest information. Many applications send `.ics` files as email attachments. You can import these -into Phabricator. +into Phorge. .ics Files: Google Calendar @@ -68,9 +68,9 @@ saving the calendar as a `.ics` file. You can also convert an individual event into an `.ics` file by dragging it from the calendar to your desktop (or any other folder). -When you import an event using an `.ics` file, Phabricator can not +When you import an event using an `.ics` file, Phorge can not automatically keep the event up to date. You'll need to repeat the process if -there are changes to the event or calendar later, so Phabricator can learn +there are changes to the event or calendar later, so Phorge can learn about the updates. @@ -78,7 +78,7 @@ Importing .ics URIs ===================== If you have a calendar in another application that supports publishing a -`.ics` URI, you can subscribe to it in Phabricator. This will import the entire +`.ics` URI, you can subscribe to it in Phorge. This will import the entire calendar, and can be configured to automatically keep it up to date and in sync with the external calendar. @@ -87,7 +87,7 @@ below for some guidance on popular calendar applications). Then, browse to {nav Calendar > Imports > Import Events > Import .ics URI}. When you import a URI, you can choose to enable automatic updates. If you do, -Phabricator will periodically update the events it imports from this source. +Phorge will periodically update the events it imports from this source. You can stop this later by turning off the automatic updates or disabling the import. @@ -121,7 +121,7 @@ shared. **Calendar.app** does not support subscriptions via `.ics` URIs. You can export a calendar as an `.ics` file by following the steps above, but -Phabricator can not automatically keep events imported in this way up to date. +Phorge can not automatically keep events imported in this way up to date. Next Steps diff --git a/src/docs/user/userguide/conduit.diviner b/src/docs/user/userguide/conduit.diviner index 35daee505f..dd74919a72 100644 --- a/src/docs/user/userguide/conduit.diviner +++ b/src/docs/user/userguide/conduit.diviner @@ -6,7 +6,7 @@ Overview of the Conduit API. Overview ======== -Conduit is the HTTP API for Phabricator. It is roughly JSON-RPC: you usually +Conduit is the HTTP API for Phorge. It is roughly JSON-RPC: you usually pass a JSON blob, and usually get a JSON blob back, although both call and result formats are flexible in some cases. @@ -30,7 +30,7 @@ in another language without needing a real client. includes examples which show how to format calls. **Other Clients**: There are also clients available in other languages. You -can check the [[ https://secure.phabricator.com/w/community_resources/ | +can check the [[ https://we.phorge.it/w/community_resources/ | Community Resources ]] page for links. API Console diff --git a/src/docs/user/userguide/differential.diviner b/src/docs/user/userguide/differential.diviner index 496fba6388..d48ff4ceff 100644 --- a/src/docs/user/userguide/differential.diviner +++ b/src/docs/user/userguide/differential.diviner @@ -5,7 +5,7 @@ Guide to the Differential (pre-push code review) tool and workflow. = Overview = -Phabricator supports two code review workflows, "review" (pre-push) and +Phorge supports two code review workflows, "review" (pre-push) and "audit" (post-push). To understand the differences between the two, see @{article:User Guide: Review vs Audit}. @@ -14,7 +14,7 @@ This document summarizes the pre-push "review" workflow implemented by the tool = How Review Works = -Code review in Phabricator is a lightweight, asynchronous web-based process. If +Code review in Phorge is a lightweight, asynchronous web-based process. If you are familiar with GitHub, it is similar to how pull requests work: - An author prepares a change to a codebase, then sends it for review. They diff --git a/src/docs/user/userguide/differential_land.diviner b/src/docs/user/userguide/differential_land.diviner index 8d5f30d784..540e4b61f0 100644 --- a/src/docs/user/userguide/differential_land.diviner +++ b/src/docs/user/userguide/differential_land.diviner @@ -1,7 +1,7 @@ @title Differential User Guide: Automated Landing @group userguide -Configuring Phabricator so you can "Land Revision" from the web UI. +Configuring Phorge so you can "Land Revision" from the web UI. Overview @@ -9,7 +9,7 @@ Overview IMPORTANT: This feature is a prototype and has substantial limitations. -Phabricator can be configured so that approved revisions may be published +Phorge can be configured so that approved revisions may be published directly from the web interface. This can make publishing changes more convenient, particularly for open source projects where authors may not have commit access to the repository. This document explains the workflow and how to diff --git a/src/docs/user/userguide/diffusion.diviner b/src/docs/user/userguide/diffusion.diviner index 8ee66d3f8f..60c28006a2 100644 --- a/src/docs/user/userguide/diffusion.diviner +++ b/src/docs/user/userguide/diffusion.diviner @@ -1,7 +1,7 @@ @title Diffusion User Guide @group userguide -Guide to Diffusion, the Phabricator application for hosting and browsing +Guide to Diffusion, the Phorge application for hosting and browsing repositories. Overview @@ -16,7 +16,7 @@ other existing hosting). Both types of repositories can be browsed and interacted with, but hosted repositories support some additional triggers and access controls which are not available for observed repositories. -Diffusion is integrated with the other tools in the Phabricator suite. For +Diffusion is integrated with the other tools in the Phorge suite. For instance: - when you commit Differential revisions to a tracked repository, they are @@ -61,7 +61,7 @@ API to create and edit repositories, see Repository Clustering ===================== -Phabricator repository hosts can be set up in a cluster configuration so you +Phorge repository hosts can be set up in a cluster configuration so you can lose hosts with minimal downtime and data loss. This is an advanced feature which most installs do not need to pursue. @@ -88,7 +88,7 @@ helpful: - get details about automatically taking actions in response to commits in @{article:Diffusion User Guide: Permanent Refs}; or - - understand how Phabricator updates repositories with + - understand how Phorge updates repositories with @{article:Diffusion User Guide: Repository Updates}; or - fix issues with repository imports with @{article:Troubleshooting Repository Imports}. diff --git a/src/docs/user/userguide/diffusion_api.diviner b/src/docs/user/userguide/diffusion_api.diviner index c2508dda72..3eaafa1bb8 100644 --- a/src/docs/user/userguide/diffusion_api.diviner +++ b/src/docs/user/userguide/diffusion_api.diviner @@ -90,7 +90,7 @@ Now that the repository exists, you can add URIs to it. This is optional, and if you're creating a //hosted// repository you may be able to skip this step. -However, if you want Phabricator to observe an existing remote, you'll +However, if you want Phorge to observe an existing remote, you'll configure it here by adding a URI in "Observe" mode. Use the PHID from the previous step to identify the repository you want to add a URI to, and call `diffusion.uri.edit` to create a new URI in Observe mode for the repository. diff --git a/src/docs/user/userguide/diffusion_existing.diviner b/src/docs/user/userguide/diffusion_existing.diviner index 341146f0fd..f0c9bf0a33 100644 --- a/src/docs/user/userguide/diffusion_existing.diviner +++ b/src/docs/user/userguide/diffusion_existing.diviner @@ -10,7 +10,7 @@ Overview If you have an existing repository, you can observe or import it into Diffusion. -Observing a repository creates a read-only copy in Phabricator that is kept +Observing a repository creates a read-only copy in Phorge that is kept up to date by continuously importing new changes. Importing a repository creates a read-write copy. @@ -31,7 +31,7 @@ To observe an existing repository: **URIs** section, in **Observe** mode. - Activate the repository in Diffusion. -This creates a read-only copy of the repository in Phabricator. Phabricator +This creates a read-only copy of the repository in Phorge. Phorge will keep its copy in sync with the remote by periodically polling the remote for changes. diff --git a/src/docs/user/userguide/diffusion_hooks.diviner b/src/docs/user/userguide/diffusion_hooks.diviner index 54c93a933b..0f74a53f58 100644 --- a/src/docs/user/userguide/diffusion_hooks.diviner +++ b/src/docs/user/userguide/diffusion_hooks.diviner @@ -5,7 +5,7 @@ Guide to commit hooks in hosted repositories. = Overview = -Phabricator installs pre-receive/pre-commit hooks in hosted repositories +Phorge installs pre-receive/pre-commit hooks in hosted repositories automatically. They enforce a few rules automatically (like preventing dangerous changes unless a repository is configured to allow them). They can also enforce more complex rules via Herald, using the "Commit Hook: @@ -26,7 +26,7 @@ relevant directory of the repository on disk: - **SVN** Put hooks in `hooks/pre-commit-phabricator.d/`. - **Git** Put hooks in `hooks/pre-receive-phabricator.d/`. - - **Mercurial** Phabricator does not currently support custom hooks in + - **Mercurial** Phorge does not currently support custom hooks in Mercurial. These hooks act like normal `pre-commit` or `pre-receive` hooks: @@ -37,14 +37,14 @@ These hooks act like normal `pre-commit` or `pre-receive` hooks: passed. - They should emit output and return codes like normal hooks do. - These hooks will run only after all the Herald rules have passed and - Phabricator is otherwise ready to accept the commit or push. + Phorge is otherwise ready to accept the commit or push. These additional variables will be available in the environment, in addition to the variables the VCS normally provides: - `PHABRICATOR_REPOSITORY` The PHID of the repository the hook is executing for. - - `PHABRICATOR_USER` The Phabricator username that the session is + - `PHABRICATOR_USER` The Phorge username that the session is authenticated under. - `PHABRICATOR_REMOTE_ADDRESS` The connection's remote address (that is, the IP address of whoever is pushing or committing). diff --git a/src/docs/user/userguide/diffusion_hosting.diviner b/src/docs/user/userguide/diffusion_hosting.diviner index 47197f52fd..c8a1c26eed 100644 --- a/src/docs/user/userguide/diffusion_hosting.diviner +++ b/src/docs/user/userguide/diffusion_hosting.diviner @@ -1,19 +1,19 @@ @title Diffusion User Guide: Repository Hosting @group userguide -Guide to configuring Phabricator repository hosting. +Guide to configuring Phorge repository hosting. Overview ======== -Phabricator can host repositories and provide authenticated read and write +Phorge can host repositories and provide authenticated read and write access to them over HTTP and SSH. This document describes how to configure repository hosting. Understanding Supported Protocols ================================= -Phabricator supports hosting over these protocols: +Phorge supports hosting over these protocols: | VCS | SSH | HTTP | |-----|-----|------| @@ -47,12 +47,12 @@ configure it for technical reasons. Creating System User Accounts ============================= -Phabricator uses two system user accounts, plus a third account if you +Phorge uses two system user accounts, plus a third account if you configure SSH access. This section will guide you through creating and -configuring them. These are system user accounts on the machine Phabricator -runs on, not Phabricator user accounts. +configuring them. These are system user accounts on the machine Phorge +runs on, not Phorge user accounts. -The system accounts Phabricator uses are: +The system accounts Phorge uses are: - The user the webserver runs as. We'll call this `www-user`. - The user the daemons run as. We'll call this `daemon-user`. This @@ -73,22 +73,22 @@ To create these users: the daemons to start as this user. - Create a `vcs-user` if one does not already exist and you plan to set up SSH. When users clone repositories, they will use a URI like - `vcs-user@phabricator.yourcompany.com`, so common names for this user are + `vcs-user@phorge.yourcompany.com`, so common names for this user are `git` or `hg`. Continue below to configure these accounts. -Configuring Phabricator +Configuring Phorge ======================= -Now that you have created or identified these accounts, update the Phabricator +Now that you have created or identified these accounts, update the Phorge configuration to specify them. First, set `phd.user` to the `daemon-user`: ``` -phabricator/ $ ./bin/config set phd.user daemon-user +phorge/ $ ./bin/config set phd.user daemon-user ``` Restart the daemons to make sure this configuration works properly. They should @@ -97,7 +97,7 @@ start as the correct user automatically. If you're using a `vcs-user` for SSH, you should also configure that: ``` -phabricator/ $ ./bin/config set diffusion.ssh-user vcs-user +phorge/ $ ./bin/config set diffusion.ssh-user vcs-user ``` Next, you'll set up `sudo` permissions so these users can interact with one @@ -214,7 +214,7 @@ disabled. If you plan to use authenticated HTTP, you (and all other users) also need to configure a VCS password for your account in {nav Settings > VCS Password}. -Your VCS password must be a different password than your main Phabricator +Your VCS password must be a different password than your main Phorge password because VCS passwords are very easy to accidentally disclose. They are often stored in plaintext in world-readable files, observable in `ps` output, and present in command output and logs. We strongly encourage you to use SSH @@ -234,10 +234,10 @@ Configuring SSH SSH access requires some additional setup. You will configure and run a second, restricted copy of `sshd` on the machine, on a different port from the standard `sshd`. This special copy of `sshd` will serve repository requests and provide -other Phabricator SSH services. +other Phorge SSH services. -NOTE: The Phabricator `sshd` service **MUST** be 6.2 or newer, because -Phabricator relies on the `AuthorizedKeysCommand` option. +NOTE: The Phorge `sshd` service **MUST** be 6.2 or newer, because +Phorge relies on the `AuthorizedKeysCommand` option. Before continuing, you must choose a strategy for which port each copy of `sshd` will run on. The next section lays out various approaches. @@ -273,7 +273,7 @@ This may be very easy to set up, particularly if you are hosted in AWS, and is often the simplest and cleanest approach. **Swap Ports**: You can move the administrative `sshd` to a new port, then run -Phabricator `sshd` on port 22. This is somewhat complicated and can be a bit +Phorge `sshd` on port 22. This is somewhat complicated and can be a bit risky if you make a mistake. See "Moving the sshd Port" below for help. **Change Client Config**: You can run on a nonstandard port, but configure SSH @@ -282,7 +282,7 @@ when connecting to the host. To do this, add a section like this to your `~/.ssh/config`: ``` -Host phabricator.corporation.com +Host phorge.corporation.com Port 2222 ``` @@ -295,7 +295,7 @@ A downside to this approach is that your users will each need to set up their `~/.ssh/config` files individually. This file also allows you to define short names for hosts using the `Host` and -`HostName` options. If you choose to do this, be aware that Phabricator uses +`HostName` options. If you choose to do this, be aware that Phorge uses remote/clone URIs to figure out which repository it is operating in, but can not resolve host aliases defined in your `ssh` config. If you create host aliases they may break some features related to repository identification. @@ -327,7 +327,7 @@ Now that you've decided how you'll handle port assignment, you're ready to continue `sshd` setup. If you plan to connect to a port other than `22`, you should set this port -as `diffusion.ssh-port` in your Phabricator config: +as `diffusion.ssh-port` in your Phorge config: ``` $ ./bin/config set diffusion.ssh-port 2222 @@ -336,22 +336,22 @@ $ ./bin/config set diffusion.ssh-port 2222 This port is not special, and you are free to choose a different port, provided you make the appropriate configuration adjustment below. -**Configure and Start Phabricator SSHD**: Now, you'll configure and start a -copy of `sshd` which will serve Phabricator services, including repositories, +**Configure and Start Phorge SSHD**: Now, you'll configure and start a +copy of `sshd` which will serve Phorge services, including repositories, over SSH. This instance will use a special locked-down configuration that uses -Phabricator to handle authentication and command execution. +Phorge to handle authentication and command execution. There are three major steps: - - Create a `phabricator-ssh-hook.sh` file. - - Create a `sshd_phabricator` config file. + - Create a `phorge-ssh-hook.sh` file. + - Create a `sshd_phorge config file. - Start a copy of `sshd` using the new configuration. -**Create `phabricator-ssh-hook.sh`**: Copy the template in -`phabricator/resources/sshd/phabricator-ssh-hook.sh` to somewhere like -`/usr/libexec/phabricator-ssh-hook.sh` and edit it to have the correct +**Create `phorge-ssh-hook.sh`**: Copy the template in +`phorge/resources/sshd/phorge-ssh-hook.sh` to somewhere like +`/usr/libexec/phorge-ssh-hook.sh` and edit it to have the correct settings. Both the script itself **and** the parent directory the script resides in must @@ -359,15 +359,15 @@ be owned by `root`, and the script must have `755` permissions: ``` $ sudo chown root /path/to/somewhere/ -$ sudo chown root /path/to/somewhere/phabricator-ssh-hook.sh -$ sudo chmod 755 /path/to/somewhere/phabricator-ssh-hook.sh +$ sudo chown root /path/to/somewhere/phorge-ssh-hook.sh +$ sudo chmod 755 /path/to/somewhere/phorge-ssh-hook.sh ``` If you don't do this, `sshd` will refuse to execute the hook. -**Create `sshd_config` for Phabricator**: Copy the template in -`phabricator/resources/sshd/sshd_config.phabricator.example` to somewhere like -`/etc/ssh/sshd_config.phabricator`. +**Create `sshd_config` for Phorge**: Copy the template in +`phorge/resources/sshd/sshd_config.phabricator.example` to somewhere like +`/etc/ssh/sshd_config.phorge`. Open the file and edit the `AuthorizedKeysCommand`, `AuthorizedKeysCommandUser`, and `AllowUsers` settings to be correct for your @@ -376,20 +376,20 @@ system. This configuration file also specifies the `Port` the service should run on. If you intend to run on a non-default port, adjust it now. -**Start SSHD**: Now, start the Phabricator `sshd`: +**Start SSHD**: Now, start the Phorge `sshd`: - sudo /path/to/sshd -f /path/to/sshd_config.phabricator + sudo /path/to/sshd -f /path/to/sshd_config.phorge If you did everything correctly, you should be able to run this command: ``` -$ echo {} | ssh vcs-user@phabricator.yourcompany.com conduit conduit.ping +$ echo {} | ssh vcs-user@phorge.yourcompany.com conduit conduit.ping ``` ...and get a response like this: ```lang=json -{"result":"phabricator.yourcompany.com","error_code":null,"error_info":null} +{"result":"phorge.yourcompany.com","error_code":null,"error_info":null} ``` If you get an authentication error, make sure you added your public key in @@ -421,7 +421,7 @@ Troubleshooting HTTP Some general tips for troubleshooting problems with HTTP: - - Make sure `diffusion.allow-http-auth` is enabled in your Phabricator config. + - Make sure `diffusion.allow-http-auth` is enabled in your Phorge config. - Make sure HTTP serving is enabled for the repository you're trying to clone. You can find this in {nav Edit Repository > Hosting}. - Make sure you've configured a VCS password. This is separate from your main @@ -467,20 +467,20 @@ Some general tips for troubleshooting problems with SSH: You should see an `svn checkout svn+ssh://...`, `git clone ssh://...` or `hg clone ssh://...` command. Run that command verbatim to clone the repository. - - Check your `phabricator-ssh-hook.sh` file for proper settings. - - Check your `sshd_config.phabricator` file for proper settings. + - Check your `phorge-ssh-hook.sh` file for proper settings. + - Check your `sshd_config.phorge` file for proper settings. To troubleshoot SSH setup: connect to the server with `ssh`, without running a command. You may need to use the `-T` flag, and will need to use `-p` if you are running on a nonstandard port. You should see a message like this one: - $ ssh -T -p 2222 vcs-user@phabricator.yourcompany.com - phabricator-ssh-exec: Welcome to Phabricator. + $ ssh -T -p 2222 vcs-user@phorge.yourcompany.com + phorge-ssh-exec: Welcome to Phorge. You are logged in as alincoln. You haven't specified a command to run. This means you're requesting an - interactive shell, but Phabricator does not provide an interactive shell over + interactive shell, but Phorge does not provide an interactive shell over SSH. Usually, you should run a command like `git clone` or `hg push` rather than @@ -498,7 +498,7 @@ settings: - The `vcs-user` has `NP` in `/etc/shadow`. - The `vcs-user` has `/bin/sh` or some other valid shell in `/etc/passwd`. - Your SSH private key is correct, and you've added the corresponding - public key to Phabricator in the Settings panel. + public key to Phorge in the Settings panel. If you can get this far, but can't execute VCS commands like `git clone`, there is probably an issue with your `sudoers` configuration. Check: @@ -513,7 +513,7 @@ is probably an issue with your `sudoers` configuration. Check: It may also be helpful to run `sshd` in debug mode: - $ /path/to/sshd -d -d -d -f /path/to/sshd_config.phabricator + $ /path/to/sshd -d -d -d -f /path/to/sshd_config.phorge This will run it in the foreground and emit a large amount of debugging information when you connect to it. @@ -525,7 +525,7 @@ doing something like this: $ sudo -E -n -u daemon-user -- /path/to/some/vcs-binary --help That will try to run the binary via `sudo` in a manner similar to the way that -Phabricator will run it. This can give you better error messages about issues +Phorge will run it. This can give you better error messages about issues with `sudoers` configuration. @@ -533,7 +533,7 @@ Miscellaneous Troubleshooting ============================= - If you're getting an error about `svnlook` not being found, add the path - where `svnlook` is located to the Phabricator configuration + where `svnlook` is located to the Phorge configuration `environment.append-paths` (even if it already appears in PATH). This issue is caused by SVN wiping the environment (including PATH) when invoking commit hooks. @@ -543,10 +543,10 @@ Moving the sshd Port ==================== If you want to move the standard (administrative) `sshd` to a different port to -make Phabricator repository URIs cleaner, this section has some tips. +make Phorge repository URIs cleaner, this section has some tips. This is optional, and it is normally easier to do this by putting a load -balancer in front of Phabricator and having it accept TCP traffic on port 22 +balancer in front of Phorge and having it accept TCP traffic on port 22 and forward it to some other port. When moving `sshd`, be careful when editing the configuration. If you get it @@ -582,8 +582,8 @@ Very carefully, restart `sshd`. Verify that you can connect on the new port: ssh -p 222 ... -Now you can move the Phabricator `sshd` to port 22, then adjust the value -for `diffusion.ssh-port` in your Phabricator configuration. +Now you can move the Phorge `sshd` to port 22, then adjust the value +for `diffusion.ssh-port` in your Phorge configuration. No Direct Pushes @@ -591,8 +591,8 @@ No Direct Pushes You may get an error about "No Direct Pushes" when trying to push. This means you are pushing directly to the repository instead of pushing through -Phabricator. This is not supported: writes to hosted repositories must go -through Phabricator so it can perform authentication, enforce permissions, +Phorge. This is not supported: writes to hosted repositories must go +through Phorge so it can perform authentication, enforce permissions, write logs, proxy requests, apply rewriting, etc. One way to do a direct push by mistake is to use a `file:///` URI to interact @@ -616,9 +616,9 @@ MUST configure things as described above for hosted repositories to work. The technical reason this error occurs is that the `PHABRICATOR_USER` variable is not defined in the environment when commit hooks run. This variable is set -by Phabricator when a request passes through the authentication layer that this +by Phorge when a request passes through the authentication layer that this document provides instructions for configuring. Its absence indicates that the -request did not pass through Phabricator. +request did not pass through Phorge. Next Steps diff --git a/src/docs/user/userguide/diffusion_managing.diviner b/src/docs/user/userguide/diffusion_managing.diviner index e3743526e9..c0b97cb53c 100644 --- a/src/docs/user/userguide/diffusion_managing.diviner +++ b/src/docs/user/userguide/diffusion_managing.diviner @@ -37,7 +37,7 @@ Basics: Callsigns ================= Each repository can optionally be identified by a "callsign", which is a short -uppercase string like "P" (for Phabricator) or "ARC" (for Arcanist). +uppercase string like "P" (for Phorge) or "ARC" (for Arcanist). The primary goal of callsigns is to namespace commits to SVN repositories: if you use multiple SVN repositories, each repository has a revision 1, revision 2, @@ -55,11 +55,11 @@ If you choose to assign a callsign to a repository, it must be unique within an install but do not need to be globally unique, so you are free to use the single-letter callsigns for brevity. For example, Facebook uses "E" for the Engineering repository, "O" for the Ops repository, "Y" for a Yum package -repository, and so on, while Phabricator uses "P" and Arcanist uses "ARC". +repository, and so on, while Phorge uses "P" and Arcanist uses "ARC". Keeping callsigns brief will make them easier to use, and the use of one-character callsigns is encouraged if they are reasonably evocative. -If you configure a callsign like `XYZ`, Phabricator will activate callsign URIs +If you configure a callsign like `XYZ`, Phorge will activate callsign URIs and activate the callsign identifier (like `rXYZ`) for the repository. These more human-readable identifiers can make things a little easier to interact with. @@ -104,7 +104,7 @@ already, so everything will work fine. The majority of repositories do not need to adjust this setting. If your repository is primarily written in some other encoding, specify it here -so Phabricator can convert from it properly when reading content to embed in +so Phorge can convert from it properly when reading content to embed in a webpage or email. @@ -131,7 +131,7 @@ users. This option is only available in Git and Mercurial, because it is impossible to make dangerous changes in Subversion. -This option has no effect if a repository is not hosted because Phabricator +This option has no effect if a repository is not hosted because Phorge can not prevent dangerous changes in a remote repository it is merely observing. @@ -195,7 +195,7 @@ Policies: View ============== The view policy for a repository controls who can view the repository from -the web UI and clone, fetch, or check it out from Phabricator. +the web UI and clone, fetch, or check it out from Phorge. Users who can view a repository can also access the "Manage" interface to review information about the repository and examine the edit history, but can @@ -220,7 +220,7 @@ Policies: Push The push policy for a repository controls who can push changes to the repository. -This policy has no effect if Phabricator is not hosting the repository, because +This policy has no effect if Phorge is not hosting the repository, because it can not control who is allowed to make changes to a remote repository it is merely observing. @@ -230,7 +230,7 @@ You do not need to be able to edit a repository to push to it. Further restrictions on who can push (and what they can push) can be configured for hosted repositories with Herald, which allows you to write more -sophisticated rules that evaluate when Phabricator receives a push. To get +sophisticated rules that evaluate when Phorge receives a push. To get started with Herald, see @{article:Herald User Guide}. Additionally, Git and Mercurial repositories have a setting which allows @@ -241,7 +241,7 @@ will prevent any users from pushing changes which rewrite or destroy history. URIs ==== -The **URIs** panel allows you to add and manage URIs which Phabricator will +The **URIs** panel allows you to add and manage URIs which Phorge will fetch from, serve from, and push to. These options are covered in detail in @{article:Diffusion User Guide: URIs}. @@ -292,14 +292,14 @@ whatever other kind of creative nonsense they manage to dream up. Branches ======== -The **Branches** panel allows you to configure how Phabricator interacts with +The **Branches** panel allows you to configure how Phorge interacts with branches. This panel is not available for Subversion repositories, because Subversion does not have formal branches. You can configure a **Default Branch**. This controls which branch is shown by -default in the UI. If no branch is provided, Phabricator will use `master` in +default in the UI. If no branch is provided, Phorge will use `master` in Git and `default` in Mercurial. **Fetch Refs**: In Git, if you are observing a remote repository, you can @@ -324,13 +324,13 @@ refs/tags/* This may be useful if the remote is on a service like GitHub, GitLab, or Gerrit and uses custom refs (like `refs/pull/` or `refs/changes/`) to store -metadata that you don't want to bring into Phabricator. +metadata that you don't want to bring into Phorge. **Permanent Refs**: To learn more about permanent refs, see: - @{article:Diffusion User Guide: Permanent Refs} -By default, Phabricator considers all branches to be permanent refs. If you +By default, Phorge considers all branches to be permanent refs. If you only want some branches to be treated as permanent refs, specify them here. When specifying branches, you should enter one branch name per line. You can @@ -356,7 +356,7 @@ For more details, see @{article:Harbormaster User Guide}. Automation ========== -The **Automation** panel configures support for allowing Phabricator to make +The **Automation** panel configures support for allowing Phorge to make writes directly to the repository, so that it can perform operations like automatically landing revisions from the web UI. @@ -400,7 +400,7 @@ All three identifiers can be used to refer to the repository in cases where the intent is unambiguous, but only the first two forms work in ambiguous contexts. -For example, if you type `R123` or `rXY` into a comment, Phabricator will +For example, if you type `R123` or `rXY` into a comment, Phorge will recognize them as references to the repository. If you type `xylophone`, it assumes you mean the word "xylophone". @@ -421,7 +421,7 @@ Each commit may have several identifiers: works if a repository has a callsign. - Any unique prefix of the commit hash. -Git and Mercurial use commit hashes to identify commits, and Phabricator will +Git and Mercurial use commit hashes to identify commits, and Phorge will recognize a commit if the hash prefix is unique and sufficiently long. Commit hashes qualified with a repository identifier must be at least 5 characters long; unqualified commit hashes must be at least 7 characters long. @@ -429,7 +429,7 @@ long; unqualified commit hashes must be at least 7 characters long. In Subversion, commit identifiers are sequential integers and prefixes can not be used to identify them. -When rendering the name of a Git or Mercurial commit hash, Phabricator tends to +When rendering the name of a Git or Mercurial commit hash, Phorge tends to shorten it to 12 characters. This "short length" is relatively long compared to Git itself (which often uses 7 characters). See this post on the LKML for a historical explanation of Git's occasional internal use of 7-character hashes: diff --git a/src/docs/user/userguide/diffusion_permanent.diviner b/src/docs/user/userguide/diffusion_permanent.diviner index fba4341c0c..f28e3259f7 100644 --- a/src/docs/user/userguide/diffusion_permanent.diviner +++ b/src/docs/user/userguide/diffusion_permanent.diviner @@ -7,8 +7,8 @@ Overview ======== Diffusion can close tasks and revisions and take other actions when commits -appear in a repository (either because they were pushed to Phabricator, or -because they were pushed to some remote which Phabricator is observing). +appear in a repository (either because they were pushed to Phorge, or +because they were pushed to some remote which Phorge is observing). This document explains when Diffusion acts on commits and how to configure this behavior. @@ -24,11 +24,11 @@ for example, many tools push temporary commits to secret places like `refs/pull/123`, `refs/notes/*`, or `refs/changes/12/345678/1`. Sometimes, human users intentionally push changes to branches like -"tmp-hack-ignore-123". This is formally discouraged by Phabricator, but the +"tmp-hack-ignore-123". This is formally discouraged by Phorge, but the practice is so widespread that we've given up trying to stop anyone from doing it. -Phabricator will import these commits and create pages for them so you can view +Phorge will import these commits and create pages for them so you can view them in the web UI and link to them, but does not take any other actions until they are "published". @@ -40,7 +40,7 @@ using a tool like GitHub) will not. Usually, commits are published by pushing them directly to a permanent branch like "master", or by merging a temporary branch into a permanent branch. -When a commit is published, Phabricator acts on it and: +When a commit is published, Phorge acts on it and: - sends email; - delivers notifications; diff --git a/src/docs/user/userguide/diffusion_symbols.diviner b/src/docs/user/userguide/diffusion_symbols.diviner index 7d14ad92b2..6d6cc8847b 100644 --- a/src/docs/user/userguide/diffusion_symbols.diviner +++ b/src/docs/user/userguide/diffusion_symbols.diviner @@ -5,7 +5,7 @@ Guide to configuring and using the symbol index. = Overview = -Phabricator can maintain a symbol index, which keeps track of where classes +Phorge can maintain a symbol index, which keeps track of where classes and functions are defined in the codebase. Once you set up indexing, you can use the index to do things like: @@ -25,11 +25,11 @@ your codebase and set up a cronjob which pipes its output to: ./scripts/symbols/import_repository_symbols.php -Phabricator includes a script which can identify symbols in PHP projects: +Phorge includes a script which can identify symbols in PHP projects: ./scripts/symbols/generate_php_symbols.php -Phabricator also includes a script which can identify symbols in any +Phorge also includes a script which can identify symbols in any programming language that has classes and/or functions, and is supported by Exuberant Ctags (http://ctags.sourceforge.net): @@ -59,7 +59,7 @@ from the project root (where ".arcconfig" is) beginning with a "/". You can look at `generate_php_symbols.php` for an example of how you might write such a script, and run this command to see its output: - $ cd phabricator/ + $ cd phorge/ $ find . -type f -name '*.php' | ./scripts/symbols/generate_php_symbols.php To actually build the symbol index, pipe this data to the @@ -83,12 +83,12 @@ You can configure some more options by going to {nav Diffusion > (Select You can leave this blank for "All languages". - **Uses Symbols From**: If this project depends on other repositories, add the other repositories which symbols should be looked for here. For example, - Phabricator lists "Arcanist" because it uses classes and functions defined + Phorge lists "Arcanist" because it uses classes and functions defined in `arcanist/`. == External Symbols == -By @{article@phabcontrib:Adding New Classes}, you can teach Phabricator +By @{article@contrib:Adding New Classes}, you can teach Phorge about symbols from the outside world. Extend @{class:DiffusionExternalSymbolsSource}; Once loaded, your new implementation will be used any time a symbol is queried. diff --git a/src/docs/user/userguide/diffusion_updates.diviner b/src/docs/user/userguide/diffusion_updates.diviner index 7012e6228e..dc3c7dfef1 100644 --- a/src/docs/user/userguide/diffusion_updates.diviner +++ b/src/docs/user/userguide/diffusion_updates.diviner @@ -6,7 +6,7 @@ Explains how Diffusion updates repositories to discover new changes. Overview ======== -When Phabricator is configured to import repositories which are hosted +When Phorge is configured to import repositories which are hosted elsewhere, it needs to poll those repositories for changes. If it polls too frequently, it can create too much load locally and on remote services. If it polls too rarely, it may take a long time for commits to show up in the web @@ -15,11 +15,11 @@ interface. This document describes the rules around polling and how to understand and adjust the behavior. In general: - - Phabricator chooses a default poll interval based on repository + - Phorge chooses a default poll interval based on repository activity. These intervals range from every 15 seconds (for active repositories) to every 6 hours (for repositories with no commits in two months). - - If you use `arc` to push commits, or you host repositories on Phabricator, + - If you use `arc` to push commits, or you host repositories on Phorge, repositories automatically update after changes are pushed. - If you don't use `arc` and your repository is hosted elsewhere, this document describes ways you can make polling more responsive. @@ -28,7 +28,7 @@ adjust the behavior. In general: Default Behavior ================ -By default, Phabricator determines how frequently to poll repositories by +By default, Phorge determines how frequently to poll repositories by examining how long it has been since the last commit. In most cases this is fairly accurate and produces good behavior. In particular, it automatically reduces the polling frequency for rarely-used repositories. This dramatically @@ -77,21 +77,21 @@ update frequency so the import finishes as quickly as possible. Triggering Repository Updates ============================= -If you want Phabricator to update a repository more quickly than the default +If you want Phorge to update a repository more quickly than the default update frequency (for example, because you just pushed a commit to it), you can -tell Phabricator that it should schedule an update as soon as possible. +tell Phorge that it should schedule an update as soon as possible. There are several ways to do this: - If you push changes with `arc land` or `arc commit`, this will be done for you automatically. These commits should normally be recognized within a few seconds. - - If your repository is hosted on Phabricator, this will also be done for you + - If your repository is hosted on Phorge, this will also be done for you automatically. - You can schedule an update from the web interface, in Diffusion > (Choose a Repository) > Manage Repository > Status > Update Now. - You can make a call to the Conduit API method `diffusion.looksoon`. This - hints to Phabricator that it should poll a repository as soon as it can. + hints to Phorge that it should poll a repository as soon as it can. All of the other mechanisms do this under the hood. In particular, you may be able to add a commit hook to your external repository @@ -109,7 +109,7 @@ Troubleshooting Updates You can manually run a repository update from the command line to troubleshoot issues, using the `--trace` flag to get full details: - phabricator/ $ ./bin/repository update --trace + phorge/ $ ./bin/repository update --trace To catch potential issues with permissions, run this command as the same user that the daemons run as. diff --git a/src/docs/user/userguide/diffusion_uris.diviner b/src/docs/user/userguide/diffusion_uris.diviner index a19d38d3a5..1e186ce6ee 100644 --- a/src/docs/user/userguide/diffusion_uris.diviner +++ b/src/docs/user/userguide/diffusion_uris.diviner @@ -6,38 +6,38 @@ Guide to configuring repository URIs for fetching, cloning and mirroring. Overview ======== -Phabricator can create, host, observe, mirror, proxy, and import repositories. +Phorge can create, host, observe, mirror, proxy, and import repositories. For example, you can: -**Host Repositories**: Phabricator can host repositories locally. Phabricator +**Host Repositories**: Phorge can host repositories locally. Phorge maintains the writable master version of the repository, and you can push and pull the repository. This is the most straightforward kind of repository configuration, and similar to repositories on other services like GitHub or Bitbucket. -**Observe Repositories**: Phabricator can create a copy of an repository which +**Observe Repositories**: Phorge can create a copy of an repository which is hosted elsewhere (like GitHub or Bitbucket) and track updates to the remote -repository. This will create a read-only copy of the repository in Phabricator. +repository. This will create a read-only copy of the repository in Phorge. -**Mirror Repositories**: Phabricator can publish any repository to mirrors, +**Mirror Repositories**: Phorge can publish any repository to mirrors, overwriting them with an exact copy of the repository that stays up to date as -the source changes. This works with both local repositories that Phabricator is -hosting and remote repositories that Phabricator is observing. +the source changes. This works with both local repositories that Phorge is +hosting and remote repositories that Phorge is observing. **Proxy Repositories**: If you are observing a repository, you can allow users -to read Phabricator's copy of the repository. Phabricator supports granular +to read Phorge's copy of the repository. Phorge supports granular read permissions, so this can let you open a private repository up a little bit in a flexible way. **Import Repositories**: If you have a repository elsewhere that you want to -host on Phabricator, you can observe the remote repository first, then turn +host on Phorge, you can observe the remote repository first, then turn the tracking off once the repository fully synchronizes. This allows you to -copy an existing repository and begin hosting it in Phabricator. +copy an existing repository and begin hosting it in Phorge. You can also import repositories by creating an empty hosted repository and then pushing everything to the repository directly. -You configure the behavior of a Phabricator repository by adding and +You configure the behavior of a Phorge repository by adding and configuring URIs and marking them to be fetched from, mirrored to, clonable, and so on. By configuring all the URIs that a repository should interact with and expose to users, you configure the read, write, and mirroring behavior @@ -50,12 +50,12 @@ detail. Host a Repository ================= -You can create new repositories that Phabricator will host, like you would -create repositories on services like GitHub or Bitbucket. Phabricator will -serve a read-write copy of the repository and you can clone it from Phabricator -and push changes to Phabricator. +You can create new repositories that Phorge will host, like you would +create repositories on services like GitHub or Bitbucket. Phorge will +serve a read-write copy of the repository and you can clone it from Phorge +and push changes to Phorge. -If you haven't already, you may need to configure Phabricator for hosting +If you haven't already, you may need to configure Phorge for hosting before you can create your first hosted repository. For a detailed guide, see @{article:Diffusion User Guide: Repository Hosting}. @@ -64,7 +64,7 @@ This is the default mode for new repositories. To host a repository: - Create a new repository. - Activate it. -Phabricator will create an empty repository and allow you to fetch from it and +Phorge will create an empty repository and allow you to fetch from it and push to it. @@ -72,10 +72,10 @@ Observe a Repository ==================== If you have an existing repository hosted on another service (like GitHub, -Bitbucket, or a private server) that you want to work with in Phabricator, -you can configure Phabricator to observe it. +Bitbucket, or a private server) that you want to work with in Phorge, +you can configure Phorge to observe it. -When observing a repository, Phabricator will keep track of changes in the +When observing a repository, Phorge will keep track of changes in the remote repository and allow you to browse and interact with the repository from the web UI in Diffusion and other applications, but you can continue hosting it elsewhere. @@ -88,7 +88,7 @@ To observe a repository: - If necessary, configure a credential. - Activate the repository. -Phabricator will perform an initial import of the repository, creating a local +Phorge will perform an initial import of the repository, creating a local read-only copy. Once this process completes, it will continue keeping track of changes in the remote, fetching them, and reflecting them in the UI. @@ -98,21 +98,21 @@ Mirror a Repository NOTE: Mirroring is not supported in Subversion. -You can create a read-only mirror of an existing repository. Phabricator will +You can create a read-only mirror of an existing repository. Phorge will continuously publish the state of the source repository to the mirror, creating an exact copy. -For example, if you have a repository hosted in Phabricator that you want to -mirror to GitHub, you can configure Phabricator to automatically maintain the +For example, if you have a repository hosted in Phorge that you want to +mirror to GitHub, you can configure Phorge to automatically maintain the mirror. This is how the upstream repositories are set up. The mirror copy must be read-only for users because any writes made to the -mirror will be undone when Phabricator updates it. The mirroring process copies +mirror will be undone when Phorge updates it. The mirroring process copies the entire repository state exactly, so the remote state will be completely replaced with an exact copy of the source repository. This may remove or destroy information. Normally, you should only mirror to an empty repository. -You can mirror any repository, even if Phabricator is only observing it and not +You can mirror any repository, even if Phorge is only observing it and not hosting it directly. To begin mirroring a repository: @@ -133,14 +133,14 @@ Import a Repository =================== If you have an existing repository that you want to move so it is hosted on -Phabricator, there are three ways to do it: +Phorge, there are three ways to do it: **Observe First**: //(Git, Mercurial)// Observe the existing repository first, -according to the instructions above. Once Phabricator's copy of the repository +according to the instructions above. Once Phorge's copy of the repository is fully synchronized, change the **I/O Type** for the **Observe** URI to **None** to stop fetching changes from the remote. -By default, this will automatically make Phabricator's copy of the repository +By default, this will automatically make Phorge's copy of the repository writable, and you can begin pushing to it. If you've adjusted URI configuration away from the defaults, you may need to set at least one URI to **Read/Write** mode so you can push to it. @@ -169,7 +169,7 @@ repository. Builtin Clone URIs ================== -By default, Phabricator automatically exposes and activates HTTP, HTTPS and +By default, Phorge automatically exposes and activates HTTP, HTTPS and SSH clone URIs by examining configuration. **HTTP**: The `http://` clone URI will be available if these conditions are @@ -198,7 +198,7 @@ Customizing Displayed Clone URIs ================================ If you have an unusual configuration and want the UI to offers users specific -clone URIs other than the URIs that Phabricator serves or interacts with, you +clone URIs other than the URIs that Phorge serves or interacts with, you can add those URIs with the **I/O Type** set to **None** and then set their **Display Type** to **Always**. @@ -214,11 +214,11 @@ Reference: I/O Types This section details the available **I/O Type** options for URIs. -Each repository has some **builtin** URIs. These are URIs hosted by Phabricator +Each repository has some **builtin** URIs. These are URIs hosted by Phorge itself. The modes available for each URI depend primarily on whether it is a builtin URI or not. -**Default**: This setting has Phabricator guess the correct option for the +**Default**: This setting has Phorge guess the correct option for the URI. For **builtin** URIs, the default behavior is //Read/Write// if the repository @@ -228,7 +228,7 @@ For custom URIs, the default type is //None// because we can not automatically guess if you want to ignore, observe, or mirror a URI and //None// is the safest default. -**Observe**: Phabricator will observe this repository and regularly fetch any +**Observe**: Phorge will observe this repository and regularly fetch any changes made to it to a local read-only copy. You can not observe builtin URIs because reading a repository from itself @@ -240,11 +240,11 @@ authorities: the observed remote copy and the hosted local copy. Take the other URI out of //Read/Write// mode first. WARNING: If you observe a remote repository, the entire state of the working -copy that Phabricator maintains will be deleted and replaced with the state of -the remote. If some changes are present only in Phabricator's working copy, +copy that Phorge maintains will be deleted and replaced with the state of +the remote. If some changes are present only in Phorge's working copy, they will be unrecoverably destroyed. -**Mirror**: Phabricator will push any changes made to this repository to the +**Mirror**: Phorge will push any changes made to this repository to the remote URI, keeping a read-only mirror hosted at that URI up to date. This works for both observed and hosted repositories. @@ -253,30 +253,30 @@ This option is not available for builtin URIs because it does not make sense to mirror a repository to itself. It is possible to mirror a repository to another repository that is also -hosted by Phabricator by adding that other repository's URI, although this is +hosted by Phorge by adding that other repository's URI, although this is silly and probably very rarely of any use. WARNING: If you mirror to a remote repository, the entire state of that remote -will be replaced with the state of the working copy Phabricator maintains. If +will be replaced with the state of the working copy Phorge maintains. If some changes are present only in the remote, they will be unrecoverably destroyed. -**None**: Phabricator will not fetch changes from or push changes to this URI. +**None**: Phorge will not fetch changes from or push changes to this URI. For builtin URIs, it will not let users fetch changes from or push changes to this URI. You can use this mode to turn off an Observe URI after an import, stop a Mirror URI from updating, or to add URIs that you're only using to customize which -clone URIs are displayed to the user but don't want Phabricator to interact +clone URIs are displayed to the user but don't want Phorge to interact with directly. -**Read Only**: Phabricator will serve the repository from this URI in read-only +**Read Only**: Phorge will serve the repository from this URI in read-only mode. Users will be able to fetch from it but will not be able to push to it. -Because Phabricator must be able to serve the repository from URIs configured +Because Phorge must be able to serve the repository from URIs configured in this mode, this option is only available for builtin URIs. -**Read/Write**: Phabricator will serve the repository from this URI in +**Read/Write**: Phorge will serve the repository from this URI in read/write mode. Users will be able to fetch from it and push to it. URIs can not be set into this mode if another URI is set to //Observe// mode, @@ -284,7 +284,7 @@ because that would mean the repository had two different authorities: the observed remote copy and the hosted local copy. Take the other URI out of //Observe// mode first. -Because Phabricator must be able to serve the repository from URIs configured +Because Phorge must be able to serve the repository from URIs configured in this mode, this option is only available for builtin URIs. @@ -293,7 +293,7 @@ Reference: Display Types This section details the available **Display Type** options for URIs. -**Default**: Phabricator will guess the correct option for the URI. It +**Default**: Phorge will guess the correct option for the URI. It guesses based on the configured **I/O Type** and whether the URI is **builtin** or not. @@ -315,5 +315,5 @@ Next Steps Continue by: - - configuring Phabricator to host repositories with + - configuring Phorge to host repositories with @{article:Diffusion User Guide: Repository Hosting}. diff --git a/src/docs/user/userguide/diviner.diviner b/src/docs/user/userguide/diviner.diviner index 01484be14c..1c0b6f4f78 100644 --- a/src/docs/user/userguide/diviner.diviner +++ b/src/docs/user/userguide/diviner.diviner @@ -8,7 +8,7 @@ Overview Diviner is an application for creating technical documentation. -This article is maintained in a text file in the Phabricator repository and +This article is maintained in a text file in the Phorge repository and generated into the display document you are currently reading using Diviner. Beyond generating articles, Diviner can also analyze source code and generate @@ -20,7 +20,7 @@ Generating Documentation To generate documentation, run: - phabricator/ $ ./bin/diviner generate --book + phorge/ $ ./bin/diviner generate --book Diviner ".book" Files diff --git a/src/docs/user/userguide/drydock.diviner b/src/docs/user/userguide/drydock.diviner index 0d43f7f3f0..52ef2d74af 100644 --- a/src/docs/user/userguide/drydock.diviner +++ b/src/docs/user/userguide/drydock.diviner @@ -256,5 +256,5 @@ Continue by: - understanding Drydock security concerns with @{article:Drydock User Guide: Security}; or - learning about blueprints in @{article:Drydock Blueprints}; or - - allowing Phabricator to write to repositories with + - allowing Phorge to write to repositories with @{article:Drydock User Guide: Repository Automation}. diff --git a/src/docs/user/userguide/drydock_hosts.diviner b/src/docs/user/userguide/drydock_hosts.diviner index 1b8f22cce1..eb506a9c0a 100644 --- a/src/docs/user/userguide/drydock_hosts.diviner +++ b/src/docs/user/userguide/drydock_hosts.diviner @@ -41,20 +41,20 @@ installing software on hosts. You'll need to make sure any hosts are configured properly with any software you need, and have tools like `git`, `hg` or `svn` that may be required to interact with working copies. -You do **not** need to install PHP, arcanist, or Phabricator on the +You do **not** need to install PHP, arcanist, or Phorge on the hosts unless you are specifically running `arc` commands. **You must configure authentication.** Drydock also does not handle credentials for VCS operations. If you're interacting with repositories hosted on -Phabricator, the simplest way to set this up is something like this: +Phorge, the simplest way to set this up is something like this: - - Create a new bot user in Phabricator. + - Create a new bot user in Phorge. - In {nav Settings > SSH Public Keys}, add a public key or generate a keypair. - Put the private key on your build hosts as `~/.ssh/id_rsa` for whatever user you're connecting with. -This will let processes on the host access Phabricator as the bot user, and +This will let processes on the host access Phorge as the bot user, and use the bot user's permissions to pull and push changes. If you're using hosted repositories from an external service, you can follow diff --git a/src/docs/user/userguide/drydock_quick_start.diviner b/src/docs/user/userguide/drydock_quick_start.diviner index 4b0bd7110d..977b07a482 100644 --- a/src/docs/user/userguide/drydock_quick_start.diviner +++ b/src/docs/user/userguide/drydock_quick_start.diviner @@ -30,7 +30,7 @@ Install `git`, `hg` or `svn` if you haven't already and set up private keys for `builder` so it can pull and push any repositories you want to operate on. -If your repository and/or staging area are hosted in Phabricator, you may want +If your repository and/or staging area are hosted in Phorge, you may want to create a corresponding bot account so you can add keys and give it permissions. diff --git a/src/docs/user/userguide/drydock_repository_automation.diviner b/src/docs/user/userguide/drydock_repository_automation.diviner index 35ef932cc2..25b6343e24 100644 --- a/src/docs/user/userguide/drydock_repository_automation.diviner +++ b/src/docs/user/userguide/drydock_repository_automation.diviner @@ -1,7 +1,7 @@ @title Drydock User Guide: Repository Automation @group userguide -Configuring repository automation so Phabricator can push commits. +Configuring repository automation so Phorge can push commits. Overview @@ -11,7 +11,7 @@ IMPORTANT: This feature is very new and some of the capabilities described in this document are not yet available. This feature as a whole is a prototype. By configuring Drydock and Diffusion appropriately, you can enable **Repository -Automation** for a repository. This will allow Phabricator to make changes +Automation** for a repository. This will allow Phorge to make changes to the repository. @@ -25,7 +25,7 @@ Limitations Security ======== -Configuring repository automation amounts to telling Phabricator where it +Configuring repository automation amounts to telling Phorge where it should perform working copy operations (like merges, cherry-picks and pushes) when doing writes. diff --git a/src/docs/user/userguide/drydock_security.diviner b/src/docs/user/userguide/drydock_security.diviner index 9a212437c7..f7df777ef9 100644 --- a/src/docs/user/userguide/drydock_security.diviner +++ b/src/docs/user/userguide/drydock_security.diviner @@ -14,7 +14,7 @@ anything you aren't comfortable with. For example, running unit tests on Drydock normally involves running relatively untrusted code (it often has a single author and has not yet been reviewed) that needs very few capabilities (generally, it only needs to be able to report -results back to Phabricator). In contrast, automating merge requests on Drydock +results back to Phorge). In contrast, automating merge requests on Drydock involves running trusted code that needs more access (it must be able to write to repositories). @@ -61,11 +61,11 @@ configuring Drydock. | Custom | Special Requirements | Use multiple pools. | Absolute | Special Requirements | Completely isolate all resources. -**Zero Isolation**: Run Drydock operations on the same host that Phabricator -runs on. This is only suitable for developing or testing Phabricator. Any -Drydock operation can potentially compromise Phabricator. It is intentionally +**Zero Isolation**: Run Drydock operations on the same host that Phorge +runs on. This is only suitable for developing or testing Phorge. Any +Drydock operation can potentially compromise Phorge. It is intentionally difficult to configure Drydock to operate in this mode. Running Drydock -operations on the Phabricator host is strongly discouraged. +operations on the Phorge host is strongly discouraged. **Low Isolation**: Designate a separate Drydock host and run Drydock operations on it. This is suitable for small installs and provides a reasonable @@ -105,14 +105,14 @@ approach which defuses only the threats you care about. Attackers have three primary targets: - capturing hosts; - - compromising Phabricator; and + - compromising Phorge; and - compromising the integrity of other Drydock processes. **Attacks against hosts** are the least sophisticated. In this scenario, an attacker wants to run a program like a Bitcoin miner or botnet client on hardware that they aren't paying for or which can't be traced to them. They write a "unit test" or which launches this software, then send a revision -containing this "unit test" for review. If Phabricator is configured to +containing this "unit test" for review. If Phorge is configured to automatically run tests on new revisions, it may execute automatically and give the attacker access to computing resources they did not previously control and which can not easily be traced back to them. @@ -125,22 +125,22 @@ prevent untrusted, anonymous contributors from running tests. For example, create a "Trusted Contributors" project and only run tests if a revision author is a member of the project. -**Attacks against Phabricator** are more sophisticated. In this scenario, an -attacker tries to compromise Phabricator itself (for example, to make themselves +**Attacks against Phorge** are more sophisticated. In this scenario, an +attacker tries to compromise Phorge itself (for example, to make themselves an administrator or gain access to an administrator account). -This is made possible if Drydock is running on the same host as Phabricator or -runs on a privileged subnet with access to resources like Phabricator database +This is made possible if Drydock is running on the same host as Phorge or +runs on a privileged subnet with access to resources like Phorge database hosts. Most installs should be concerned about this attack. The best way to defuse this attack is to run Drydock processes on a separate host which is not on a privileged subnet. For example, use a `build.mycompany.com` host or pool for Drydock processes, separate from your -`phabricator.mycompany.com` host or pool. +`phorge.mycompany.com` host or pool. Even if the host is not privileged, many Drydock processes have some level of privilege (enabling them to clone repositories, or report test results back to -Phabricator). Be aware that tests can hijack credentials they are run with, +Phorge). Be aware that tests can hijack credentials they are run with, and potentially hijack credentials given to other processes on the same hosts. You should use credentials with a minimum set of privileges and assume all processes on a host have the highest level of access that any process on the diff --git a/src/docs/user/userguide/events.diviner b/src/docs/user/userguide/events.diviner index e18578288b..d004dad84f 100644 --- a/src/docs/user/userguide/events.diviner +++ b/src/docs/user/userguide/events.diviner @@ -1,7 +1,7 @@ @title Events User Guide: Installing Event Listeners @group userguide -Using Phabricator event listeners to customize behavior. +Using Phorge event listeners to customize behavior. = Overview = @@ -9,27 +9,27 @@ Using Phabricator event listeners to customize behavior. system is strongly discouraged. We have been removing events since 2013 and will continue to remove events in the future. -Phabricator and Arcanist allow you to install custom runtime event listeners +Phorge and Arcanist allow you to install custom runtime event listeners which can react to certain things happening (like a Maniphest Task being edited or a user creating a new Differential Revision) and run custom code to perform logging, synchronize with other systems, or modify workflows. -These listeners are PHP classes which you install beside Phabricator or -Arcanist, and which Phabricator loads at runtime and runs in-process. They +These listeners are PHP classes which you install beside Phorge or +Arcanist, and which Phorge loads at runtime and runs in-process. They require somewhat more effort upfront than simple configuration switches, but are the most direct and powerful way to respond to events. -= Installing Event Listeners (Phabricator) = += Installing Event Listeners (Phorge) = -To install event listeners in Phabricator, follow these steps: +To install event listeners in Phorge, follow these steps: - Write a listener class which extends @{class@arcanist:PhutilEventListener}. - Add it to a libphutil library, or create a new library (for instructions, - see @{article@phabcontrib:Adding New Classes}. - - Configure Phabricator to load the library by adding it to `load-libraries` - in the Phabricator config. - - Configure Phabricator to install the event listener by adding the class - name to `events.listeners` in the Phabricator config. + see @{article@contrib:Adding New Classes}. + - Configure Phorge to load the library by adding it to `load-libraries` + in the Phorge config. + - Configure Phorge to install the event listener by adding the class + name to `events.listeners` in the Phorge config. You can verify your listener is registered in the "Events" tab of DarkConsole. It should appear at the top under "Registered Event Listeners". You can also @@ -42,8 +42,8 @@ To install event listeners in Arcanist, follow these steps: - Write a listener class which extends @{class@arcanist:PhutilEventListener}. - Add it to a libphutil library, or create a new library (for instructions, - see @{article@phabcontrib:Adding New Classes}. - - Configure Phabricator to load the library by adding it to `load` + see @{article@contrib:Adding New Classes}. + - Configure Phorge to load the library by adding it to `load` in the Arcanist config (e.g., `.arcconfig`, or user/global config). - Configure Arcanist to install the event listener by adding the class name to `events.listeners` in the Arcanist config. @@ -54,7 +54,7 @@ event listener. = Example Listener = -Phabricator includes an example event listener, +Phorge includes an example event listener, @{class:PhabricatorExampleEventListener}, which may be useful as a starting point in developing your own listeners. This listener listens for a test event that is emitted by the script `scripts/util/emit_test_event.php`. @@ -80,7 +80,7 @@ test event was emitted. = Available Events = -You can find a list of all Phabricator events in @{class:PhabricatorEventType}. +You can find a list of all Phorge events in @{class:PhabricatorEventType}. == All Events == @@ -184,9 +184,9 @@ NOTE: This event is unstable and subject to change. If you're having problems with your listener, try these steps: - - If you're getting an error about Phabricator being unable to find the + - If you're getting an error about Phorge being unable to find the listener class, make sure you've added it to a libphutil library and - configured Phabricator to load the library with `load-libraries`. + configured Phorge to load the library with `load-libraries`. - Make sure the listener is registered. It should appear in the "Events" tab of DarkConsole. If it's not there, you may have forgotten to add it to `events.listeners`. diff --git a/src/docs/user/userguide/external_editor.diviner b/src/docs/user/userguide/external_editor.diviner index ce39ad6610..1e44aeec34 100644 --- a/src/docs/user/userguide/external_editor.diviner +++ b/src/docs/user/userguide/external_editor.diviner @@ -30,7 +30,7 @@ editor://open/?file=%f Mapping Repositories ==================== -When you open a file in an external editor, Phabricator needs to be able to +When you open a file in an external editor, Phorge needs to be able to build a URI which includes the correct absolute path on disk to the local version of the file, including the repository directory. diff --git a/src/docs/user/userguide/forms.diviner b/src/docs/user/userguide/forms.diviner index 034293ff29..bd65007e9d 100644 --- a/src/docs/user/userguide/forms.diviner +++ b/src/docs/user/userguide/forms.diviner @@ -1,7 +1,7 @@ @title User Guide: Customizing Forms @group userguide -Guide to prefilling and customizing forms in Phabricator applications. +Guide to prefilling and customizing forms in Phorge applications. Overview ======== @@ -194,17 +194,17 @@ they go to create new objects in an application. Using {nav Mark as "Create" Form} from the detail page for a form configuration, you can mark a form to appear in the create menu. -When a user visits the application, Phabricator finds all the form +When a user visits the application, Phorge finds all the form configurations that are: - marked as "create" forms; and - visible to the user based on policy configuration; and - enabled. -If there is only one such form, Phabricator renders a single "Create" button. +If there is only one such form, Phorge renders a single "Create" button. (If there are zero forms, it renders the button but disables it.) -If there are several such forms, Phabricator renders a dropdown which allows +If there are several such forms, Phorge renders a dropdown which allows the user to choose between them. You can reorder these forms by returning to the configuration list and using @@ -446,7 +446,7 @@ the report like this: //after// creating it and open the policies; or - regardless of their edit form access, they can use the Conduit API to change the task policy; or - - regardless of any policy controls in Phabricator, they can screenshot, + - regardless of any policy controls in Phorge, they can screenshot, print, or forward email about the task to anyone; or - regardless of any technical controls in any software, they can decline to report the issue to you in the first place and sell it on the black market @@ -473,7 +473,7 @@ current configuration may not be exactly the same as the one described below. We run an open source project with a small core team, a moderate number of regular contributors, and a large public userbase. Access to the upstream -Phabricator instance is open to the public. +Phorge instance is open to the public. Although our product is fairly technical, we receive many bug reports and feature requests which are of very poor quality. Some users also ignore all the diff --git a/src/docs/user/userguide/harbormaster.diviner b/src/docs/user/userguide/harbormaster.diviner index a6f2047fdd..cf4b234e82 100644 --- a/src/docs/user/userguide/harbormaster.diviner +++ b/src/docs/user/userguide/harbormaster.diviner @@ -10,7 +10,7 @@ WARNING: Harbormaster is still very rough. Read this document carefully to understand what it can and can not do and what to expect in the future. The Harbormaster application provides build and continuous integration support -for Phabricator. +for Phorge. Harbormaster is not a mature application. You should expect it to have major missing capabilities and to change substantially over time. The current version @@ -95,7 +95,7 @@ plans should therefore look something like this: - Have the build step "Wait for Message" after the external system is notified. - Write custom code on the build server to respond to the request, run a - build, then report the results back to Phabricator by calling the + build, then report the results back to Phorge by calling the `harbormaster.sendmessage` Conduit API. You'll need to write a nontrivial amount of code to get this working today. @@ -149,7 +149,7 @@ side effect of running `arc diff`. In Git, it pushes changes to a tag like The build system can then interact with this copy using normal VCS commands. This is simpler to configure, use, troubleshoot and work with than `arc patch`. -With `arc patch`, the build system downloads patches from Phabricator and +With `arc patch`, the build system downloads patches from Phorge and applies them to a local working copy. This is more complex and more error-prone than staging areas. @@ -201,7 +201,7 @@ area repository and do a checkout of the relevant tag in order to perform a build. **`arc patch`**: You can also have the build system pull changes out of -Phabricator as patches and apply them with `arc patch`. This mechanism is the +Phorge as patches and apply them with `arc patch`. This mechanism is the most complex to configure and debug, and is much less reliable than using staging areas. It is not recommended. @@ -225,6 +225,6 @@ line. Run it as `bin/harbormaster help` for details. In particular, you can run manual builds in the foreground from the CLI to see more details about what they're doing: - phabricator/ $ ./bin/harbormaster build D123 --plan 456 --trace + phorge/ $ ./bin/harbormaster build D123 --plan 456 --trace This may help you understand or debug issues with a build plan. diff --git a/src/docs/user/userguide/herald.diviner b/src/docs/user/userguide/herald.diviner index b7cfdeca95..a3cfa26f54 100644 --- a/src/docs/user/userguide/herald.diviner +++ b/src/docs/user/userguide/herald.diviner @@ -13,7 +13,7 @@ interested in, even if they didn't add you as a reviewer. One way to think about Herald is that it is a lot like the mail rules you can set up in most email clients to organize mail based on "To", "Subject", etc. -Herald works very similarly, but operates on Phabricator objects (like revisions +Herald works very similarly, but operates on Phorge objects (like revisions and commits) instead of emails. For example, you can write a personal rule like this which triggers on tasks: diff --git a/src/docs/user/userguide/jump.diviner b/src/docs/user/userguide/jump.diviner index 0421f194a8..8e6277303b 100644 --- a/src/docs/user/userguide/jump.diviner +++ b/src/docs/user/userguide/jump.diviner @@ -6,7 +6,7 @@ Command reference for global search shortcuts. Overview ======== -Phabricator's global search bar automatically interprets certain commands as +Phorge's global search bar automatically interprets certain commands as shortcuts to make it easy to navigate to specific places. To use these shortcuts, just type them into the global search bar in the main diff --git a/src/docs/user/userguide/legalpad.diviner b/src/docs/user/userguide/legalpad.diviner index 053f275d9f..cd5f6578e9 100644 --- a/src/docs/user/userguide/legalpad.diviner +++ b/src/docs/user/userguide/legalpad.diviner @@ -36,7 +36,7 @@ identifying the corporation they are signing on behalf of) or **No One** (for policy documents or other documents which do not require a signature). **Require Signature** This field allows you to create a document which all of -your users must sign before they can use Phabricator, like a terms of service +your users must sign before they can use Phorge, like a terms of service document. See "Use Case: Terms of Service" below for details. These documents must be signable by individuals. @@ -84,7 +84,7 @@ do other things with their account (you'll need to agree to it, too, as soon as you save your changes, so that will give you a sense of the workflow). Note that although users who have not signed all of the required documents can -not use most Phabricator functions, they can browse other Legalpad documents +not use most Phorge functions, they can browse other Legalpad documents that they have permission to see. This allows a terms document to be supplemented with additional policy or guideline documents that users are free to review before agreeing to the terms. @@ -123,13 +123,3 @@ To add an exemption, go to **Manage Document**, then **View Signatures**, then You can optionally add notes about why a user is exempt from signing a document. To review the notes later (and see who added the exemption), click the colored asterisk in the list view. - - -Roadmap -======== - -You can find discussion about the Legalpad roadmap here: - -https://secure.phabricator.com/T5505 - -If there are features you'd like to see, let us know. diff --git a/src/docs/user/userguide/mail_rules.diviner b/src/docs/user/userguide/mail_rules.diviner index cdbbf1919d..5eb20043a5 100644 --- a/src/docs/user/userguide/mail_rules.diviner +++ b/src/docs/user/userguide/mail_rules.diviner @@ -1,12 +1,12 @@ -@title User Guide: Managing Phabricator Email +@title User Guide: Managing Phorge Email @group userguide -How to effectively manage Phabricator email notifications. +How to effectively manage Phorge email notifications. Overview ======== -Phabricator uses email as a major notification channel, but the amount of email +Phorge uses email as a major notification channel, but the amount of email it sends can seem overwhelming if you're working on an active team. This document discusses some strategies for managing email. @@ -29,13 +29,13 @@ The best approach to managing mail is to write mail rules. Simply writing rules to move mail from Differential, Maniphest and Herald to separate folders will vastly simplify mail management. -Phabricator also adds mail headers (see below) which can allow you to write +Phorge also adds mail headers (see below) which can allow you to write more sophisticated mail rules. Mail Headers ============ -Phabricator sends various information in mail headers that can be useful in +Phorge sends various information in mail headers that can be useful in crafting rules to route and manage mail. To see a full list of headers, use the "View Raw Message" feature in your mail client. diff --git a/src/docs/user/userguide/multi_factor_auth.diviner b/src/docs/user/userguide/multi_factor_auth.diviner index eca85d0f92..cccf3e12b9 100644 --- a/src/docs/user/userguide/multi_factor_auth.diviner +++ b/src/docs/user/userguide/multi_factor_auth.diviner @@ -1,7 +1,7 @@ @title User Guide: Multi-Factor Authentication @group userguide -Explains how multi-factor authentication works in Phabricator. +Explains how multi-factor authentication works in Phorge. Overview ======== @@ -30,11 +30,11 @@ you'll be stopped and asked to enter additional credentials. Usually, this means you'll receive an SMS with a authorization code on your phone, or you'll open an app on your phone which will show you a authorization code or ask you to confirm the action. If you're given a authorization code, -you'll enter it into Phabricator. +you'll enter it into Phorge. -If you're logging in, Phabricator will log you in after you enter the code. +If you're logging in, Phorge will log you in after you enter the code. -If you're taking a sensitive action, Phabricator will sometimes put your +If you're taking a sensitive action, Phorge will sometimes put your account in "high security" mode for a few minutes. In this mode, you can take sensitive actions like changing passwords or SSH keys freely, without entering any more credentials. @@ -64,7 +64,7 @@ Factor: Mobile Phone App (TOTP) =============================== TOTP stands for "Time-based One-Time Password". This factor operates by having -you enter authorization codes from your mobile phone into Phabricator. The codes +you enter authorization codes from your mobile phone into Phorge. The codes change every 30 seconds, so you will need to have your phone with you in order to enter them. @@ -77,16 +77,16 @@ Your company may have a preferred application, or may use some other application, so check any in-house documentation for details. In general, any TOTP application should work properly. -After you've downloaded the application onto your phone, use the Phabricator +After you've downloaded the application onto your phone, use the Phorge settings panel to add a factor to your account. You'll be prompted to scan a QR code, and then read an authorization code from your phone and type it into -Phabricator. +Phorge. Later, when you need to authenticate, you'll follow this same process: launch -the application, read the authorization code, and type it into Phabricator. +the application, read the authorization code, and type it into Phorge. This will prove you have your phone. -Don't lose your phone! You'll need it to log into Phabricator in the future. +Don't lose your phone! You'll need it to log into Phorge in the future. Factor: SMS @@ -123,7 +123,7 @@ to take a sensitive action, you'll be asked to confirm it in the application. Administration: Configuration ============================= -New Phabricator installs start without any multi-factor providers enabled. +New Phorge installs start without any multi-factor providers enabled. Users won't be able to add new factors until you set up multi-factor authentication by configuring at least one provider. @@ -187,7 +187,7 @@ their phone, run this command: ```lang=console # Strip all factors from a given user account. -phabricator/ $ ./bin/auth strip --user --all-types +phorge/ $ ./bin/auth strip --user --all-types ``` You can run `bin/auth help strip` for more detail and all available flags and @@ -198,7 +198,7 @@ This command can selectively strip factors by factor type. You can use ```lang=console # Show supported factor types. -phabricator/ $ ./bin/auth list-factors +phorge/ $ ./bin/auth list-factors ``` Once you've identified the factor types you want to strip, you can strip @@ -207,7 +207,7 @@ types: ```lang=console # Strip all SMS and TOTP factors for a user. -phabricator/ $ ./bin/auth strip --user --type sms --type totp +phorge/ $ ./bin/auth strip --user --type sms --type totp ``` The `bin/auth strip` command can also selectively strip factors for certain @@ -218,5 +218,5 @@ Once you have a provider PHID, use `--provider` to select factors to strip: ```lang=console # Strip all factors for a particular provider. -phabricator/ $ ./bin/auth strip --user --provider +phorge/ $ ./bin/auth strip --user --provider ``` diff --git a/src/docs/user/userguide/multimeter.diviner b/src/docs/user/userguide/multimeter.diviner index 6aab740486..e2f242bcdc 100644 --- a/src/docs/user/userguide/multimeter.diviner +++ b/src/docs/user/userguide/multimeter.diviner @@ -9,7 +9,7 @@ Overview IMPORTANT: This document describes a prototype application. Multimeter is a sampling profiler that can give you coarse information about -Phabricator resource usage. In particular, it can help quickly identify sources +Phorge resource usage. In particular, it can help quickly identify sources of load, like bots or scripts which are making a very large number of requests. Configuring and Using Multimeter @@ -26,7 +26,7 @@ data, at a small performance cost. Using Multimeter ================ -Multimeter shows you what Phabricator has spent time doing recently. By +Multimeter shows you what Phorge has spent time doing recently. By looking at the samples it collects, you can identify major sources of load or resource use, whether they are specific users, pages, subprocesses, or other types of activity. @@ -36,7 +36,7 @@ or configuration to make better use of resources (for example, rewrite bots that are making too many calls), or report specific, actionable issues to the upstream for resolution. -The main screen of Multimeter shows you everything Phabricator has spent +The main screen of Multimeter shows you everything Phorge has spent resources on recently, broken down by action type. Categories are folded up by default, with "(All)" labels. diff --git a/src/docs/user/userguide/phame.diviner b/src/docs/user/userguide/phame.diviner index d42ddc7d23..2889f5240a 100644 --- a/src/docs/user/userguide/phame.diviner +++ b/src/docs/user/userguide/phame.diviner @@ -7,14 +7,14 @@ Overview ======== Phame is a simple platform for writing blogs and blog posts. Content published -through Phame is integrated with other Phabricator applications (like Feed, +through Phame is integrated with other Phorge applications (like Feed, Herald and Dashboards). You can use Phame to write and publish posts on any topic. You might use it to make announcements, hold discussions, or provide progress updates about a project. -In the upstream, we use several Phame blogs to discuss changes to Phabricator, +In the upstream, we use several Phame blogs to discuss changes to Phorge, make company announcements, photograph food, and provide visionary thought leadership. @@ -61,7 +61,7 @@ for all users who can see it. Using Phame With Other Applications =================================== -Phame integrates with other Phabricator applications, so you can do a few +Phame integrates with other Phorge applications, so you can do a few interesting things: **Dashboards**: You can create a dashboard panel which shows posts on a @@ -90,7 +90,7 @@ Phacility corporate blog is an example of an external Phame blog: > https://blog.phacility.com/ External blogs are public (they do not require login) and are only supported if -your Phabricator install is also public. You can make an install public by +your Phorge install is also public. You can make an install public by adjusting `policy.allow-public` in Config, but make sure you understand the effects of adjusting this setting before touching it. @@ -100,7 +100,7 @@ like this: - **View Policy**: Set the "View Policy" for the blog to "Public". Blogs must have a public view policy to be served from an external domain. - **Full Domain URI**: Set this to the full URI of your external domain, - like `https://blog.mycompany.com/`. When users visit this URI, Phabricator + like `https://blog.mycompany.com/`. When users visit this URI, Phorge will serve the blog to them. To configure the blog's navigation breadcrumbs so that it links back to the @@ -115,7 +115,7 @@ users return to the blog's parent site. It will look something like this: - {nav My Company > Blog Name} -Finally, configure DNS for `blog.mycompany.com` to point at Phabricator. +Finally, configure DNS for `blog.mycompany.com` to point at Phorge. If everything is set up properly, visiting `blog.mycompany.com` should now serve your blog. diff --git a/src/docs/user/userguide/profile_menu.diviner b/src/docs/user/userguide/profile_menu.diviner index 88eb32d3ff..a7e48a6924 100644 --- a/src/docs/user/userguide/profile_menu.diviner +++ b/src/docs/user/userguide/profile_menu.diviner @@ -130,7 +130,7 @@ the menu for, but most objects support these items: - {icon map-marker} **Label**: Lets you label sections of menu items. This is also purely cosmetic. - {icon link} **Link**: Allows you to create an item which links to - somewhere else in Phabricator, or to an external site. + somewhere else in Phorge, or to an external site. - {icon plus} **Form**: Provides quick access to custom and built-in forms from any application that supports EditEngine. - {icon briefcase} **Projects**: Provides quick access to a project. diff --git a/src/docs/user/userguide/projects.diviner b/src/docs/user/userguide/projects.diviner index 4e3ab3616f..0d9dd2aaa2 100644 --- a/src/docs/user/userguide/projects.diviner +++ b/src/docs/user/userguide/projects.diviner @@ -8,7 +8,7 @@ Overview NOTE: This document is only partially complete. -Phabricator projects are flexible, general-purpose groups of objects that you +Phorge projects are flexible, general-purpose groups of objects that you can use to organize information. Projects have some basic information like a name and an icon, and may optionally have members. @@ -37,7 +37,7 @@ only members of the "Backend" project, nor does it allow them to see it if they otherwise could not. Likewise, removing projects does not affect visibility. If you're familiar with other software that works differently, this may be -unexpected, but the rule in Phabricator is simple: **adding and removing +unexpected, but the rule in Phorge is simple: **adding and removing projects never affects policies.** Note that you //can// write policy rules which restrict capabilities to members @@ -124,7 +124,7 @@ documentation to the menu to make it easy to find and access. You could also link to a Conpherence if you have a chatroom for a project. **Link to External Resources**: You can link to external resources outside -of Phabricator if you have other pages which are relevant to a project. +of Phorge if you have other pages which are relevant to a project. **Set Workboard as Default**: For projects that are mostly used to organize tasks, change the default item to the workboard instead of the profile to get @@ -278,13 +278,13 @@ As discussed above, adding and removing projects never affects who can see an object. This is an explicit product design choice aimed at reducing the complexity of policy management. -Phabricator projects are a flexible, general-purpose, freeform tool. This is a +Phorge projects are a flexible, general-purpose, freeform tool. This is a good match for many organizational use cases, but a very poor match for policies. It is important that policies be predictable and rigid, because the cost of making a mistake with policies is high (inadvertent disclosure of private information). -In Phabricator, each object (like a task) can be tagged with multiple projects. +In Phorge, each object (like a task) can be tagged with multiple projects. This is important in a flexible organizational tool, but is a liability in a policy tool. diff --git a/src/docs/user/userguide/prototypes.diviner b/src/docs/user/userguide/prototypes.diviner index c84bad1178..52ae25bf75 100644 --- a/src/docs/user/userguide/prototypes.diviner +++ b/src/docs/user/userguide/prototypes.diviner @@ -6,7 +6,7 @@ Information about prototypes. Overview ======== -Phabricator includes //prototype applications//, which are applications in an +Phorge includes //prototype applications//, which are applications in an early stage of development. When we begin working on a new application, we usually implement it as a diff --git a/src/docs/user/userguide/remarkup.diviner b/src/docs/user/userguide/remarkup.diviner index f96c8c7b25..4fb8fc75f8 100644 --- a/src/docs/user/userguide/remarkup.diviner +++ b/src/docs/user/userguide/remarkup.diviner @@ -6,7 +6,7 @@ arguments. = Overview = -Phabricator uses a lightweight markup language called "Remarkup", similar to +Phorge uses a lightweight markup language called "Remarkup", similar to other lightweight markup languages like Markdown and Wiki markup. This document describes how to format text using Remarkup. @@ -331,7 +331,7 @@ On an entirely different topic, ... = Linking URIs = -URIs are automatically linked: http://phabricator.org/ +URIs are automatically linked: http://phorge.it/ If you have a URI with problematic characters in it, like "`http://comma.org/,`", you can surround it with angle brackets: @@ -341,7 +341,7 @@ If you have a URI with problematic characters in it, like This will force the parser to consume the whole URI: You can also use create named links, where you choose the displayed text. These -work within Phabricator or on the internet at large: +work within Phorge or on the internet at large: [[/herald/transcript/ | Herald Transcripts]] [[http://www.boring-legal-documents.com/ | exciting legal documents]] @@ -352,7 +352,7 @@ Markdown-style links are also supported: = Linking to Objects = -You can link to Phabricator objects, such as Differential revisions, Diffusion +You can link to Phorge objects, such as Differential revisions, Diffusion commits and Maniphest tasks, by mentioning the name of an object: D123 # Link to Differential revision D123 @@ -366,7 +366,7 @@ can be found on the date stamp of any transaction/comment): T123#412 # Link to comment id #412 of task T123 -See the Phabricator configuration setting `remarkup.ignored-object-names` to +See the Phorge configuration setting `remarkup.ignored-object-names` to modify this behavior. = Embedding Objects @@ -501,7 +501,7 @@ following: By default, the font used to create the text for the meme is `tuffy.ttf`. For the more authentic feel of `impact.ttf`, you simply have to place the Impact -TrueType font in the Phabricator subfolder `/resources/font/`. If Remarkup +TrueType font in the Phorge subfolder `/resources/font/`. If Remarkup detects the presence of `impact.ttf`, it will automatically use it. = Mentioning Users = diff --git a/src/docs/user/userguide/reviews_vs_audit.diviner b/src/docs/user/userguide/reviews_vs_audit.diviner index 642683840c..bf6afb64b4 100644 --- a/src/docs/user/userguide/reviews_vs_audit.diviner +++ b/src/docs/user/userguide/reviews_vs_audit.diviner @@ -6,7 +6,7 @@ Discusses the differences between "review" and "audit" workflows. Overview ======== -Phabricator supports two similar but separate code review workflows: "review" +Phorge supports two similar but separate code review workflows: "review" and "audit". Review occurs in **Differential**, before changes are published. You can learn @@ -111,7 +111,7 @@ supplement review and is better than nothing on its own. If you are unpersuaded by the arguments above (or work on a team that is unswayed), audits provide some of the benefits of review with less friction: - - Audits are driven entirely by Phabricator: users do not need to install + - Audits are driven entirely by Phorge: users do not need to install `arc`. - Audits require little adjustment to existing workflows and little training. - Audits are completely nonblocking, and send fewer notifications than review. diff --git a/src/docs/user/userguide/search.diviner b/src/docs/user/userguide/search.diviner index 9276298f31..b10e4c147c 100644 --- a/src/docs/user/userguide/search.diviner +++ b/src/docs/user/userguide/search.diviner @@ -1,12 +1,12 @@ @title Search User Guide @group userguide -Introduction to searching for documents in Phabricator. +Introduction to searching for documents in Phorge. Overview ======== -Phabricator has two major ways to search for documents and objects (like tasks, +Phorge has two major ways to search for documents and objects (like tasks, code reviews, users, wiki documents, and so on): **global search** and **application search**. @@ -34,14 +34,14 @@ By default, global search queries search all document types: for example, they will find matching tasks, commits, wiki documents, users, etc. You can use the dropdown to the left of the search box to select a different search scope. -If you choose the **Current Application** scope, Phabricator will search for +If you choose the **Current Application** scope, Phorge will search for open documents in the current application. For example, if you're in Maniphest and run a search, you'll get matching tasks. If you're in Phriction and run a search, you'll get matching wiki documents. Some pages (like the 404 page) don't belong to an application, or belong to an application which doesn't have any searchable documents. In these cases, -Phabricator will search all documents. +Phorge will search all documents. To quickly **jump to an object** like a task, enter the object's ID in the global search box and search for it. For example, you can enter `T123` or @@ -53,7 +53,7 @@ After running a search, you can scroll up to add filters and refine the result set. You can also select **Advanced Search** from the dropdown menu to jump here immediately, or press return in the search box without entering a query. -This interface supports standard Phabricator search and filtering features, +This interface supports standard Phorge search and filtering features, like **saved queries** and **typeaheads**. See below for more details on using these features. diff --git a/src/docs/user/userguide/spaces.diviner b/src/docs/user/userguide/spaces.diviner index 4a748fb25b..a2809cbe3e 100644 --- a/src/docs/user/userguide/spaces.diviner +++ b/src/docs/user/userguide/spaces.diviner @@ -21,7 +21,7 @@ share the same access policy. For example: enemies at the company, that she might use the element of surprise to later expand her domain. -Phabricator's access control policies are generally powerful enough to handle +Phorge's access control policies are generally powerful enough to handle these use cases on their own, but applying the same policy to a large group of objects requires a lot of effort and is error-prone. @@ -138,7 +138,7 @@ Limitations and Caveats Some information is shared between spaces, so they do not completely isolate users from other activity on the install. This section discusses limitations of the isolation model. Most of these limitations are intrinsic to the policy -model Phabricator uses. +model Phorge uses. **Shared IDs**: Spaces do not have unique object IDs: there is only one `T1`, not a separate one in each space. It can be moved between spaces, but `T1` diff --git a/src/docs/user/userguide/tone.diviner b/src/docs/user/userguide/tone.diviner index 553fe2851b..6398917d2c 100644 --- a/src/docs/user/userguide/tone.diviner +++ b/src/docs/user/userguide/tone.diviner @@ -1,12 +1,12 @@ @title User Guide: Project Tone @group userguide -Explains why Phabricator uses a lighthearted tone. +Explains why Phorge uses a lighthearted tone. Overview ======== -Phabricator uses a lighthearted tone in documentation and some interfaces, and +Phorge uses a lighthearted tone in documentation and some interfaces, and includes some features which primarily exist to add flavor or make things sillier. @@ -28,7 +28,7 @@ setting in the {nav Config} application. documentation.) If you don't like a piece of flavor because it creates legitimate difficulty or -confusion for you or your users and makes it harder to use Phabricator, let us +confusion for you or your users and makes it harder to use Phorge, let us know. We don't intend flavor or tone to get in the way of usability, and can sometimes take a joke too far (particularly for users who don't speak English natively). diff --git a/src/docs/user/userguide/unlocking.diviner b/src/docs/user/userguide/unlocking.diviner index 456655a393..cd1157a21d 100644 --- a/src/docs/user/userguide/unlocking.diviner +++ b/src/docs/user/userguide/unlocking.diviner @@ -6,7 +6,7 @@ Explains how to access locked or invisible objects and accounts. Overview ======== -Phabricator tries to make it difficult for users to lock themselves out of +Phorge tries to make it difficult for users to lock themselves out of things, but you can occasionally end up in situations where no one has access to an object that you need access to. diff --git a/src/docs/user/userguide/users.diviner b/src/docs/user/userguide/users.diviner index d66f8080d3..5e15f2f2af 100644 --- a/src/docs/user/userguide/users.diviner +++ b/src/docs/user/userguide/users.diviner @@ -18,7 +18,7 @@ Administrators **Administrators** are normal users with a few extra capabilities. Their primary role is to keep things running smoothly, and they are not all-powerful. -In Phabricator, administrators are more like //janitors//. +In Phorge, administrators are more like //janitors//. Administrators can create, delete, enable, disable, and approve user accounts. Various applications have a few other capabilities which are reserved for diff --git a/src/docs/user/userguide/utf8.diviner b/src/docs/user/userguide/utf8.diviner index b6742f0c36..0e6727197d 100644 --- a/src/docs/user/userguide/utf8.diviner +++ b/src/docs/user/userguide/utf8.diviner @@ -1,11 +1,11 @@ @title User Guide: UTF-8 and Character Encoding @group userguide -How Phabricator handles character encodings. +How Phorge handles character encodings. = Overview = -Phabricator stores all internal text data as UTF-8, processes all text data +Phorge stores all internal text data as UTF-8, processes all text data as UTF-8, outputs in UTF-8, and expects all inputs to be UTF-8. Principally, this means that you should write your source code in UTF-8. In most cases this does not require you to change anything, because ASCII text is a subset of @@ -17,14 +17,14 @@ options: - Convert all files in the repository to ASCII or UTF-8 (see "Detecting and Repairing Files" below). This is recommended, especially if the encoding problems are accidental. - - Configure Phabricator to convert files into UTF-8 from whatever encoding + - Configure Phorge to convert files into UTF-8 from whatever encoding your repository is in when it needs to (see "Support for Alternate Encodings" below). This is not completely supported, and repositories with files that have multiple encodings are not supported. = Support for Alternate Encodings = -Phabricator has some support for encodings other than UTF-8. +Phorge has some support for encodings other than UTF-8. NOTE: Alternate encodings are not completely supported, and a few features will not work correctly. Codebases with files that have multiple different encodings diff --git a/src/docs/user/userguide/webhooks.diviner b/src/docs/user/userguide/webhooks.diviner index 10d1f36da0..c2d0678b26 100644 --- a/src/docs/user/userguide/webhooks.diviner +++ b/src/docs/user/userguide/webhooks.diviner @@ -7,7 +7,7 @@ Guide to configuring webhooks. Overview ======== -If you'd like to react to events in Phabricator or publish them into external +If you'd like to react to events in Phorge or publish them into external systems, you can configure webhooks. Configure webhooks in {nav Herald > Webhooks}. Users must have the @@ -35,7 +35,7 @@ You can also use the command-line tool, which supports a few additional options: ``` -phabricator/ $ ./bin/webhook call --id 42 --object D123 +phorge/ $ ./bin/webhook call --id 42 --object D123 ``` @@ -43,12 +43,12 @@ Verifying Requests ================== When your webhook callback URI receives a request, it didn't necessarily come -from Phabricator. An attacker or mischievous user can normally call your hook +from Phorge. An attacker or mischievous user can normally call your hook directly and pretend to be notifying you of an event. To verify that the request is authentic, first retrieve the webhook key from the web UI with {nav View HMAC Key}. This is a shared secret which will let you -verify that Phabricator originated a request. +verify that Phorge originated a request. When you receive a request, compute the SHA256 HMAC value of the request body using the HMAC key as the key. The value should match the value in the @@ -124,7 +124,7 @@ The **action** map has metadata about the action: - `test` This was a test call from the web UI or console. - `silent` This is a silent edit which won't send mail or notifications in - Phabricator. If your hook is doing something like copying events into + Phorge. If your hook is doing something like copying events into a chatroom, it may want to respect this flag. - `secure` Details about this object should only be transmitted over secure channels. Your hook may want to respect this flag. @@ -198,7 +198,7 @@ to some kind of worker queue if you expect to routinely require more than 10 seconds to respond to requests. Hook callbacks are single-threaded: you will never receive more than one -simultaneous call to the same webhook from Phabricator. If you have a firehose +simultaneous call to the same webhook from Phorge. If you have a firehose hook on an active install, it may be important to respond to requests quickly to avoid accumulating a backlog. From af36da47417c1b90337a38caeb62c399f6f45a81 Mon Sep 17 00:00:00 2001 From: Matthew Bowker Date: Sun, 20 Jun 2021 22:46:02 -0600 Subject: [PATCH 004/425] Rename example sshd files Test Plan: Looked at new files, made sure the only changes were to rename the files in line with the documentation Reviewers: O1 Blessed Committers, eax Reviewed By: O1 Blessed Committers, eax Subscribers: speck, tobiaswiese Maniphest Tasks: T15017 Differential Revision: https://we.phorge.it/D25010 --- .../sshd/{phabricator-ssh-hook.sh => phorge-ssh-hook.sh} | 4 ++-- ..._config.phabricator.example => sshd_config.phorge.example} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename resources/sshd/{phabricator-ssh-hook.sh => phorge-ssh-hook.sh} (66%) rename resources/sshd/{sshd_config.phabricator.example => sshd_config.phorge.example} (84%) diff --git a/resources/sshd/phabricator-ssh-hook.sh b/resources/sshd/phorge-ssh-hook.sh similarity index 66% rename from resources/sshd/phabricator-ssh-hook.sh rename to resources/sshd/phorge-ssh-hook.sh index fc7de81dad..4e5843986e 100755 --- a/resources/sshd/phabricator-ssh-hook.sh +++ b/resources/sshd/phorge-ssh-hook.sh @@ -3,8 +3,8 @@ # NOTE: Replace this with the username that you expect users to connect with. VCSUSER="vcs-user" -# NOTE: Replace this with the path to your Phabricator directory. -ROOT="/path/to/phabricator" +# NOTE: Replace this with the path to your Phorge directory. +ROOT="/path/to/phorge" if [ "$1" != "$VCSUSER" ]; then diff --git a/resources/sshd/sshd_config.phabricator.example b/resources/sshd/sshd_config.phorge.example similarity index 84% rename from resources/sshd/sshd_config.phabricator.example rename to resources/sshd/sshd_config.phorge.example index 506d32bbbf..34c0d14d33 100644 --- a/resources/sshd/sshd_config.phabricator.example +++ b/resources/sshd/sshd_config.phorge.example @@ -3,7 +3,7 @@ # NOTE: Edit these to the correct values for your setup. -AuthorizedKeysCommand /usr/libexec/phabricator-ssh-hook.sh +AuthorizedKeysCommand /usr/libexec/phorge-ssh-hook.sh AuthorizedKeysCommandUser vcs-user AllowUsers vcs-user @@ -21,4 +21,4 @@ PasswordAuthentication no ChallengeResponseAuthentication no AuthorizedKeysFile none -PidFile /var/run/sshd-phabricator.pid +PidFile /var/run/sshd-phorge.pid From 2258ba8535d57081582add7375c31837ac2f26f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20S=C3=A9chet?= Date: Sat, 19 Jun 2021 17:11:01 +0000 Subject: [PATCH 005/425] Add package.json for aphlict Summary: This makes the whole setup easier, future proof and reproducible. Test Plan: cd support/aphlict/server/ npm install See that ws gets installed as expected. Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: Matthew, Ekubischta, speck, tobiaswiese Maniphest Tasks: T15019 Differential Revision: https://we.phorge.it/D25006 --- src/docs/user/configuration/notifications.diviner | 6 +++--- support/aphlict/server/package-lock.json | 12 ++++++++++++ support/aphlict/server/package.json | 12 ++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 support/aphlict/server/package-lock.json create mode 100644 support/aphlict/server/package.json diff --git a/src/docs/user/configuration/notifications.diviner b/src/docs/user/configuration/notifications.diviner index bd1835c294..de1a32d5e1 100644 --- a/src/docs/user/configuration/notifications.diviner +++ b/src/docs/user/configuration/notifications.diviner @@ -41,13 +41,13 @@ The notification server uses Node.js, so you'll need to install it first. To install Node.js, follow the instructions on [[ http://nodejs.org | nodejs.org ]]. -You will also need to install the `ws` module for Node. This needs to be +You will also need to install the dependencies for Node. This needs to be installed into the notification server directory: phorge/ $ cd support/aphlict/server/ - phorge/support/aphlict/server/ $ npm install ws + phorge/support/aphlict/server/ $ npm install -Once Node.js and the `ws` module are installed, you're ready to start the +Once Node.js and its dependencies are installed, you're ready to start the server. diff --git a/support/aphlict/server/package-lock.json b/support/aphlict/server/package-lock.json new file mode 100644 index 0000000000..8af62ae233 --- /dev/null +++ b/support/aphlict/server/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "aphlict-server", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "ws": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", + "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==" + } + } +} diff --git a/support/aphlict/server/package.json b/support/aphlict/server/package.json new file mode 100644 index 0000000000..b66270b3c5 --- /dev/null +++ b/support/aphlict/server/package.json @@ -0,0 +1,12 @@ +{ + "name": "aphlict-server", + "description": "Phorge's aphlict's server", + "main": "aphlict_server.js", + "directories": { + "lib": "lib" + }, + "license": "Apache-2.0", + "dependencies": { + "ws": "^7.5.0" + } +} From e7740c8669b4dc4dc15f0bfd4fa333ce5fb99c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20S=C3=A9chet?= Date: Sat, 19 Jun 2021 13:09:54 +0000 Subject: [PATCH 006/425] Add HarbormasterHookController as an entry point for all Harbormaster hooks Summary: This makes the set of hooks easily extensible, as a first step toward integrating more 3rd party CI in phorge. Test Plan: Send requests to `/harbormaster/hook/circleci/` and `/harbormaster/hook/buildkite/` and check they run the proper handler. Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: Matthew, speck, tobiaswiese Maniphest Tasks: T15018 Differential Revision: https://we.phorge.it/D25005 --- src/__phutil_library_map__.php | 16 ++++++----- .../PhabricatorHarbormasterApplication.php | 5 +--- .../controller/HarbormasterHookController.php | 21 +++++++++++++++ .../integration/HarbormasterHookHandler.php | 27 +++++++++++++++++++ ...masterBuildkiteBuildStepImplementation.php | 0 .../HarbormasterBuildkiteHookHandler.php} | 8 +++--- ...rmasterCircleCIBuildStepImplementation.php | 0 .../HarbormasterCircleCIHookHandler.php} | 8 +++--- 8 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 src/applications/harbormaster/controller/HarbormasterHookController.php create mode 100644 src/applications/harbormaster/integration/HarbormasterHookHandler.php rename src/applications/harbormaster/{step => integration/buildkite}/HarbormasterBuildkiteBuildStepImplementation.php (100%) rename src/applications/harbormaster/{controller/HarbormasterBuildkiteHookController.php => integration/buildkite/HarbormasterBuildkiteHookHandler.php} (95%) rename src/applications/harbormaster/{step => integration/circleci}/HarbormasterCircleCIBuildStepImplementation.php (100%) rename src/applications/harbormaster/{controller/HarbormasterCircleCIHookController.php => integration/circleci/HarbormasterCircleCIHookHandler.php} (94%) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 3eedd1605a..0b4fa88c48 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1478,13 +1478,13 @@ 'HarbormasterBuildableTransactionEditor' => 'applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php', 'HarbormasterBuildableTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php', 'HarbormasterBuildableViewController' => 'applications/harbormaster/controller/HarbormasterBuildableViewController.php', - 'HarbormasterBuildkiteBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterBuildkiteBuildStepImplementation.php', + 'HarbormasterBuildkiteBuildStepImplementation' => 'applications/harbormaster/integration/buildkite/HarbormasterBuildkiteBuildStepImplementation.php', 'HarbormasterBuildkiteBuildableInterface' => 'applications/harbormaster/interface/HarbormasterBuildkiteBuildableInterface.php', - 'HarbormasterBuildkiteHookController' => 'applications/harbormaster/controller/HarbormasterBuildkiteHookController.php', + 'HarbormasterBuildkiteHookHandler' => 'applications/harbormaster/integration/buildkite/HarbormasterBuildkiteHookHandler.php', 'HarbormasterBuiltinBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterBuiltinBuildStepGroup.php', - 'HarbormasterCircleCIBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterCircleCIBuildStepImplementation.php', + 'HarbormasterCircleCIBuildStepImplementation' => 'applications/harbormaster/integration/circleci/HarbormasterCircleCIBuildStepImplementation.php', 'HarbormasterCircleCIBuildableInterface' => 'applications/harbormaster/interface/HarbormasterCircleCIBuildableInterface.php', - 'HarbormasterCircleCIHookController' => 'applications/harbormaster/controller/HarbormasterCircleCIHookController.php', + 'HarbormasterCircleCIHookHandler' => 'applications/harbormaster/integration/circleci/HarbormasterCircleCIHookHandler.php', 'HarbormasterConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php', 'HarbormasterControlBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterControlBuildStepGroup.php', 'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php', @@ -1498,6 +1498,8 @@ 'HarbormasterExternalBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterExternalBuildStepGroup.php', 'HarbormasterFileArtifact' => 'applications/harbormaster/artifact/HarbormasterFileArtifact.php', 'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php', + 'HarbormasterHookController' => 'applications/harbormaster/controller/HarbormasterHookController.php', + 'HarbormasterHookHandler' => 'applications/harbormaster/integration/HarbormasterHookHandler.php', 'HarbormasterHostArtifact' => 'applications/harbormaster/artifact/HarbormasterHostArtifact.php', 'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php', 'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php', @@ -7727,10 +7729,10 @@ 'HarbormasterBuildableTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'HarbormasterBuildableViewController' => 'HarbormasterController', 'HarbormasterBuildkiteBuildStepImplementation' => 'HarbormasterBuildStepImplementation', - 'HarbormasterBuildkiteHookController' => 'HarbormasterController', + 'HarbormasterBuildkiteHookHandler' => 'HarbormasterHookHandler', 'HarbormasterBuiltinBuildStepGroup' => 'HarbormasterBuildStepGroup', 'HarbormasterCircleCIBuildStepImplementation' => 'HarbormasterBuildStepImplementation', - 'HarbormasterCircleCIHookController' => 'HarbormasterController', + 'HarbormasterCircleCIHookHandler' => 'HarbormasterHookHandler', 'HarbormasterConduitAPIMethod' => 'ConduitAPIMethod', 'HarbormasterControlBuildStepGroup' => 'HarbormasterBuildStepGroup', 'HarbormasterController' => 'PhabricatorController', @@ -7744,6 +7746,8 @@ 'HarbormasterExternalBuildStepGroup' => 'HarbormasterBuildStepGroup', 'HarbormasterFileArtifact' => 'HarbormasterArtifact', 'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation', + 'HarbormasterHookController' => 'HarbormasterController', + 'HarbormasterHookHandler' => 'Phobject', 'HarbormasterHostArtifact' => 'HarbormasterDrydockLeaseArtifact', 'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterLintMessagesController' => 'HarbormasterController', diff --git a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php index 57f04e550b..6c499bf63a 100644 --- a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php +++ b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php @@ -94,10 +94,7 @@ public function getRoutes() { 'lint/' => array( '(?P\d+)/' => 'HarbormasterLintMessagesController', ), - 'hook/' => array( - 'circleci/' => 'HarbormasterCircleCIHookController', - 'buildkite/' => 'HarbormasterBuildkiteHookController', - ), + 'hook/(?P[^/]+)/' => 'HarbormasterHookController', 'log/' => array( 'view/(?P\d+)/(?:\$(?P\d+(?:-\d+)?))?' => 'HarbormasterBuildLogViewController', diff --git a/src/applications/harbormaster/controller/HarbormasterHookController.php b/src/applications/harbormaster/controller/HarbormasterHookController.php new file mode 100644 index 0000000000..8eb442bc41 --- /dev/null +++ b/src/applications/harbormaster/controller/HarbormasterHookController.php @@ -0,0 +1,21 @@ +getURIData('handler'); + $handler = HarbormasterHookHandler::getHandler($name); + + if (!$handler) { + throw new Exception(pht('No handler found for %s', $name)); + } + + return $handler->handleRequest($request); + } + +} diff --git a/src/applications/harbormaster/integration/HarbormasterHookHandler.php b/src/applications/harbormaster/integration/HarbormasterHookHandler.php new file mode 100644 index 0000000000..5b4aafa612 --- /dev/null +++ b/src/applications/harbormaster/integration/HarbormasterHookHandler.php @@ -0,0 +1,27 @@ +setAncestorClass(__CLASS__) + ->setUniqueMethod('getName') + ->execute(); + } + + public static function getHandler($handler) { + $base = idx(self::getHandlers(), $handler); + + if ($base) { + return (clone $base); + } + + return null; + } + + abstract public function getName(); + + abstract public function handleRequest(AphrontRequest $request); + +} diff --git a/src/applications/harbormaster/step/HarbormasterBuildkiteBuildStepImplementation.php b/src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteBuildStepImplementation.php similarity index 100% rename from src/applications/harbormaster/step/HarbormasterBuildkiteBuildStepImplementation.php rename to src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteBuildStepImplementation.php diff --git a/src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php b/src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteHookHandler.php similarity index 95% rename from src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php rename to src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteHookHandler.php index df50ea9126..2a29ceffa0 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php +++ b/src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteHookHandler.php @@ -1,10 +1,10 @@ Date: Sat, 26 Jun 2021 15:44:56 +0000 Subject: [PATCH 007/425] Updated .arclint in Phorge to exclude package-lock.json from Aphlict setup Summary: Aphlict requires a machine generated `package-lock.json` that can trigger linters unnecessarily. This revision excludes it from all linters Test Plan: **Testing** ```name="Fake Change to set off the linter"" diff --git a/support/aphlict/server/package-lock.json b/support/aphlict/server/package-lock.json index 8af62ae233..a417725182 100644 --- a/support/aphlict/server/package-lock.json +++ b/support/aphlict/server/package-lock.json @@ -2,11 +2,12 @@ "name": "aphlict-server", "requires": true, "lockfileVersion": 1, + "testing" : true, "dependencies": { "ws": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", - "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==" + "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfi4vw==" } } } ``` ```name="arc lint results" root@63c842bff39e:/srv/phorge/phorge# arc lint >>> Lint for support/aphlict/server/package-lock.json: Warning (TXT3) Line Too Long This line is 156 characters long, but the convention is 80 characters. 7 "ws": { 8 "version": "7.5.0", 9 "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", >>> 10 "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfi4vw==" 11 } 12 } 13 } root@63c842bff39e:/srv/phorge/phorge# ``` Updated change ```name="Change to .arclint" diff --git a/.arclint b/.arclint index f215d93fdc..ca89c601e4 100644 --- a/.arclint +++ b/.arclint @@ -2,7 +2,8 @@ "exclude": [ "(^externals/)", "(^webroot/rsrc/externals/(?!javelin/))", - "(/__tests__/data/)" + "(/__tests__/data/)", + "(^support/aphlict/server/package-lock.json)" ], "linters": { "chmod": { ``` ```name="arc lint results" root@63c842bff39e:/srv/phorge/phorge# arc lint OKAY No lint messages. root@63c842bff39e:/srv/phorge/phorge# ``` Reviewers: O1 Blessed Committers, deadalnix, eax, speck Reviewed By: O1 Blessed Committers, deadalnix, eax, speck Subscribers: speck, tobiaswiese Maniphest Tasks: T15021 Differential Revision: https://we.phorge.it/D25012 --- .arclint | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.arclint b/.arclint index f215d93fdc..ca89c601e4 100644 --- a/.arclint +++ b/.arclint @@ -2,7 +2,8 @@ "exclude": [ "(^externals/)", "(^webroot/rsrc/externals/(?!javelin/))", - "(/__tests__/data/)" + "(/__tests__/data/)", + "(^support/aphlict/server/package-lock.json)" ], "linters": { "chmod": { From a4948ec800e9e385ecb519d86b40c5f13719fb53 Mon Sep 17 00:00:00 2001 From: James Brown Date: Sat, 4 Sep 2021 13:51:06 -0400 Subject: [PATCH 008/425] Add conduit endpoints for querying legalpad Summary: This diff adds conduit methods for searching for legalpad documents and signatures. This is very helpful for auditing who's actually signed a document. It also fixes the "contributorPHIDs" constraint in the existing search engine. In order to expose legalpad signatures through Conduit, this adds a `phid` column to the `legalpad_documentsignature` table. It includes a migration (in the style of many previous phid-adding migrations) to actually populate the column. Test Plan: We run this on my company's internal fork and it seems to work okay. I don't think any other conduit methods anywhere have tests (???), but if you can point me at one I'm glad to write a unit test! Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: 20after4, speck, tobiaswiese Differential Revision: https://we.phorge.it/D25018 --- ...02.legalpad_document_signature.01.phid.sql | 2 + ...ad_document_signature.02.phid-populate.php | 79 ++++++++++++++++++ src/__phutil_library_map__.php | 12 +++ ...LegalpadDocumentSearchConduitAPIMethod.php | 18 ++++ ...egalpadSignatureSearchConduitAPIMethod.php | 18 ++++ ...atorLegalpadBodySearchEngineAttachment.php | 25 ++++++ ...galpadSignaturesSearchEngineAttachment.php | 38 +++++++++ ...catorLegalpadDocumentSignaturePHIDType.php | 47 +++++++++++ .../query/LegalpadDocumentSearchEngine.php | 2 +- .../query/LegalpadDocumentSignatureQuery.php | 31 ++++--- .../LegalpadDocumentSignatureSearchEngine.php | 69 +++++++++++++++ .../legalpad/storage/LegalpadDocument.php | 49 ++++++++++- .../storage/LegalpadDocumentSignature.php | 83 ++++++++++++++++++- 13 files changed, 458 insertions(+), 15 deletions(-) create mode 100644 resources/sql/autopatches/20210802.legalpad_document_signature.01.phid.sql create mode 100644 resources/sql/autopatches/20210802.legalpad_document_signature.02.phid-populate.php create mode 100644 src/applications/legalpad/conduit/LegalpadDocumentSearchConduitAPIMethod.php create mode 100644 src/applications/legalpad/conduit/LegalpadSignatureSearchConduitAPIMethod.php create mode 100644 src/applications/legalpad/engineextension/PhabricatorLegalpadBodySearchEngineAttachment.php create mode 100644 src/applications/legalpad/engineextension/PhabricatorLegalpadSignaturesSearchEngineAttachment.php create mode 100644 src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php diff --git a/resources/sql/autopatches/20210802.legalpad_document_signature.01.phid.sql b/resources/sql/autopatches/20210802.legalpad_document_signature.01.phid.sql new file mode 100644 index 0000000000..92e50cce98 --- /dev/null +++ b/resources/sql/autopatches/20210802.legalpad_document_signature.01.phid.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature + ADD phid VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20210802.legalpad_document_signature.02.phid-populate.php b/resources/sql/autopatches/20210802.legalpad_document_signature.02.phid-populate.php new file mode 100644 index 0000000000..1a2ce50293 --- /dev/null +++ b/resources/sql/autopatches/20210802.legalpad_document_signature.02.phid-populate.php @@ -0,0 +1,79 @@ +establishConnection('w'); +$table_name = $docsig_table->getTableName(); + +$chunk_size = 4096; + +$temporary_table = 'tmp_20210802_docsig_id_map'; + +try { + queryfx( + $conn, + 'CREATE TEMPORARY TABLE %T ( + docsig_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + docsig_phid VARBINARY(64) NOT NULL)', + $temporary_table); +} catch (AphrontAccessDeniedQueryException $ex) { + throw new PhutilProxyException( + pht( + 'Failed to "CREATE TEMPORARY TABLE". You may need to "GRANT" the '. + 'current MySQL user this permission.'), + $ex); +} + +$table_iterator = id(new LiskRawMigrationIterator($conn, $table_name)) + ->setPageSize($chunk_size); + +$chunk_iterator = new PhutilChunkedIterator($table_iterator, $chunk_size); +foreach ($chunk_iterator as $chunk) { + + $map = array(); + foreach ($chunk as $docsig_row) { + $phid = $docsig_row['phid']; + + if (strlen($phid)) { + continue; + } + + $phid = PhabricatorPHID::generateNewPHID($phid_type); + $id = $docsig_row['id']; + + $map[(int)$id] = $phid; + } + + if (!$map) { + continue; + } + + $sql = array(); + foreach ($map as $docsig_id => $docsig_phid) { + $sql[] = qsprintf( + $conn, + '(%d, %s)', + $docsig_id, + $docsig_phid); + } + + queryfx( + $conn, + 'TRUNCATE TABLE %T', + $temporary_table); + + queryfx( + $conn, + 'INSERT INTO %T (docsig_id, docsig_phid) VALUES %LQ', + $temporary_table, + $sql); + + queryfx( + $conn, + 'UPDATE %T c JOIN %T x ON c.id = x.docsig_id + SET c.phid = x.docsig_phid', + $table_name, + $temporary_table); +} diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 0b4fa88c48..c92b9b9f77 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1723,6 +1723,7 @@ 'LegalpadDocumentQuery' => 'applications/legalpad/query/LegalpadDocumentQuery.php', 'LegalpadDocumentRemarkupRule' => 'applications/legalpad/remarkup/LegalpadDocumentRemarkupRule.php', 'LegalpadDocumentRequireSignatureTransaction' => 'applications/legalpad/xaction/LegalpadDocumentRequireSignatureTransaction.php', + 'LegalpadDocumentSearchConduitAPIMethod' => 'applications/legalpad/conduit/LegalpadDocumentSearchConduitAPIMethod.php', 'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php', 'LegalpadDocumentSignController' => 'applications/legalpad/controller/LegalpadDocumentSignController.php', 'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php', @@ -1742,6 +1743,7 @@ 'LegalpadRequireSignatureHeraldAction' => 'applications/legalpad/herald/LegalpadRequireSignatureHeraldAction.php', 'LegalpadSchemaSpec' => 'applications/legalpad/storage/LegalpadSchemaSpec.php', 'LegalpadSignatureNeededByObjectEdgeType' => 'applications/legalpad/edge/LegalpadSignatureNeededByObjectEdgeType.php', + 'LegalpadSignatureSearchConduitAPIMethod' => 'applications/legalpad/conduit/LegalpadSignatureSearchConduitAPIMethod.php', 'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php', 'LegalpadTransactionComment' => 'applications/legalpad/storage/LegalpadTransactionComment.php', 'LegalpadTransactionQuery' => 'applications/legalpad/query/LegalpadTransactionQuery.php', @@ -3659,8 +3661,11 @@ 'PhabricatorLabelProfileMenuItem' => 'applications/search/menuitem/PhabricatorLabelProfileMenuItem.php', 'PhabricatorLanguageSettingsPanel' => 'applications/settings/panel/PhabricatorLanguageSettingsPanel.php', 'PhabricatorLegalpadApplication' => 'applications/legalpad/application/PhabricatorLegalpadApplication.php', + 'PhabricatorLegalpadBodySearchEngineAttachment' => 'applications/legalpad/engineextension/PhabricatorLegalpadBodySearchEngineAttachment.php', 'PhabricatorLegalpadDocumentPHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php', + 'PhabricatorLegalpadDocumentSignaturePHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php', 'PhabricatorLegalpadSignaturePolicyRule' => 'applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php', + 'PhabricatorLegalpadSignaturesSearchEngineAttachment' => 'applications/legalpad/engineextension/PhabricatorLegalpadSignaturesSearchEngineAttachment.php', 'PhabricatorLibraryTestCase' => '__tests__/PhabricatorLibraryTestCase.php', 'PhabricatorLinkProfileMenuItem' => 'applications/search/menuitem/PhabricatorLinkProfileMenuItem.php', 'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php', @@ -7992,6 +7997,7 @@ 'PhabricatorSubscribableInterface', 'PhabricatorApplicationTransactionInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorConduitResultInterface', ), 'LegalpadDocumentBody' => array( 'LegalpadDAO', @@ -8008,11 +8014,13 @@ 'LegalpadDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'LegalpadDocumentRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'LegalpadDocumentRequireSignatureTransaction' => 'LegalpadDocumentTransactionType', + 'LegalpadDocumentSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'LegalpadDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine', 'LegalpadDocumentSignController' => 'LegalpadController', 'LegalpadDocumentSignature' => array( 'LegalpadDAO', 'PhabricatorPolicyInterface', + 'PhabricatorConduitResultInterface', ), 'LegalpadDocumentSignatureAddController' => 'LegalpadController', 'LegalpadDocumentSignatureListController' => 'LegalpadController', @@ -8030,6 +8038,7 @@ 'LegalpadRequireSignatureHeraldAction' => 'HeraldAction', 'LegalpadSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'LegalpadSignatureNeededByObjectEdgeType' => 'PhabricatorEdgeType', + 'LegalpadSignatureSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'LegalpadTransaction' => 'PhabricatorModularTransaction', 'LegalpadTransactionComment' => 'PhabricatorApplicationTransactionComment', 'LegalpadTransactionQuery' => 'PhabricatorApplicationTransactionQuery', @@ -10233,8 +10242,11 @@ 'PhabricatorLabelProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorLanguageSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorLegalpadApplication' => 'PhabricatorApplication', + 'PhabricatorLegalpadBodySearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorLegalpadDocumentPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorLegalpadDocumentSignaturePHIDType' => 'PhabricatorPHIDType', 'PhabricatorLegalpadSignaturePolicyRule' => 'PhabricatorPolicyRule', + 'PhabricatorLegalpadSignaturesSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorLibraryTestCase' => 'PhutilLibraryTestCase', 'PhabricatorLinkProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorLipsumArtist' => 'Phobject', diff --git a/src/applications/legalpad/conduit/LegalpadDocumentSearchConduitAPIMethod.php b/src/applications/legalpad/conduit/LegalpadDocumentSearchConduitAPIMethod.php new file mode 100644 index 0000000000..bb94c48536 --- /dev/null +++ b/src/applications/legalpad/conduit/LegalpadDocumentSearchConduitAPIMethod.php @@ -0,0 +1,18 @@ +needDocumentBodies(true); + } + + public function getAttachmentForObject($object, $data, $spec) { + return array( + 'body' => $object->getDocumentBody()->getText(), + 'preamble' => $object->getPreamble(), + ); + } + +} diff --git a/src/applications/legalpad/engineextension/PhabricatorLegalpadSignaturesSearchEngineAttachment.php b/src/applications/legalpad/engineextension/PhabricatorLegalpadSignaturesSearchEngineAttachment.php new file mode 100644 index 0000000000..9904951c68 --- /dev/null +++ b/src/applications/legalpad/engineextension/PhabricatorLegalpadSignaturesSearchEngineAttachment.php @@ -0,0 +1,38 @@ +needSignatures(true); + } + + public function getAttachmentForObject($object, $data, $spec) { + $signatures = array(); + foreach ($object->getSignatures() as $signature) { + $signatures[] = array( + 'phid' => $signature->getPHID(), + 'signerPHID' => $signature->getSignerPHID(), + 'exemptionPHID' => $signature->getExemptionPHID(), + 'isExemption' => $signature->getIsExemption(), + 'signerName' => $signature->getSignerName(), + 'signerEmail' => $signature->getSignerEmail(), + 'documentVersion' => $signature->getDocumentVersion(), + 'dateCreated' => (int)$signature->getDateCreated(), + ); + } + + return array( + 'signatures' => $signatures, + ); + } + +} diff --git a/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php new file mode 100644 index 0000000000..782dbf13aa --- /dev/null +++ b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php @@ -0,0 +1,47 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $sig = $objects[$phid]; + $id = $sig->getID(); + $handle->setName('Signature '.$id); + + $signer_name = $sig->getSignerName(); + $handle->setFullName("Signature {$id} by {$signer_name}"); + $handle->setURI("/legalpad/signature/{$id}"); + } + } +} diff --git a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php index a245417483..8c4dd31ff1 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php +++ b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php @@ -53,7 +53,7 @@ protected function buildQueryFromParameters(array $map) { } if ($map['contributorPHIDs']) { - $query->withContributorPHIDs($map['creatorPHIDs']); + $query->withContributorPHIDs($map['contributorPHIDs']); } if ($map['creatorPHIDs']) { diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php index c310dd3d64..f8ee72c913 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php @@ -4,6 +4,7 @@ final class LegalpadDocumentSignatureQuery extends PhabricatorCursorPagedPolicyAwareQuery { private $ids; + private $phids; private $documentPHIDs; private $signerPHIDs; private $documentVersions; @@ -16,6 +17,11 @@ public function withIDs(array $ids) { return $this; } + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + public function withDocumentPHIDs(array $phids) { $this->documentPHIDs = $phids; return $this; @@ -46,20 +52,14 @@ public function withEmailContains($text) { return $this; } - protected function loadPage() { - $table = new LegalpadDocumentSignature(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); + public function newResultObject() { + return new LegalpadDocumentSignature(); + } + protected function loadPage() { + $table = $this->newResultObject(); + $data = $this->loadStandardPageRows($table); $signatures = $table->loadAllFromArray($data); - return $signatures; } @@ -98,6 +98,13 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { $this->ids); } + if ($this->phids !== null) { + $where[] = qsprintf( + $conn, + 'phid IN (%Ls)', + $this->phids); + } + if ($this->documentPHIDs !== null) { $where[] = qsprintf( $conn, diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php index ea14fd4a2f..14ebca9d04 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php +++ b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php @@ -5,6 +5,75 @@ final class LegalpadDocumentSignatureSearchEngine private $document; + public function newQuery() { + return new LegalpadDocumentSignatureQuery(); + } + + protected function buildCustomSearchFields() { + return array( + id(new PhabricatorUsersSearchField()) + ->setLabel(pht('Signed By')) + ->setKey('signerPHIDs') + ->setAliases(array('signer', 'signers', 'signerPHID')) + ->setDescription( + pht('Search for signatures by given users.')), + id(new PhabricatorPHIDsSearchField()) + ->setLabel(pht('Documents')) + ->setKey('documentPHIDs') + ->setAliases(array('document', 'documents', 'documentPHID')) + ->setDescription( + pht('Search for signatures on the given documents')), + id(new PhabricatorSearchTextField()) + ->setLabel(pht('Name Contains')) + ->setKey('nameContains') + ->setDescription( + pht('Search for signatures with a name containing the '. + 'given string.')), + id(new PhabricatorSearchTextField()) + ->setLabel(pht('Email Contains')) + ->setKey('emailContains') + ->setDescription( + pht('Search for signatures with an email containing the '. + 'given string.')), + id(new PhabricatorSearchDateField()) + ->setLabel(pht('Created After')) + ->setKey('createdStart'), + id(new PhabricatorSearchDateField()) + ->setLabel(pht('Created Before')) + ->setKey('createdEnd'), + ); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['signerPHIDs']) { + $query->withSignerPHIDs($map['signerPHIDs']); + } + + if ($map['documentPHIDs']) { + $query->withDocumentPHIDs($map['documentPHIDs']); + } + + if ($map['createdStart']) { + $query->withDateCreatedAfter($map['createdStart']); + } + + if ($map['createdEnd']) { + $query->withDateCreatedAfter($map['createdStart']); + } + + if ($map['nameContains']) { + $query->withNameContains($map['nameContains']); + } + + if ($map['emailContains']) { + $query->withEmailContains($map['emailContains']); + } + + return $query; + } + public function getResultTypeDescription() { return pht('Legalpad Signatures'); } diff --git a/src/applications/legalpad/storage/LegalpadDocument.php b/src/applications/legalpad/storage/LegalpadDocument.php index efcd6b5f15..e3245532e8 100644 --- a/src/applications/legalpad/storage/LegalpadDocument.php +++ b/src/applications/legalpad/storage/LegalpadDocument.php @@ -5,7 +5,8 @@ final class LegalpadDocument extends LegalpadDAO PhabricatorPolicyInterface, PhabricatorSubscribableInterface, PhabricatorApplicationTransactionInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorConduitResultInterface { protected $title; protected $contributorCount; @@ -159,6 +160,10 @@ public function getSignatureTypeIcon() { return idx($map, $type, 'fa-user grey'); } + public function getPreamble() { + return $this->preamble; + } + /* -( PhabricatorSubscribableInterface )----------------------------------- */ @@ -167,6 +172,48 @@ public function isAutomaticallySubscribed($phid) { return ($this->creatorPHID == $phid); } +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('title') + ->setType('string') + ->setDescription(pht('The title of this document')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('creatorPHID') + ->setType('phid') + ->setDescription(pht('This user who created this document')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('versions') + ->setType('int') + ->setDescription(pht('The number of versions of this document')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('requireSignature') + ->setType('bool') + ->setDescription(pht( + 'Whether signatures on this doc are required to use this install')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'title' => $this->title, + 'creatorPHID' => $this->creatorPHID, + 'versions' => $this->versions, + 'requireSignature' => (bool)$this->requireSignature, + ); + } + + public function getConduitSearchAttachments() { + return array( + id(new PhabricatorLegalpadBodySearchEngineAttachment()) + ->setAttachmentKey('body'), + id(new PhabricatorLegalpadSignaturesSearchEngineAttachment()) + ->setAttachmentKey('signatures'), + ); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/legalpad/storage/LegalpadDocumentSignature.php b/src/applications/legalpad/storage/LegalpadDocumentSignature.php index 1cd0875dda..308d51ff2a 100644 --- a/src/applications/legalpad/storage/LegalpadDocumentSignature.php +++ b/src/applications/legalpad/storage/LegalpadDocumentSignature.php @@ -2,7 +2,9 @@ final class LegalpadDocumentSignature extends LegalpadDAO - implements PhabricatorPolicyInterface { + implements + PhabricatorPolicyInterface, + PhabricatorConduitResultInterface { const VERIFIED = 0; const UNVERIFIED = 1; @@ -23,6 +25,7 @@ final class LegalpadDocumentSignature protected function getConfiguration() { return array( + self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'signatureData' => self::SERIALIZATION_JSON, ), @@ -51,6 +54,14 @@ protected function getConfiguration() { ) + parent::getConfiguration(); } + public function getPHIDType() { + return PhabricatorLegalpadDocumentSignaturePHIDType::TYPECONST; + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID($this->getPHIDType()); + } + public function save() { if (!$this->getSecretKey()) { $this->setSecretKey(Filesystem::readRandomCharacters(20)); @@ -71,6 +82,76 @@ public function attachDocument(LegalpadDocument $document) { return $this; } + public function getSignerPHID() { + return $this->signerPHID; + } + + public function getIsExemption() { + return (bool)$this->isExemption; + } + + public function getExemptionPHID() { + return $this->exemptionPHID; + } + + public function getSignerName() { + return $this->signerName; + } + + public function getSignerEmail() { + return $this->signerEmail; + } + + public function getDocumentVersion() { + return (int)$this->documentVersion; + } + +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('documentPHID') + ->setType('phid') + ->setDescription(pht('The PHID of the document')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('signerPHID') + ->setType('phid?') + ->setDescription(pht('The PHID of the signer')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('exemptionPHID') + ->setType('phid?') + ->setDescription(pht('The PHID of the user who granted the exemption')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('signerName') + ->setType('string') + ->setDescription(pht('The name used by the signer.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('signerEmail') + ->setType('string') + ->setDescription(pht('The email used by the signer.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('isExemption') + ->setType('bool') + ->setDescription(pht('Whether or not this signature is an exemption')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'documentPHID' => $this->getDocumentPHID(), + 'signerPHID' => $this->getSignerPHID(), + 'exemptionPHID' => $this->getExemptionPHID(), + 'signerName' => $this->getSignerName(), + 'signerEmail' => $this->getSignerEmail(), + 'isExemption' => $this->getIsExemption(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ From ce31289228b9bf63379abb14d23eb5a14f2f92e3 Mon Sep 17 00:00:00 2001 From: MacFan4000 Date: Sat, 4 Sep 2021 15:46:44 -0400 Subject: [PATCH 009/425] (PhabricatorENV) update doclinks to link to we.phorge.it instead of secure.phabricator.com Summary: With this change, links to documentation will point to we.phorge.it rather then secure.phabricator,com Test Plan: No testing needed - simple URL update Reviewers: O1 Blessed Committers, deadalnix Reviewed By: O1 Blessed Committers, deadalnix Subscribers: taavi, speck, tobiaswiese Differential Revision: https://we.phorge.it/D25014 --- src/infrastructure/env/PhabricatorEnv.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index 4806289f52..3eaba5cd10 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -495,7 +495,7 @@ public static function getDoclink($resource, $type = 'article') { ); $uri = new PhutilURI( - 'https://secure.phabricator.com/diviner/find/', + 'https://we.phorge.it/diviner/find/', $params); return phutil_string_cast($uri); From fd1922c7631b43528037d40141707adcee3eb027 Mon Sep 17 00:00:00 2001 From: Leon Eckardt Date: Sat, 4 Sep 2021 15:48:31 -0400 Subject: [PATCH 010/425] Update Install and Update Scripts to the new Paths and Repository links Summary: Set the Reposity links to actually install Phorge, Update Documentation links to the new Website. Notes: - Github Mirrors are not set up. Use one anyway? Or just use the Repos at we.phorge.it? - Documentation Links still contain "phabricator". The Docs are changed, but the Diviner Books are not rebuilt. (Add a Cronjob or do it after every Commit?) - mysql-server is not included anymore in Debian environments. Should mariadb-server be used? - Should we print somewhere that Phorge is a Fork of Phabricator? Test Plan: Run the install script. It should install Phorge and display Links to we.phore.it Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese Differential Revision: https://we.phorge.it/D25019 --- scripts/install/install_rhel-derivs.sh | 27 ++++++++------ scripts/install/install_ubuntu.sh | 36 +++++++++++-------- ...update_phabricator.sh => update_phorge.sh} | 27 +++++++------- 3 files changed, 52 insertions(+), 38 deletions(-) rename scripts/install/{update_phabricator.sh => update_phorge.sh} (66%) diff --git a/scripts/install/install_rhel-derivs.sh b/scripts/install/install_rhel-derivs.sh index b5abfbdd25..78a3375c95 100755 --- a/scripts/install/install_rhel-derivs.sh +++ b/scripts/install/install_rhel-derivs.sh @@ -6,18 +6,23 @@ confirm() { } RHEL_VER_FILE="/etc/redhat-release" +INSTALL_URI=" https://we.phorge.it/book/phorge/article/installation_guide/" +CONFIG_URI=" https://we.phorge.it/book/phorge/article/configuration_guide/" +REPOSITORY_URI="https://we.phorge.it/source" +NAME_MAIN="phorge" +NAME_ARC="arcanist" if [[ ! -f $RHEL_VER_FILE ]] then echo "It looks like you're not running a Red Hat-derived distribution." - echo "This script is intended to install Phabricator on RHEL-derived" + echo "This script is intended to install $NAME_MAIN on RHEL-derived" echo "distributions such as RHEL, Fedora, CentOS, and Scientific Linux." echo "Proceed with caution." confirm fi -echo "PHABRICATOR RED HAT DERIVATIVE INSTALLATION SCRIPT"; -echo "This script will install Phabricator and all of its core dependencies."; +echo "$NAME_MAIN RED HAT DERIVATIVE INSTALLATION SCRIPT"; +echo "This script will install $NAME_MAIN and all of its core dependencies."; echo "Run it from the directory you want to install into."; echo @@ -43,7 +48,7 @@ then confirm fi -echo "Phabricator will be installed to: $(pwd)."; +echo "$NAME_MAIN will be installed to: $(pwd)."; confirm echo "Testing sudo/root..." @@ -117,22 +122,22 @@ fi confirm -if [[ ! -e arcanist ]] +if [[ ! -e "$NAME_ARC" ]] then - git clone https://github.com/phacility/arcanist.git + git clone "$REPOSITORY_URI/$NAME_ARC.git" else - (cd arcanist && git pull --rebase) + (cd "$NAME_ARC" && git pull --rebase) fi -if [[ ! -e phabricator ]] +if [[ ! -e "$NAME_MAIN" ]] then - git clone https://github.com/phacility/phabricator.git + git clone "$REPOSITORY_URI/$NAME_MAIN.git" else - (cd phabricator && git pull --rebase) + (cd "$NAME_MAIN" && git pull --rebase) fi echo echo echo "Install probably worked mostly correctly. Continue with the 'Configuration Guide':"; echo -echo " https://secure.phabricator.com/book/phabricator/article/configuration_guide/"; +echo $CONFIG_URI diff --git a/scripts/install/install_ubuntu.sh b/scripts/install/install_ubuntu.sh index 7f5f552fdf..2694a4ab9b 100755 --- a/scripts/install/install_ubuntu.sh +++ b/scripts/install/install_ubuntu.sh @@ -5,7 +5,13 @@ confirm() { read -e ignored } -INSTALL_URI=" https://phurl.io/u/install" +INSTALL_URI=" https://we.phorge.it/book/phorge/article/installation_guide/" +CONFIG_URI=" https://we.phorge.it/book/phorge/article/configuration_guide/" +REPOSITORY_URI="https://we.phorge.it/source" +NAME_MAIN="phorge" +NAME_ARC="arcanist" + +NAME_UPPER="$(echo $NAME_MAIN | tr a-z A-Z)" failed() { echo @@ -30,8 +36,8 @@ then confirm fi -echo "PHABRICATOR UBUNTU INSTALL SCRIPT"; -echo "This script will install Apache, Phabricator and its core dependencies."; +echo "$NAME_UPPER UBUNTU INSTALL SCRIPT"; +echo "This script will install Apache, $NAME_MAIN and its core dependencies."; echo "Run it from the directory you want to install into."; echo @@ -52,8 +58,8 @@ if [ "$MAJOR" -lt 16 ] then echo 'This script is intented to install on modern operating systems; Your ' echo 'operating system is too old for this script.' - echo 'You can still install Phabricator manually - please consult the installation' - echo 'guide to see how:' + echo 'You can still install $NAME_MAIN manually - please consult the' + echo 'installation guide to see how:' echo echo $INSTALL_URI echo @@ -65,7 +71,7 @@ fi if [ "$MAJOR" -eq 16 ] then echo 'This version of Ubuntu requires additional resources in order to install' - echo 'and run Phabricator.' + echo 'and run $NAME_MAIN.' echo 'We will now add a the following package repository to your system:' echo ' https://launchpad.net/~ondrej/+archive/ubuntu/php' echo @@ -76,7 +82,7 @@ then fi ROOT=`pwd` -echo "Phabricator will be installed to: ${ROOT}."; +echo "$NAME_MAIN will be installed to: ${ROOT}."; confirm echo "Installing dependencies: git, apache, mysql, php..."; @@ -91,27 +97,27 @@ echo "Enabling mod_rewrite in Apache..." echo sudo a2enmod rewrite || failed -echo "Downloading Phabricator and dependencies..." +echo "Downloading $NAME_MAIN and dependencies..." echo -if [ ! -e arcanist ] +if [ ! -e "$NAME_ARC" ] then - git clone https://github.com/phacility/arcanist.git + git clone "$REPOSITORY_URI/$NAME_ARC.git" else - (cd arcanist && git pull --rebase) + (cd "$NAME_ARC" && git pull --rebase) fi -if [ ! -e phabricator ] +if [ ! -e "$NAME_MAIN" ] then - git clone https://github.com/phacility/phabricator.git + git clone "$REPOSITORY_URI/$NAME_MAIN.git" else - (cd phabricator && git pull --rebase) + (cd "$NAME_MAIN" && git pull --rebase) fi echo echo echo "Install probably worked mostly correctly. Continue with the 'Configuration Guide':"; echo -echo " https://secure.phabricator.com/book/phabricator/article/configuration_guide/"; +echo $CONFIG_URI echo echo 'Next step is "Configuring Apache webserver".' diff --git a/scripts/install/update_phabricator.sh b/scripts/install/update_phorge.sh similarity index 66% rename from scripts/install/update_phabricator.sh rename to scripts/install/update_phorge.sh index 3831acd963..c84583506b 100755 --- a/scripts/install/update_phabricator.sh +++ b/scripts/install/update_phorge.sh @@ -3,32 +3,35 @@ set -e set -x -# This is an example script for updating Phabricator, similar to the one used to -# update . It might not work perfectly on your -# system, but hopefully it should be easy to adapt. This script is not intended -# to work without modifications. +# This is an example script for updating Phabricator. It might not work +# perfectly on your system, but hopefully it should be easy to adapt. This +# script is not intended to work without modifications. # NOTE: This script assumes you are running it from a directory which contains -# arcanist/ and phabricator/. +# arcanist/ and phorge/. If you named them differently, you can change them +# here: +NAME_MAIN="phorge" +NAME_ARC="arcanist" + ROOT=`pwd` # You can hard-code the path here instead. ### UPDATE WORKING COPIES ###################################################### -cd $ROOT/arcanist +cd $ROOT/$NAME_ARC git pull -cd $ROOT/phabricator +cd $ROOT/$NAME_MAIN git pull ### CYCLE WEB SERVER AND DAEMONS ############################################### # Stop daemons. -$ROOT/phabricator/bin/phd stop +$ROOT/$NAME_MAIN/bin/phd stop # If running the notification server, stop it. -# $ROOT/phabricator/bin/aphlict stop +# $ROOT/$NAME_MAIN/bin/aphlict stop # Stop the webserver (apache, nginx, lighttpd, etc). This command will differ # depending on which system and webserver you are running: replace it with an @@ -40,14 +43,14 @@ sudo /etc/init.d/httpd stop # Upgrade the database schema. You may want to add the "--force" flag to allow # this script to run noninteractively. -$ROOT/phabricator/bin/storage upgrade +$ROOT/$NAME_MAIN/bin/storage upgrade # Restart the webserver. As above, this depends on your system and webserver. # NOTE: If you're running php-fpm, restart it here too. sudo /etc/init.d/httpd start # Restart daemons. -$ROOT/phabricator/bin/phd start +$ROOT/$NAME_MAIN/bin/phd start # If running the notification server, start it. -# $ROOT/phabricator/bin/aphlict start +# $ROOT/$NAME_MAIN/bin/aphlict start From b293e6ffed0ded5c13ace43275b80e31d5c4397d Mon Sep 17 00:00:00 2001 From: Leon Eckardt Date: Tue, 21 Sep 2021 20:45:14 -0400 Subject: [PATCH 011/425] Make Datepicker translatable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: This adds the Ablillity to translate the Content of the Date Picker. It is possible to display more than one Letter for the Day Captions. Test Plan: - Change the Language in the User Preferences, make sure it translates 'S|M|T|W|T|F|S' and Months or add the following to the translation.override config to get German Translation: ``` { 'S|M|T|W|T|F|S': 'So|Mo|Di|Mi|Do|Fr|Sa', 'January': 'Januar', 'February': 'Februar', 'March': 'März', 'May': 'Mai', 'June': 'Juni', 'July': 'Juli', 'October': 'Oktober', 'December': 'Dezember' } ``` - Open Datepicker on a Datefield (Edit Event or Edit for Custom Field of Type date) - The Day Headers and the Months should now be translated Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: CSharp, 0, speck, tobiaswiese Tags: #calendar Differential Revision: https://we.phorge.it/D25016 --- resources/celerity/map.php | 18 ++++++------ .../form/control/AphrontFormDateControl.php | 16 ++++++++++ .../rsrc/js/core/behavior-fancy-datepicker.js | 29 ++++++++++--------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 309e698272..7625e174cb 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -481,7 +481,7 @@ 'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94', 'rsrc/js/core/behavior-device.js' => 'ac2b1e01', 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '7ad020a5', - 'rsrc/js/core/behavior-fancy-datepicker.js' => '956f3eeb', + 'rsrc/js/core/behavior-fancy-datepicker.js' => 'e77fcc9d', 'rsrc/js/core/behavior-form.js' => '55d7b788', 'rsrc/js/core/behavior-gesture.js' => 'b58d1a2a', 'rsrc/js/core/behavior-global-drag-and-drop.js' => '1cab0e9a', @@ -635,7 +635,7 @@ 'javelin-behavior-editengine-reorder-configs' => '4842f137', 'javelin-behavior-editengine-reorder-fields' => '0ad8d31f', 'javelin-behavior-event-all-day' => '0b1bc990', - 'javelin-behavior-fancy-datepicker' => '956f3eeb', + 'javelin-behavior-fancy-datepicker' => 'e77fcc9d', 'javelin-behavior-global-drag-and-drop' => '1cab0e9a', 'javelin-behavior-harbormaster-log' => 'b347a301', 'javelin-behavior-herald-rule-editor' => '0922e81d', @@ -1781,13 +1781,6 @@ 'javelin-vector', 'javelin-stratcom', ), - '956f3eeb' => array( - 'javelin-behavior', - 'javelin-util', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-vector', - ), '9623adc1' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2174,6 +2167,13 @@ 'javelin-dom', 'phabricator-draggable-list', ), + 'e77fcc9d' => array( + 'javelin-behavior', + 'javelin-util', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-vector', + ), 'e8240b50' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/src/view/form/control/AphrontFormDateControl.php b/src/view/form/control/AphrontFormDateControl.php index 833c0a7c89..35613b3d66 100644 --- a/src/view/form/control/AphrontFormDateControl.php +++ b/src/view/form/control/AphrontFormDateControl.php @@ -267,9 +267,25 @@ protected function renderInput() { $week_key = PhabricatorWeekStartDaySetting::SETTINGKEY; $week_start = $viewer->getUserSetting($week_key); + $date_pht = array( + 'S|M|T|W|T|F|S' => pht('S|M|T|W|T|F|S'), + 'January' => pht('January'), + 'February' => pht('February'), + 'March' => pht('March'), + 'April' => pht('April'), + 'May' => pht('May'), + 'June' => pht('June'), + 'July' => pht('July'), + 'August' => pht('August'), + 'September' => pht('September'), + 'October' => pht('October'), + 'November' => pht('November'), + 'December' => pht('December'), + ); Javelin::initBehavior('fancy-datepicker', array( 'format' => $this->getDateFormat(), 'weekStart' => $week_start, + 'pht' => $date_pht, )); $classes = array(); diff --git a/webroot/rsrc/js/core/behavior-fancy-datepicker.js b/webroot/rsrc/js/core/behavior-fancy-datepicker.js index afd5ff25ad..36942560b7 100644 --- a/webroot/rsrc/js/core/behavior-fancy-datepicker.js +++ b/webroot/rsrc/js/core/behavior-fancy-datepicker.js @@ -13,6 +13,7 @@ JX.behavior('fancy-datepicker', function(config, statics) { } statics.initialized = true; + var pht = JX.phtize(config.pht); var picker; var anchor_node; var root; @@ -262,18 +263,18 @@ JX.behavior('fancy-datepicker', function(config, statics) { var year = valid_date.getYear() + 1900; var months = [ - 'January', - 'February', - 'March', - 'April', - 'May', - 'June', - 'July', - 'August', - 'September', - 'October', - 'November', - 'December']; + pht('January'), + pht('February'), + pht('March'), + pht('April'), + pht('May'), + pht('June'), + pht('July'), + pht('August'), + pht('September'), + pht('October'), + pht('November'), + pht('December')]; var buttons = [ cell('\u25C0', 'm:-1', false, 'lrbutton'), @@ -330,14 +331,14 @@ JX.behavior('fancy-datepicker', function(config, statics) { var weeks = []; // First, render the weekday names. - var weekdays = 'SMTWTFS'; + var weekdays = pht('S|M|T|W|T|F|S').split('|'); var weekday_names = []; var week_start = parseInt(get_week_start(), 10); var week_end = weekdays.length + week_start; for (var ii = week_start; ii < week_end; ii++) { var index = ii%7; - weekday_names.push(cell(weekdays.charAt(index), null, false, 'day-name')); + weekday_names.push(cell(weekdays[index], null, false, 'day-name')); } weeks.push(JX.$N('tr', {}, weekday_names)); From 7d4357683a313f4ec96d2f2546128d4af7b2fb47 Mon Sep 17 00:00:00 2001 From: Matthew Bowker Date: Wed, 30 Mar 2022 09:16:52 -0600 Subject: [PATCH 012/425] Hide the blurb of a user when that user is disabled Summary: T15074 Test Plan: Disabled a user, then made sure their blurb disappeared. Reviewers: O1 Blessed Committers, avivey, speck Reviewed By: O1 Blessed Committers, avivey, speck Subscribers: 20after4, avivey, speck, tobiaswiese, valerio.bozzolan Maniphest Tasks: T15074 Differential Revision: https://we.phorge.it/D25035 --- .../people/storage/PhabricatorUser.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index fa6dc08e9f..1560f9f65e 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -320,12 +320,28 @@ private function generateConduitCertificate() { const EMAIL_CYCLE_FREQUENCY = 86400; const EMAIL_TOKEN_LENGTH = 24; + /** + * This function removes the blurb from a profile. + * This is an incredibly broad hammer to handle some spam on the upstream, + * which will be refined later. + * + * @return void + */ + private function cleanUpProfile() { + $this->profile->setBlurb(''); + } + public function getUserProfile() { return $this->assertAttached($this->profile); } public function attachUserProfile(PhabricatorUserProfile $profile) { $this->profile = $profile; + + if ($this->isDisabled) { + $this->cleanUpProfile(); + } + return $this; } @@ -342,6 +358,10 @@ public function loadUserProfile() { $this->profile = PhabricatorUserProfile::initializeNewProfile($this); } + if ($this->isDisabled) { + $this->cleanUpProfile(); + } + return $this->profile; } From dbf7cd2b4a1a62b423b703fa32256a82d82d51fc Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Thu, 25 Aug 2022 01:26:07 -0700 Subject: [PATCH 013/425] Rebrand: Rename library and update "version" logic Summary: Make the "version" data in Config show information. Also rename the library. Test Plan: See version "branched from" information. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew Maniphest Tasks: T15006 Differential Revision: https://we.phorge.it/D25047 --- src/__phutil_library_init__.php | 2 +- .../controller/PhabricatorConfigConsoleController.php | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/__phutil_library_init__.php b/src/__phutil_library_init__.php index f150c1f94f..55c0912aba 100644 --- a/src/__phutil_library_init__.php +++ b/src/__phutil_library_init__.php @@ -1,3 +1,3 @@ Date: Thu, 25 Aug 2022 01:29:23 -0700 Subject: [PATCH 014/425] Update default wordmark and logo Summary: The current default wordmark is "Phabricator" which is trademarked and the default logo is also copyright. (This change was made by @speck directly in the deployed instnace, bringing it into `master` now). Test Plan: should be fine. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, speck Differential Revision: https://we.phorge.it/D25048 --- resources/celerity/map.php | 14 +++++++------- src/view/page/menu/PhabricatorMainMenuView.php | 2 +- .../rsrc/css/application/base/main-menu-view.css | 4 ++-- webroot/rsrc/image/logo/light-eye.png | Bin 1057 -> 0 bytes webroot/rsrc/image/logo/project-logo.png | Bin 0 -> 655 bytes 5 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 webroot/rsrc/image/logo/light-eye.png create mode 100644 webroot/rsrc/image/logo/project-logo.png diff --git a/resources/celerity/map.php b/resources/celerity/map.php index e5f124fcf3..819dc37463 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'b816811e', + 'core.pkg.css' => '80481fe6', 'core.pkg.js' => 'd2de90d9', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => 'ffb69e3d', @@ -38,7 +38,7 @@ 'rsrc/css/aphront/typeahead.css' => '8779483d', 'rsrc/css/application/almanac/almanac.css' => '2e050f4f', 'rsrc/css/application/auth/auth.css' => 'c2f23d74', - 'rsrc/css/application/base/main-menu-view.css' => 'bcec20f0', + 'rsrc/css/application/base/main-menu-view.css' => '89fc16b6', 'rsrc/css/application/base/notification-menu.css' => '4df1ee30', 'rsrc/css/application/base/phui-theme.css' => '35883b37', 'rsrc/css/application/base/standard-page-view.css' => 'a374f94c', @@ -320,7 +320,7 @@ 'rsrc/image/icon/tango/upload.png' => '3fe6b92d', 'rsrc/image/icon/unsubscribe.png' => 'db04378a', 'rsrc/image/lightblue-header.png' => 'e6d483c6', - 'rsrc/image/logo/light-eye.png' => '72337472', + 'rsrc/image/logo/project-logo.png' => '019d256f', 'rsrc/image/main_texture.png' => '894d03c4', 'rsrc/image/menu_texture.png' => '896c9ade', 'rsrc/image/people/harding.png' => '95b2db63', @@ -791,7 +791,7 @@ 'phabricator-flag-css' => '2b77be8d', 'phabricator-keyboard-shortcut' => '1a844c06', 'phabricator-keyboard-shortcut-manager' => '81debc48', - 'phabricator-main-menu-view' => 'bcec20f0', + 'phabricator-main-menu-view' => '89fc16b6', 'phabricator-nav-view-css' => '423f92cc', 'phabricator-notification' => 'a9b91e3f', 'phabricator-notification-css' => '30240bd2', @@ -1665,6 +1665,9 @@ 'javelin-stratcom', 'javelin-install', ), + '89fc16b6' => array( + 'phui-theme-css', + ), '8ac32fd9' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2010,9 +2013,6 @@ 'phabricator-drag-and-drop-file-upload', 'javelin-workboard-board', ), - 'bcec20f0' => array( - 'phui-theme-css', - ), 'c03f2fb4' => array( 'javelin-install', ), diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php index 29a259b44c..6a69c4feaa 100644 --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -327,7 +327,7 @@ private function renderPhabricatorLogo() { $logo_node = phutil_tag( 'span', array( - 'class' => 'phabricator-main-menu-eye', + 'class' => 'phabricator-main-menu-project-logo', 'style' => implode(' ', $logo_style), )); diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index 66f81da0bc..8c5000247a 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -47,13 +47,13 @@ padding-left: 6px; } -.phabricator-main-menu-eye { +.phabricator-main-menu-project-logo { margin: 2px 0; width: 40px; height: 40px; float: left; display: block; - background-image: url(/rsrc/image/logo/light-eye.png); + background-image: url(/rsrc/image/logo/project-logo.png); background-size: 40px 40px; } diff --git a/webroot/rsrc/image/logo/light-eye.png b/webroot/rsrc/image/logo/light-eye.png deleted file mode 100644 index 772dcb0c01a2c3360861b3229bd63f36fe721074..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1057 zcmV++1m63JP)nhyD>9cb2<0&DWk{(kYh9-avvA7IWtM`VO9>|S&CFr;4uznR;e`>p)alU z)OZ?LSZaki=uZh(1sa%5!s7sjQ>|g_DIqZnH>1llkZ<^gffUeP&&(1AOY$`~pVNn( zS%PV#1JkesJJW~HmCr98BHkJd#nem_D@g^cL?4g{CO7bL) zVU4iyUSuh8z*4-VL<|sbG8Zz9IAR(uVzL=AM7%tEOO@E0yn95qcm=i<4{S@(J)%qJ z?kk?yPwQy^cyNVSu_0Hpio|dha)4QLqI%^n%V!;2u$n2OEnOi}4MW+@M8dV{neB#`~RVjOqUm znxmHI?|jCaQ5%q&`UW~iWKks^+$ss2K{Zy91a1W-;-X^1OK6Ue1irl z2^@juB}ro|bn6&}_7&3doXCvw7na}wY~GLr)m*G|{z~q~^pjbr zm;>FNVqy@QoORRw9C3F`0&|l$;w>&I;lg*Fj?7qSO$WNn+TMq!N3IxDOKdG85qh(jH`H z>A+fy#8hTauQKCQs6WK!SI%Wy_TmOg*o^M|dr_JQl2LP|3NX;Celt?~>mDPNOlx0?Lx#bJOG= zaA&ciziT`LYv|if6RmXM8`foQZlv4ZV!;ZONgBf{{u}qVm`*JCy{ir`&@n!5r31TI zH|}pJ8}~3rAI!tK{KgL~9xEZ&GKJ^a%X$`Zxe_nr;Stj^izG0=Sh0`K;ZgYGx2?mU z5c(Wo?&%XR{1Nc^dgu23A^H-$07|@GzlNBdD?(gD7+A5Yi<}i`v|i+_CQ~1a@wt8# z#^T0jccu@YV)GSChPz$A4qc9ce9hMkB!})sW|5HCgJD!_82d?h%tk#0Tovih98xPR zOatTTsTTUOh}0Uhaxjlkppqibdi_&{KW1WcF6KTyWHdPd@{HnB?&Wf}W@gD`GMP*! b^RMHi`00000NkvXXu0mjfP6hBs diff --git a/webroot/rsrc/image/logo/project-logo.png b/webroot/rsrc/image/logo/project-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..85d0ddad1efd71906c47fba726d4e552f0b61c07 GIT binary patch literal 655 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qt-3`{DXE{-7;ac^%nPHZv|U=7%5wZ}>43D-tJ z?u`QLC+xP{@xQdcQA16dchZmhK57dT-uNXj=eZz*C8@Rd^Dpvc*k>fWZ_&T~hHJmG z^}^ookAL0$JF8J)rUl#VBoB`17Z0V}KXm`zt-M^v1nVtRRC)@hY^wYqVQrlwEg;}N z&%Cn#!N#t>Bjg+n7Ssgnje`LHCTwlE@ zKD6M|N_{>Jp9>W&zVFH-_D_9a>3(~yokit7*N+^+ThljRHO@QCD|^jg>Zx{yApNcN zn;9Qlt6e!-wpKi;fSotI*u1Xn=c!bqs@1IydlnZ2g`BYaykdJri-6C$-v<;KWA@cN z33|`+ON?boG3T>A6PKCVIN8ZFo!%*5wYBj6>}CerV(0Z$&uip3OrFmZyX<$d!*PS^ zzm%-i8AlWz%vof#{OlsZ<^%HsLvHCw8G0O0c2)hlH0$<+jya1y*|{pSojjqb^*+=j zX@bCa?OIL+LrvGGd%N9ugi7Au40ly#6%zgH@AA%J)1gI&Qbl?BRdQlNLQIMj3U#!! hq?C|A(BiYt`1jw6e#%hRlm|>i44$rjF6*2Ung9_Q67B#1 literal 0 HcmV?d00001 From a16e8773f009daaed9e058b902b544bcdbcf724f Mon Sep 17 00:00:00 2001 From: Matthew Bowker Date: Wed, 21 Sep 2022 11:25:51 -0600 Subject: [PATCH 015/425] Show confirmation dialog when closing a modal if form contents have been changed Summary: Honestly I did not realize that Differential can do this. Anyway this is related to T15034 ... Originally opened at https://secure.phabricator.com/T12676 Test Plan: 1) Start creating a task via a Workboard in Manifest, type many words, press `ESC` 2) Start creating a task via a Workboard in Manifest, type no words, press `ESC` Reviewers: O1 Blessed Committers, Ekubischta, speck Reviewed By: O1 Blessed Committers, Ekubischta, speck Subscribers: Leon95, 20after4, avivey, Ekubischta, speck, tobiaswiese Tags: #maniphest Differential Revision: https://we.phorge.it/D25015 --- .../rsrc/externals/javelin/lib/Workflow.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/webroot/rsrc/externals/javelin/lib/Workflow.js b/webroot/rsrc/externals/javelin/lib/Workflow.js index 25de547deb..7d63a3408a 100644 --- a/webroot/rsrc/externals/javelin/lib/Workflow.js +++ b/webroot/rsrc/externals/javelin/lib/Workflow.js @@ -403,6 +403,16 @@ JX.install('Workflow', { JX.$E('Response to workflow request went unhandled.'); } } + + var form = JX.DOM.scry(this._root, 'form', 'jx-dialog'); + if (form.length) { + JX.DOM.listen(form[0], 'keydown', null, function(e) { + if (e.getSpecialKey()) { + return; + } + JX.Stratcom.addSigil(form[0], 'dialog-keydown'); + }); + } }, _push : function() { if (!this._pushed) { @@ -536,6 +546,15 @@ JX.install('Workflow', { return; } + var form = JX.DOM.scry(active._root, 'form', 'jx-dialog'); + if ( + form.length && + JX.Stratcom.hasSigil(form[0], 'dialog-keydown') && + !confirm('Form data may have changed. Are you sure you want to close this dialog?') + ) { + return; + } + JX.Workflow._pop(); e.prevent(); } From 69b2710af9c0485cefae6078249b891ef3aa712f Mon Sep 17 00:00:00 2001 From: Leon Eckardt Date: Wed, 21 Sep 2022 11:31:37 -0600 Subject: [PATCH 016/425] Prevent Line Numbers in Diffusion being copied as Tabs Summary: Marks Line Numbers in Diffusion File Preview as unselectable Test Plan: - Select multiple Lines from a File Preview in Diffusion - Copy them into a Text Editor - The Leading Tabs should no longer included Reviewers: O1 Blessed Committers, speck, Ekubischta Reviewed By: O1 Blessed Committers, speck, Ekubischta Subscribers: Ekubischta, speck, tobiaswiese, valerio.bozzolan Differential Revision: https://we.phorge.it/D25024 --- resources/celerity/map.php | 4 ++-- webroot/rsrc/css/layout/phabricator-source-code-view.css | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 819dc37463..2985c19563 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -121,7 +121,7 @@ 'rsrc/css/fuel/fuel-handle-list.css' => '2c4cbeca', 'rsrc/css/fuel/fuel-map.css' => 'd6e31510', 'rsrc/css/fuel/fuel-menu.css' => '21f5d199', - 'rsrc/css/layout/phabricator-source-code-view.css' => '03d7ac28', + 'rsrc/css/layout/phabricator-source-code-view.css' => '6b31244f', 'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4', 'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa', 'rsrc/css/phui/button/phui-button.css' => 'ea704902', @@ -803,7 +803,7 @@ 'phabricator-search-results-css' => '9ea70ace', 'phabricator-shaped-request' => '995f5102', 'phabricator-slowvote-css' => '1694baed', - 'phabricator-source-code-view-css' => '03d7ac28', + 'phabricator-source-code-view-css' => '6b31244f', 'phabricator-standard-page-view' => 'a374f94c', 'phabricator-textareautils' => 'f340a484', 'phabricator-title' => '43bc9360', diff --git a/webroot/rsrc/css/layout/phabricator-source-code-view.css b/webroot/rsrc/css/layout/phabricator-source-code-view.css index 9b61425d63..71e3516003 100644 --- a/webroot/rsrc/css/layout/phabricator-source-code-view.css +++ b/webroot/rsrc/css/layout/phabricator-source-code-view.css @@ -25,6 +25,8 @@ border-right: 1px solid {$paste.border}; color: {$sh-yellowtext}; white-space: nowrap; + -webkit-user-select: none; + user-select: none; } .phabricator-source-line > a::before { From f8ffa393c4427f8b9026075e6dc490990a12df12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vedran=20Mileti=C4=87?= Date: Sun, 9 Oct 2022 03:13:05 -0700 Subject: [PATCH 017/425] Added cross-platform default fonts Summary: `system-ui` is provided by modern browsers as [a cross-platform default font](https://caniuse.com/font-family-system-ui). Using this font [was a controversial choice back in 2017](https://infinnie.github.io/blog/2017/systemui.html). because it did not address i18n well and therefore it was removed from Bootstrap [twbs/bootstrap#22377](https://github.com/twbs/bootstrap/pull/22377). However, it was added back in Bootstrap v5 [twbs/bootstrap#30561](https://github.com/twbs/bootstrap/pull/30561) since it got better over time. Fixes T15049. Test Plan: Tested locally for now. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: Matthew, avivey, Cigaryno, 20after4, Ekubischta, speck, tobiaswiese, valerio.bozzolan Maniphest Tasks: T15049 Differential Revision: https://we.phorge.it/D25021 --- resources/celerity/map.php | 82 +++++++++---------- .../CelerityDarkModePostprocessor.php | 12 +-- .../CelerityDefaultPostprocessor.php | 12 +-- .../CelerityLargeFontPostprocessor.php | 6 +- .../application/config/config-template.css | 3 +- .../application/harbormaster/harbormaster.css | 3 +- 6 files changed, 60 insertions(+), 58 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 2985c19563..3815362684 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,10 +9,10 @@ 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '80481fe6', - 'core.pkg.js' => 'd2de90d9', + 'core.pkg.css' => 'd95915b7', + 'core.pkg.js' => '256dfd7b', 'dark-console.pkg.js' => '187792c2', - 'differential.pkg.css' => 'ffb69e3d', + 'differential.pkg.css' => '609e63d4', 'differential.pkg.js' => 'c60bec1b', 'diffusion.pkg.css' => '42c75c37', 'diffusion.pkg.js' => '78c9885d', @@ -45,7 +45,7 @@ 'rsrc/css/application/chatlog/chatlog.css' => 'abdc76ee', 'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41', 'rsrc/css/application/config/config-options.css' => '16c920ae', - 'rsrc/css/application/config/config-template.css' => '20babf50', + 'rsrc/css/application/config/config-template.css' => 'e689dbbd', 'rsrc/css/application/config/setup-issue.css' => '5eed85b2', 'rsrc/css/application/config/unhandled-exception.css' => '9ecfc00d', 'rsrc/css/application/conpherence/color.css' => 'b17746b0', @@ -63,9 +63,9 @@ 'rsrc/css/application/diff/diff-tree-view.css' => 'e2d3e222', 'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d', 'rsrc/css/application/differential/add-comment.css' => '7e5900d9', - 'rsrc/css/application/differential/changeset-view.css' => '60c3d405', + 'rsrc/css/application/differential/changeset-view.css' => 'bf159129', 'rsrc/css/application/differential/core.css' => '7300a73e', - 'rsrc/css/application/differential/phui-inline-comment.css' => '9863a85e', + 'rsrc/css/application/differential/phui-inline-comment.css' => 'a864426f', 'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d', 'rsrc/css/application/differential/revision-history.css' => '237a2979', 'rsrc/css/application/differential/revision-list.css' => '93d2df7d', @@ -77,7 +77,7 @@ 'rsrc/css/application/feed/feed.css' => 'd8b6e3f8', 'rsrc/css/application/files/global-drag-and-drop.css' => '1d2713a4', 'rsrc/css/application/flag/flag.css' => '2b77be8d', - 'rsrc/css/application/harbormaster/harbormaster.css' => '8dfe16b2', + 'rsrc/css/application/harbormaster/harbormaster.css' => 'd98decda', 'rsrc/css/application/herald/herald-test.css' => '7e7bbdae', 'rsrc/css/application/herald/herald.css' => '648d39e2', 'rsrc/css/application/maniphest/report.css' => '3d53188b', @@ -101,15 +101,15 @@ 'rsrc/css/application/policy/policy-transaction-detail.css' => 'c02b8384', 'rsrc/css/application/policy/policy.css' => 'ceb56a08', 'rsrc/css/application/ponder/ponder-view.css' => '05a09d0a', - 'rsrc/css/application/project/project-card-view.css' => 'a9f2c2dd', + 'rsrc/css/application/project/project-card-view.css' => 'c1200da7', 'rsrc/css/application/project/project-triggers.css' => 'cd9c8bb9', - 'rsrc/css/application/project/project-view.css' => '567858b3', + 'rsrc/css/application/project/project-view.css' => '2f7caa20', 'rsrc/css/application/search/application-search-view.css' => '0f7c06d8', 'rsrc/css/application/search/search-results.css' => '9ea70ace', 'rsrc/css/application/slowvote/slowvote.css' => '1694baed', 'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd', 'rsrc/css/application/uiexample/example.css' => 'b4795059', - 'rsrc/css/core/core.css' => 'b3ebd90d', + 'rsrc/css/core/core.css' => 'a708bd25', 'rsrc/css/core/remarkup.css' => '5baa3bd9', 'rsrc/css/core/syntax.css' => '548567f6', 'rsrc/css/core/z-index.css' => 'ac3bfcd4', @@ -124,7 +124,7 @@ 'rsrc/css/layout/phabricator-source-code-view.css' => '6b31244f', 'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4', 'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa', - 'rsrc/css/phui/button/phui-button.css' => 'ea704902', + 'rsrc/css/phui/button/phui-button.css' => 'e434f171', 'rsrc/css/phui/calendar/phui-calendar-day.css' => '9597d706', 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2', 'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42', @@ -142,7 +142,7 @@ 'rsrc/css/phui/phui-big-info-view.css' => '362ad37b', 'rsrc/css/phui/phui-box.css' => '5ed3b8cb', 'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30', - 'rsrc/css/phui/phui-chart.css' => '14df9ae3', + 'rsrc/css/phui/phui-chart.css' => 'fe8f87a7', 'rsrc/css/phui/phui-cms.css' => '8c05c41e', 'rsrc/css/phui/phui-comment-form.css' => '68a2d99a', 'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0', @@ -155,7 +155,7 @@ 'rsrc/css/phui/phui-feed-story.css' => 'a0c05029', 'rsrc/css/phui/phui-fontkit.css' => '1ec937e5', 'rsrc/css/phui/phui-form-view.css' => '01b796c0', - 'rsrc/css/phui/phui-form.css' => '1f177cb7', + 'rsrc/css/phui/phui-form.css' => 'd1adb52c', 'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed', 'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', 'rsrc/css/phui/phui-header-view.css' => '36c86a58', @@ -168,7 +168,7 @@ 'rsrc/css/phui/phui-left-right.css' => '68513c34', 'rsrc/css/phui/phui-lightbox.css' => '4ebf22da', 'rsrc/css/phui/phui-list.css' => '0c04affd', - 'rsrc/css/phui/phui-object-box.css' => 'b8d7eea0', + 'rsrc/css/phui/phui-object-box.css' => 'fdffed5c', 'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', 'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64', @@ -257,7 +257,7 @@ 'rsrc/externals/javelin/lib/URI.js' => '2e255291', 'rsrc/externals/javelin/lib/Vector.js' => 'e9c80beb', 'rsrc/externals/javelin/lib/WebSocket.js' => 'fdc13e4e', - 'rsrc/externals/javelin/lib/Workflow.js' => '945ff654', + 'rsrc/externals/javelin/lib/Workflow.js' => 'd7ba6915', 'rsrc/externals/javelin/lib/__tests__/Cookie.js' => 'ca686f71', 'rsrc/externals/javelin/lib/__tests__/DOM.js' => '4566e249', 'rsrc/externals/javelin/lib/__tests__/JSON.js' => '710377ae', @@ -559,7 +559,7 @@ 'conpherence-transaction-css' => '3a3f5e7e', 'd3' => '9d068042', 'diff-tree-view-css' => 'e2d3e222', - 'differential-changeset-view-css' => '60c3d405', + 'differential-changeset-view-css' => 'bf159129', 'differential-core-view-css' => '7300a73e', 'differential-revision-add-comment-css' => '7e5900d9', 'differential-revision-comment-css' => '7dbc8d1d', @@ -578,7 +578,7 @@ 'fuel-map-css' => 'd6e31510', 'fuel-menu-css' => '21f5d199', 'global-drag-and-drop-css' => '1d2713a4', - 'harbormaster-css' => '8dfe16b2', + 'harbormaster-css' => 'd98decda', 'herald-css' => '648d39e2', 'herald-rule-editor' => '2633bef7', 'herald-test-css' => '7e7bbdae', @@ -756,7 +756,7 @@ 'javelin-workboard-header' => '111bfd2d', 'javelin-workboard-header-template' => 'ebe83a6b', 'javelin-workboard-order-template' => '03e8891f', - 'javelin-workflow' => '945ff654', + 'javelin-workflow' => 'd7ba6915', 'maniphest-report-css' => '3d53188b', 'maniphest-task-edit-css' => '272daa84', 'maniphest-task-summary-css' => '61d1667e', @@ -771,7 +771,7 @@ 'phabricator-busy' => '5202e831', 'phabricator-chatlog-css' => 'abdc76ee', 'phabricator-content-source-view-css' => 'cdf0d579', - 'phabricator-core-css' => 'b3ebd90d', + 'phabricator-core-css' => 'a708bd25', 'phabricator-countdown-css' => 'bff8012f', 'phabricator-darklog' => '3b869402', 'phabricator-darkmessage' => '26cd4b73', @@ -784,7 +784,7 @@ 'phabricator-diff-tree-view' => '5d83623b', 'phabricator-drag-and-drop-file-upload' => '4370900d', 'phabricator-draggable-list' => '0169e425', - 'phabricator-fatal-config-template-css' => '20babf50', + 'phabricator-fatal-config-template-css' => 'e689dbbd', 'phabricator-favicon' => '7930776a', 'phabricator-feed-css' => 'd8b6e3f8', 'phabricator-file-upload' => 'ab85e184', @@ -827,13 +827,13 @@ 'phui-box-css' => '5ed3b8cb', 'phui-bulk-editor-css' => '374d5e30', 'phui-button-bar-css' => 'a4aa75c4', - 'phui-button-css' => 'ea704902', + 'phui-button-css' => 'e434f171', 'phui-button-simple-css' => '1ff278aa', 'phui-calendar-css' => 'f11073aa', 'phui-calendar-day-css' => '9597d706', 'phui-calendar-list-css' => 'ccd7e4e2', 'phui-calendar-month-css' => 'cb758c42', - 'phui-chart-css' => '14df9ae3', + 'phui-chart-css' => 'fe8f87a7', 'phui-cms-css' => '8c05c41e', 'phui-comment-form-css' => '68a2d99a', 'phui-comment-panel-css' => 'ec4e31c0', @@ -846,7 +846,7 @@ 'phui-feed-story-css' => 'a0c05029', 'phui-font-icon-base-css' => '303c9b87', 'phui-fontkit-css' => '1ec937e5', - 'phui-form-css' => '1f177cb7', + 'phui-form-css' => 'd1adb52c', 'phui-form-view-css' => '01b796c0', 'phui-formation-view-css' => 'd2dec8ed', 'phui-head-thing-view-css' => 'd7f293df', @@ -858,12 +858,12 @@ 'phui-icon-view-css' => '4cbc684a', 'phui-image-mask-css' => '62c7f4d2', 'phui-info-view-css' => 'a10a909b', - 'phui-inline-comment-view-css' => '9863a85e', + 'phui-inline-comment-view-css' => 'a864426f', 'phui-invisible-character-view-css' => 'c694c4a4', 'phui-left-right-css' => '68513c34', 'phui-lightbox-css' => '4ebf22da', 'phui-list-view-css' => '0c04affd', - 'phui-object-box-css' => 'b8d7eea0', + 'phui-object-box-css' => 'fdffed5c', 'phui-oi-big-ui-css' => 'fa74cc35', 'phui-oi-color-css' => 'b517bfa0', 'phui-oi-drag-ui-css' => 'da15d3dc', @@ -900,9 +900,9 @@ 'policy-edit-css' => '8794e2ed', 'policy-transaction-detail-css' => 'c02b8384', 'ponder-view-css' => '05a09d0a', - 'project-card-view-css' => 'a9f2c2dd', + 'project-card-view-css' => 'c1200da7', 'project-triggers-css' => 'cd9c8bb9', - 'project-view-css' => '567858b3', + 'project-view-css' => '2f7caa20', 'setup-issue-css' => '5eed85b2', 'sprite-login-css' => '18b368a6', 'sprite-tokens-css' => 'f1896dc5', @@ -1512,9 +1512,6 @@ '5faf27b9' => array( 'phuix-form-control-view', ), - '60c3d405' => array( - 'phui-inline-comment-view-css', - ), '60cd9241' => array( 'javelin-behavior', ), @@ -1747,17 +1744,6 @@ 'javelin-typeahead-preloaded-source', 'javelin-util', ), - '945ff654' => array( - 'javelin-stratcom', - 'javelin-request', - 'javelin-dom', - 'javelin-vector', - 'javelin-install', - 'javelin-util', - 'javelin-mask', - 'javelin-uri', - 'javelin-routable', - ), '9623adc1' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2013,6 +1999,9 @@ 'phabricator-drag-and-drop-file-upload', 'javelin-workboard-board', ), + 'bf159129' => array( + 'phui-inline-comment-view-css', + ), 'c03f2fb4' => array( 'javelin-install', ), @@ -2080,6 +2069,17 @@ 'd4cc2d2a' => array( 'javelin-install', ), + 'd7ba6915' => array( + 'javelin-stratcom', + 'javelin-request', + 'javelin-dom', + 'javelin-vector', + 'javelin-install', + 'javelin-util', + 'javelin-mask', + 'javelin-uri', + 'javelin-routable', + ), 'd7d3ba75' => array( 'javelin-dom', 'javelin-util', diff --git a/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php b/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php index 39ee4be57a..31932352ef 100644 --- a/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php @@ -15,13 +15,13 @@ public function buildVariables() { return array( // Fonts - 'basefont' => "13px 'Segoe UI', 'Segoe UI Emoji', ". - "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ". - "Helvetica, Arial, sans-serif", + 'basefont' => "13px -apple-system, system-ui, BlinkMacSystemFont, ". + "'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', ". + "'Helvetica Neue', Helvetica, Arial, sans-serif", - 'fontfamily' => "'Segoe UI', 'Segoe UI Emoji', ". - "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ". - "Helvetica, Arial, sans-serif", + 'fontfamily' => "-apple-system, system-ui, BlinkMacSystemFont, ". + "'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', ". + "'Helvetica Neue', Helvetica, Arial, sans-serif", // Drop Shadow 'dropshadow' => '0 2px 12px rgba(0, 0, 0, .20)', diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php index be039772c1..ff7e7f3668 100644 --- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php @@ -20,13 +20,13 @@ public function buildDefaultPostprocessor() { public function buildVariables() { return array( // Fonts - 'basefont' => "13px 'Segoe UI', 'Segoe UI Emoji', ". - "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ". - "Helvetica, Arial, sans-serif", + 'basefont' => "13px -apple-system, system-ui, BlinkMacSystemFont, ". + "'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', ". + "'Helvetica Neue', Helvetica, Arial, sans-serif", - 'fontfamily' => "'Segoe UI', 'Segoe UI Emoji', ". - "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ". - "Helvetica, Arial, sans-serif", + 'fontfamily' => "-apple-system, system-ui, BlinkMacSystemFont, ". + "'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', ". + "'Helvetica Neue', Helvetica, Arial, sans-serif", // Drop Shadow 'dropshadow' => '0 2px 12px rgba(0, 0, 0, .20)', diff --git a/src/applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php b/src/applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php index 4dbe561951..1bc4e1cb3c 100644 --- a/src/applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php @@ -14,9 +14,9 @@ public function getPostprocessorName() { public function buildVariables() { return array( - 'basefont' => "14px 'Segoe UI', 'Segoe UI Web Regular', ". - "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', Helvetica, ". - "Arial, sans-serif", + 'basefont' => "14px -apple-system, system-ui, BlinkMacSystemFont, ". + "'Segoe UI', 'Segoe UI Web Regular', 'Segoe UI Symbol', 'Lato', ". + "'Helvetica Neue', Helvetica, Arial, sans-serif", // Font Sizes 'biggestfontsize' => '16px', diff --git a/webroot/rsrc/css/application/config/config-template.css b/webroot/rsrc/css/application/config/config-template.css index 13ce56488a..8b212520fd 100644 --- a/webroot/rsrc/css/application/config/config-template.css +++ b/webroot/rsrc/css/application/config/config-template.css @@ -6,7 +6,8 @@ body { background: #f9f9f9; margin: 0; padding: 0; - font: 13px/1.231 'Segoe UI', 'Segoe UI Web Regular', 'Segoe UI Symbol', + font: 13px/1.231 -apple-system, system-ui, BlinkMacSystemFont, + 'Segoe UI', 'Segoe UI Web Regular', 'Segoe UI Symbol', 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: left; -webkit-text-size-adjust: none; diff --git a/webroot/rsrc/css/application/harbormaster/harbormaster.css b/webroot/rsrc/css/application/harbormaster/harbormaster.css index 9fdad43a42..2fa5af7b22 100644 --- a/webroot/rsrc/css/application/harbormaster/harbormaster.css +++ b/webroot/rsrc/css/application/harbormaster/harbormaster.css @@ -106,7 +106,8 @@ .harbormaster-log-expand-table td { vertical-align: middle; - font: 13px 'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', + font: 13px -apple-system, system-ui, BlinkMacSystemFont, + 'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', 'Helvetica Neue', Helvetica, Arial, sans-serif; } From 4aedc206c727b88fb071fdcc6c8488b58b8cdfc0 Mon Sep 17 00:00:00 2001 From: ekubischta Date: Sun, 9 Oct 2022 03:17:22 -0700 Subject: [PATCH 018/425] Update PassphraseCredential to support Mentions Summary: This revision allows us to see where passphrase credentials are mentioned in other parts of phabricator This is very useful to "What is this credential for?" If we can see where it was been mentioned Test Plan: Mentioned a passphrase and saw it in the transaction list Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: Matthew, Cigaryno, speck, tobiaswiese, valerio.bozzolan Differential Revision: https://we.phorge.it/D25032 --- src/__phutil_library_map__.php | 1 + src/applications/passphrase/storage/PassphraseCredential.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 02353b32c1..fec9ac13dc 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -8401,6 +8401,7 @@ 'PhabricatorApplicationTransactionInterface', 'PhabricatorPolicyInterface', 'PhabricatorFlaggableInterface', + 'PhabricatorMentionableInterface', 'PhabricatorSubscribableInterface', 'PhabricatorDestructibleInterface', 'PhabricatorSpacesInterface', diff --git a/src/applications/passphrase/storage/PassphraseCredential.php b/src/applications/passphrase/storage/PassphraseCredential.php index c470ea661f..d180fc6b31 100644 --- a/src/applications/passphrase/storage/PassphraseCredential.php +++ b/src/applications/passphrase/storage/PassphraseCredential.php @@ -5,6 +5,7 @@ final class PassphraseCredential extends PassphraseDAO PhabricatorApplicationTransactionInterface, PhabricatorPolicyInterface, PhabricatorFlaggableInterface, + PhabricatorMentionableInterface, PhabricatorSubscribableInterface, PhabricatorDestructibleInterface, PhabricatorSpacesInterface, From abb693962d327550215789e9ee0827943ca05cbe Mon Sep 17 00:00:00 2001 From: MacFan4000 Date: Fri, 11 Nov 2022 13:03:46 -0700 Subject: [PATCH 019/425] Update sprite sheets Summary: This fully updates some login provider icons. Somehow this didn't work fully on previous updates. I used --force this time. Test Plan: None just ran the script which regenerated the spritesheets. Reviewers: O1 Blessed Committers, #blessed_committers, avivey Reviewed By: O1 Blessed Committers, #blessed_committers, avivey Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Tags: #auth Differential Revision: https://we.phorge.it/D25055 --- webroot/rsrc/image/sprite-login-X2.png | Bin 29253 -> 37744 bytes webroot/rsrc/image/sprite-login.png | Bin 12792 -> 16010 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/webroot/rsrc/image/sprite-login-X2.png b/webroot/rsrc/image/sprite-login-X2.png index 2d3c1187456bb482a0aa5895e05947eb9eb355b9..24b3ed02b18d8cee35dd5d3d382763a2543993d0 100644 GIT binary patch literal 37744 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 literal 29253 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 Date: Fri, 11 Nov 2022 13:06:34 -0700 Subject: [PATCH 020/425] Fix broken file PHID extraction that causes Pholio uploads to crash Summary: A commit earlier this year modified the structure of the file upload transaction data value, by nesting the array of file upload PHIDs in another array. The `extractFilePHIDs` method was not updated to deal with that change though, therefore new mock uploads via Pholio would crash. This patch fixes that method so it can process the updated transaction data. Resolves T15105 Test Plan: Patched my live Phabricator installation with this fix and successfully uploaded new Pholio mockups. Reviewers: O1 Blessed Committers, Cigaryno, Matthew Reviewed By: O1 Blessed Committers, Cigaryno, Matthew Subscribers: Matthew, speck, tobiaswiese, valerio.bozzolan, Cigaryno Tags: #pholio Maniphest Tasks: T15105 Differential Revision: https://we.phorge.it/D25058 --- .../pholio/xaction/PholioImageFileTransaction.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/applications/pholio/xaction/PholioImageFileTransaction.php b/src/applications/pholio/xaction/PholioImageFileTransaction.php index e18fca28e2..ce1a0e9773 100644 --- a/src/applications/pholio/xaction/PholioImageFileTransaction.php +++ b/src/applications/pholio/xaction/PholioImageFileTransaction.php @@ -110,9 +110,11 @@ public function extractFilePHIDs($object, $value) { $new_phids = $value; $file_phids = array(); - foreach ($new_phids as $phid) { - $file_phids[] = $editor->loadPholioImage($object, $phid) - ->getFilePHID(); + foreach ($new_phids as $phids) { + foreach ($phids as $phid) { + $file_phids[] = $editor->loadPholioImage($object, $phid) + ->getFilePHID(); + } } return $file_phids; From dc558b5538cd9c4872eacbfa5ca108f1c6e4f40d Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 8 Dec 2022 15:32:03 -0700 Subject: [PATCH 021/425] Fix NULL pointer exception in some circumstances from Calendar's homepage Summary: After importing specific weird events, for example from Google Calendar (bleah), it can happen that the Calendar's homepage becomes broken. This was the Exception error shown to video: "Call to a member function getEventsBetween() on null" It was happening since this method can return NULL: PhabricatorCalendarEventQuery#newRecurrenceSet() This changeset verifies this condition from the Calendar's homepage. Closes T15136 Test Plan: I tried in my server. I've executed the syntax lint. On my local machine I was not able to run "arc diff" since it tries to connect to root@localhost for some reasons. Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: 0, Cigaryno, 20after4, speck, tobiaswiese, Matthew Tags: #calendar Maniphest Tasks: T15136 Differential Revision: https://we.phorge.it/D25060 --- .../calendar/query/PhabricatorCalendarEventQuery.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php index db50bb4d77..d20250e711 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -222,12 +222,16 @@ protected function loadPage() { $limit = $this->getRecurrenceLimit($event, $raw_limit); + // note that this can be NULL for some imported events $set = $event->newRecurrenceSet(); - $recurrences = $set->getEventsBetween( - $start_date, - $end_date, - $limit + 1); + $recurrences = array(); + if ($set) { + $recurrences = $set->getEventsBetween( + $start_date, + $end_date, + $limit + 1); + } // We're generating events from the beginning and then filtering them // here (instead of only generating events starting at the start date) From d5040f9a8f4978ba21160c6ccf6c35b28a3497b2 Mon Sep 17 00:00:00 2001 From: k__nard Date: Thu, 8 Dec 2022 15:40:54 -0700 Subject: [PATCH 022/425] updating twitch to latest api (Helix) Summary: api doc : https://dev.twitch.tv/docs/api/reference oauth2 doc : https://dev.twitch.tv/docs/authentication Test Plan: I have successfully setup OAuth2 authentication against Twitch Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Tags: #auth Maniphest Tasks: T15122 Differential Revision: https://we.phorge.it/D25057 --- .../auth/adapter/PhutilTwitchAuthAdapter.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/applications/auth/adapter/PhutilTwitchAuthAdapter.php b/src/applications/auth/adapter/PhutilTwitchAuthAdapter.php index dce2c7e2f0..364ac55eac 100644 --- a/src/applications/auth/adapter/PhutilTwitchAuthAdapter.php +++ b/src/applications/auth/adapter/PhutilTwitchAuthAdapter.php @@ -14,7 +14,7 @@ public function getAdapterDomain() { } public function getAccountID() { - return $this->getOAuthAccountData('_id'); + return $this->getOAuthAccountData('id'); } public function getAccountEmail() { @@ -22,11 +22,11 @@ public function getAccountEmail() { } public function getAccountName() { - return $this->getOAuthAccountData('name'); + return $this->getOAuthAccountData('login'); } public function getAccountImageURI() { - return $this->getOAuthAccountData('logo'); + return $this->getOAuthAccountData('profile_image_url'); } public function getAccountURI() { @@ -42,11 +42,11 @@ public function getAccountRealName() { } protected function getAuthenticateBaseURI() { - return 'https://api.twitch.tv/kraken/oauth2/authorize'; + return 'https://id.twitch.tv/oauth2/authorize'; } protected function getTokenBaseURI() { - return 'https://api.twitch.tv/kraken/oauth2/token'; + return 'https://id.twitch.tv/oauth2/token'; } public function getScope() { @@ -69,7 +69,7 @@ protected function loadOAuthAccountData() { return id(new PhutilTwitchFuture()) ->setClientID($this->getClientID()) ->setAccessToken($this->getAccessToken()) - ->setRawTwitchQuery('user') + ->setRawTwitchQuery('users') ->resolve(); } From 02a4f8b0c8f1279fc0040ad8077942fd8b0d948b Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 28 Nov 2022 21:54:54 +0100 Subject: [PATCH 023/425] Fix editing a Calendar import ICS URI Summary: Before this change it was only possible to create a Calendar import ICS URI. After this change it's possible to also edit already-existing elements. This change fixes this specific exception when visiting similar pages: /calendar/import/edit/5/ Argument 2 passed to PhabricatorCalendarImport::initializeNewCalendarImport() must be an instance of PhabricatorCalendarImportEngine, null given, called in phorge/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php on line 45 Before this change it was only theorically possible to edit the name, policies, etc. but not the URI. This change also introduces the ability to edit the specific ICS URI, in order to change legitimate parts of the URI, like the secret token. Closes T15137 Test Plan: I tested in my own installation with success lints. To test, visit the Calendar, create an import ICS URI, and then try to edit it again. It will work only after this change. I was not able to conclude the "arc diff" since it tries to connect to an unexisting database owned by root. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, Cigaryno, speck, tobiaswiese, Matthew Maniphest Tasks: T15137 Differential Revision: https://we.phorge.it/D25061 --- ...habricatorCalendarImportEditController.php | 29 ++++++++++++++++++- .../PhabricatorCalendarImportEditEngine.php | 15 ++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php b/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php index 0af9fa1540..9acbd0f2d2 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php @@ -8,7 +8,22 @@ public function handleRequest(AphrontRequest $request) { ->setController($this); $id = $request->getURIData('id'); - if (!$id) { + if ($id) { + + // edit a specific entry + + $calendar_import = self::queryImportByID($request, $id); + if (!$calendar_import) { + return new Aphront404Response(); + } + + // pass the correct import engine to build the response + $engine->setImportEngine($calendar_import->getEngine()); + + } else { + + // create an entry + $list_uri = $this->getApplicationURI('import/'); $import_type = $request->getStr('importType'); @@ -27,6 +42,18 @@ public function handleRequest(AphrontRequest $request) { return $engine->buildResponse(); } + private static function queryImportByID(AphrontRequest $request, int $id) { + return id(new PhabricatorCalendarImportQuery()) + ->setViewer($request->getViewer()) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + } + private function buildEngineTypeResponse($cancel_uri) { $import_engines = PhabricatorCalendarImportEngine::getAllImportEngines(); diff --git a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php index 7be3969671..9a4a7c0107 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php @@ -83,6 +83,12 @@ protected function buildCustomEditFields($object) { $engine = $object->getEngine(); $can_trigger = $engine->supportsTriggers($object); + // calendar URI import + // note that it can contains a secret token + // if we are here you have enough privileges to edit and see the value + $uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI; + $uri = $object->getParameter($uri_key); + $fields = array( id(new PhabricatorTextEditField()) ->setKey('name') @@ -94,6 +100,15 @@ protected function buildCustomEditFields($object) { ->setConduitTypeDescription(pht('New import name.')) ->setPlaceholder($object->getDisplayName()) ->setValue($object->getName()), + id(new PhabricatorTextEditField()) + ->setKey('uri') + ->setLabel(pht('URI')) + ->setDescription(pht('URI to import.')) + ->setTransactionType( + PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE) + ->setConduitDescription(pht('URI to import.')) + ->setConduitTypeDescription(pht('New URI.')) + ->setValue($uri), id(new PhabricatorBoolEditField()) ->setKey('disabled') ->setOptions(pht('Active'), pht('Disabled')) From 6a563a68b511f9eddcbbf941803cec970abcbab7 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Thu, 19 Jan 2023 01:58:09 -0800 Subject: [PATCH 024/425] Celerify Summary: Fix unit tests in master #cowboycommit Test Plan: arc unit Reviewers: O1 Blessed Committers! Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25062 --- resources/celerity/map.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 3815362684..80ba37f78e 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'd95915b7', + 'core.pkg.css' => 'f538846d', 'core.pkg.js' => '256dfd7b', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '609e63d4', @@ -184,7 +184,7 @@ 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', 'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6', 'rsrc/css/phui/workboards/phui-workpanel.css' => '3ae89b20', - 'rsrc/css/sprite-login.css' => '18b368a6', + 'rsrc/css/sprite-login.css' => '35d1510c', 'rsrc/css/sprite-tokens.css' => 'f1896dc5', 'rsrc/css/syntax/syntax-default.css' => '055fc231', 'rsrc/externals/d3/d3.min.js' => '9d068042', @@ -342,8 +342,8 @@ 'rsrc/image/phrequent_active.png' => 'de66dc50', 'rsrc/image/phrequent_inactive.png' => '79c61baf', 'rsrc/image/resize.png' => '9cc83373', - 'rsrc/image/sprite-login-X2.png' => '604545f6', - 'rsrc/image/sprite-login.png' => '7a001a9a', + 'rsrc/image/sprite-login-X2.png' => '269800ec', + 'rsrc/image/sprite-login.png' => 'a843f146', 'rsrc/image/sprite-tokens-X2.png' => '21621dd9', 'rsrc/image/sprite-tokens.png' => 'bede2580', 'rsrc/image/texture/card-gradient.png' => 'e6892cb4', @@ -904,7 +904,7 @@ 'project-triggers-css' => 'cd9c8bb9', 'project-view-css' => '2f7caa20', 'setup-issue-css' => '5eed85b2', - 'sprite-login-css' => '18b368a6', + 'sprite-login-css' => '35d1510c', 'sprite-tokens-css' => 'f1896dc5', 'syntax-default-css' => '055fc231', 'syntax-highlighting-css' => '548567f6', From 83672ba949fb3d72b9d9452c2687f70a23aa9cd4 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 10 Feb 2023 20:39:33 +0100 Subject: [PATCH 025/425] Allow to prefill name and description fields of a new Passphrase Summary: Allow to prefill name and description fields of a new Passphrase Closes T15142 Test Plan: I've visited this URL in my local test installation: /passphrase/edit/?type=password&username=user&name=super&description=mario And I was able to see the Name and Description fields prefilled, just like the username. I also tried to put the query string on an already-existing element and it was working as expected, so, without any prefilled value from the query string. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15142 Differential Revision: https://we.phorge.it/D25065 --- .../controller/PassphraseCredentialEditController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/applications/passphrase/controller/PassphraseCredentialEditController.php b/src/applications/passphrase/controller/PassphraseCredentialEditController.php index a3a346c20f..7247321648 100644 --- a/src/applications/passphrase/controller/PassphraseCredentialEditController.php +++ b/src/applications/passphrase/controller/PassphraseCredentialEditController.php @@ -45,6 +45,12 @@ public function handleRequest(AphrontRequest $request) { // Prefill username if provided. $credential->setUsername((string)$request->getStr('username')); + // Prefill name if provided. + $credential->setName((string)$request->getStr('name')); + + // Prefill description if provided. + $credential->setDescription((string)$request->getStr('description')); + if (!$request->getStr('isInitialized')) { $type->didInitializeNewCredential($viewer, $credential); } From 20fb93d1a4be471f5e3e401c8ee4d2b9755aa556 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 11 Feb 2023 11:09:39 +0100 Subject: [PATCH 026/425] Add small documentation details Summary: Add some small Diviner documentation details like: * mention PHP 8.1 (thanks https://we.phorge.it/Q18) * mention the fact that you need multiple databases (it may be not obvious from the startup) * mention MariaDB and not just MySQL (thanks MariaDB community!) * mention "Administrator" and not just "User" for the "Instructions for installing, configuring, and using Phorge." I was not bold enough to do more changes. Test Plan: I tested locally and Diviner does not look bad. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25064 --- src/docs/book/user.book | 2 +- .../configuration/configuration_guide.diviner | 1 + src/docs/user/installation_guide.diviner | 15 ++++++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/docs/book/user.book b/src/docs/book/user.book index 449acd4ffc..b5b6dac7b1 100644 --- a/src/docs/book/user.book +++ b/src/docs/book/user.book @@ -1,6 +1,6 @@ { "name": "phorge", - "title": "Phorge User Documentation", + "title": "Phorge Administrator and User Documentation", "short": "User Docs", "preface": "Instructions for installing, configuring, and using Phorge.", "root": "../../../", diff --git a/src/docs/user/configuration/configuration_guide.diviner b/src/docs/user/configuration/configuration_guide.diviner index 7798ff0e0b..1fde967a95 100644 --- a/src/docs/user/configuration/configuration_guide.diviner +++ b/src/docs/user/configuration/configuration_guide.diviner @@ -11,6 +11,7 @@ If you haven't, see @{article:Installation Guide}. The next steps are: - Configure your webserver (Apache, nginx, or lighttpd). + - Configure the databases. - Access Phorge with your browser. - Follow the instructions to complete setup. diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index ded0e4c93a..6db0df4d7c 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -15,10 +15,17 @@ Phorge, you will need: - a domain name (like `phorge.example.com`); - basic sysadmin skills; - Apache, nginx, or another webserver; - - PHP, MySQL, and Git. + - PHP; + - MySQL (you will need a server with multiple databases); + - git The remainder of this document details these requirements. +You may be interested also in preparing these optional stuff: + + - have valid SMTP parameters for outgoing email notifications; + - having nothing listening on port 22, to then setup a SSH+git server + Installation Requirements ========================= @@ -71,7 +78,9 @@ Beyond an operating system, you will need **a webserver**. You will also need: - **MySQL**: You need MySQL. We strongly recommend MySQL 5.5 or newer. - - **PHP**: You need PHP 5.5 or newer. + You will need a server with multiple databases. + - **PHP**: You need PHP 5.5 or newer. Note that PHP 8.1 and above are not + fully supported. You'll probably also need a **domain name**. In particular, you should read this note: @@ -106,7 +115,7 @@ Here's a general description of what you need to install: - git (usually called "git" in package management systems) - Apache (usually "httpd" or "apache2") (or nginx) - - MySQL Server (usually "mysqld" or "mysql-server") + - MySQL Server (usually "mysqld" or "mysql-server" or "mariadb-server") - PHP (usually "php") - Required PHP extensions: mbstring, iconv, mysql (or mysqli), curl, pcntl (these might be something like "php-mysql" or "php5-mysqlnd") From 36dba82224de1b64bf571ebae5e944ce8a02e7c2 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 11 Feb 2023 11:10:58 +0100 Subject: [PATCH 027/425] Show the API summary on the top of each Conduit API page Summary: Show the API summary on the top of each Conduit API page. Before this change, the summary was only displayed from the Conduit APIs list. Closes T15141 Test Plan: I've opened 20 random Conduit API pages from the web interface and I checked that now there is an amazing "Summary" field. I also double-checked that there were not green peppers. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15141 Differential Revision: https://we.phorge.it/D25063 --- .../controller/PhabricatorConduitConsoleController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index 70a2253084..b3dfa805a2 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -247,6 +247,10 @@ private function buildMethodProperties(ConduitAPIMethod $method) { )); } + $view->addProperty( + pht('Summary'), + $method->getMethodSummary()); + $view->addProperty( pht('Returns'), $method->getReturnType()); From 53c31b7b135cb17bd2d14169b81743931f1a6542 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 3 Mar 2023 12:12:01 +0100 Subject: [PATCH 028/425] Fix middle-click, CTRL+click, right-click etc. on Typehead search results Summary: Fix middle-click, CTRL+click, right-click etc. on Typehead search results. Closes T15149 Test Plan: Try the following actions on various typeheads: - right-click - middle-click - CTRL+click - normal click Demonstration video (2M) showing After patch (on localhost) and Before patch (here on we.phorge.it), where I middle-click and normal-click on menu entries: {F256610} Notes: - the middle click now works (opening in new tab) - the CTRL+click (or "command" key + click) now works (opening in new tab) - the right click now opens the context menu (previously broken) - the normal click should just click (as usual) Try on: - search results while typing in main search bar - search results when editing a Task Tags / assigned to, etc. - try to click on other weird places - $$$ Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15149 Differential Revision: https://we.phorge.it/D25069 --- resources/celerity/map.php | 18 +++++++++--------- .../javelin/lib/control/typeahead/Typeahead.js | 13 ++++++++++++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 80ba37f78e..71761a45e6 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,7 +10,7 @@ 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', 'core.pkg.css' => 'f538846d', - 'core.pkg.js' => '256dfd7b', + 'core.pkg.js' => '6a2c22c2', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '609e63d4', 'differential.pkg.js' => 'c60bec1b', @@ -265,7 +265,7 @@ 'rsrc/externals/javelin/lib/__tests__/behavior.js' => '8426ebeb', 'rsrc/externals/javelin/lib/behavior.js' => '1b6acc2a', 'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '89a1ae3a', - 'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => 'a4356cde', + 'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => 'd96e47a4', 'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => 'a241536a', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '22ee68a5', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '23387297', @@ -731,7 +731,7 @@ 'javelin-sound' => 'd4cc2d2a', 'javelin-stratcom' => '0889b835', 'javelin-tokenizer' => '89a1ae3a', - 'javelin-typeahead' => 'a4356cde', + 'javelin-typeahead' => 'd96e47a4', 'javelin-typeahead-composite-source' => '22ee68a5', 'javelin-typeahead-normalizer' => 'a241536a', 'javelin-typeahead-ondemand-source' => '23387297', @@ -1800,12 +1800,6 @@ 'javelin-workflow', 'phabricator-draggable-list', ), - 'a4356cde' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-vector', - 'javelin-util', - ), 'a43ae2ae' => array( 'javelin-install', 'javelin-dom', @@ -2100,6 +2094,12 @@ 'javelin-util', 'phabricator-shaped-request', ), + 'd96e47a4' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-vector', + 'javelin-util', + ), 'da15d3dc' => array( 'phui-oi-list-view-css', ), diff --git a/webroot/rsrc/externals/javelin/lib/control/typeahead/Typeahead.js b/webroot/rsrc/externals/javelin/lib/control/typeahead/Typeahead.js index 5875bf9ea6..e971ae1495 100644 --- a/webroot/rsrc/externals/javelin/lib/control/typeahead/Typeahead.js +++ b/webroot/rsrc/externals/javelin/lib/control/typeahead/Typeahead.js @@ -84,8 +84,19 @@ JX.install('Typeahead', { 'mousedown', 'tag:a', JX.bind(this, function(e) { - if (!e.isRightButton()) { + if (e.isNormalMouseEvent()) { this._choose(e.getNode('tag:a')); + } else { + // fix the middle-click and any non-normal mouse event + // in order to have an "open in a new tab" that just works natively + // or any other browser action that is supposed to be there. + // + // Probably this is one of the specific cases where kill() has + // sense instead of just stop(), since there are not much chances + // that another event listener had anything else to do + // during non-normal mousedown/click events. + // https://we.phorge.it/T15149 + e.kill(); } })); From 66192a5b832cff2856809736ab6e4696595a0934 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 3 Mar 2023 12:15:48 +0100 Subject: [PATCH 029/425] Fix "Undefined index: icon" when visiting Search Servers using MySQL Summary: Fix "Undefined index: icon" when visiting Search Servers using MySQL NOTE: This patch just fixes the exception at my best but this section probably deserves more improvement to show a better default. Closes T15155 Test Plan: - use the default Search Server configuration (that is MySQL) - open the page Search Servers (/config/cluster/search/) - verify that it does not explode anymore but it displays something unuseful Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15155 Differential Revision: https://we.phorge.it/D25070 --- .../PhabricatorConfigClusterSearchController.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/applications/config/controller/services/PhabricatorConfigClusterSearchController.php b/src/applications/config/controller/services/PhabricatorConfigClusterSearchController.php index 5e877d6b95..706d6b4fa9 100644 --- a/src/applications/config/controller/services/PhabricatorConfigClusterSearchController.php +++ b/src/applications/config/controller/services/PhabricatorConfigClusterSearchController.php @@ -60,8 +60,20 @@ private function renderStatusView($service) { foreach ($service->getHosts() as $host) { try { + // Default status icon + // + // At the moment the default status is shown also when + // you just use MySQL as search server. So, on MySQL it + // shows "Unknown" even if probably it should says "Active". + // If you have time, please improve the MySQL getConnectionStatus() + // to return something more useful than this default. + $default_status = array( + 'icon' => 'fa-question-circle', + 'color' => 'blue', + 'label' => pht('Unknown'), + ); $status = $host->getConnectionStatus(); - $status = idx($status_map, $status, array()); + $status = idx($status_map, $status, $default_status); } catch (Exception $ex) { $status['icon'] = 'fa-times'; $status['label'] = pht('Connection Error'); From 9623e667458821f696eb92e9dac5a2c9720af7af Mon Sep 17 00:00:00 2001 From: MacFan4000 Date: Fri, 3 Mar 2023 19:27:30 +0100 Subject: [PATCH 030/425] replace usage of each() with foreach() for smtp Summary: this replaces uses with warning suppression of each() (depreccated in PHP7, removed in PHP8) with foreach Test Plan: can verify that these chages do work (tested on my own install) Reviewers: O1 Blessed Committers, #blessed_committers, dsadad, avivey, valerio.bozzolan Reviewed By: O1 Blessed Committers, #blessed_committers, dsadad, avivey, valerio.bozzolan Subscribers: avivey, Cigaryno, speck, tobiaswiese, valerio.bozzolan, Matthew Differential Revision: https://we.phorge.it/D25059 --- externals/phpmailer/class.smtp.php | 1626 ++++++++++++++-------------- 1 file changed, 813 insertions(+), 813 deletions(-) diff --git a/externals/phpmailer/class.smtp.php b/externals/phpmailer/class.smtp.php index c2ca1cb3b8..1b238eec95 100644 --- a/externals/phpmailer/class.smtp.php +++ b/externals/phpmailer/class.smtp.php @@ -1,814 +1,814 @@ -smtp_conn = 0; - $this->error = null; - $this->helo_rply = null; - - $this->do_debug = 0; - } - - ///////////////////////////////////////////////// - // CONNECTION FUNCTIONS - ///////////////////////////////////////////////// - - /** - * Connect to the server specified on the port specified. - * If the port is not specified use the default SMTP_PORT. - * If tval is specified then a connection will try and be - * established with the server for that number of seconds. - * If tval is not specified the default is 30 seconds to - * try on the connection. - * - * SMTP CODE SUCCESS: 220 - * SMTP CODE FAILURE: 421 - * @access public - * @return bool - */ - public function Connect($host, $port = 0, $tval = 30) { - // set the error val to null so there is no confusion - $this->error = null; - - // make sure we are __not__ connected - if($this->connected()) { - // already connected, generate error - $this->error = array("error" => "Already connected to a server"); - return false; - } - - if(empty($port)) { - $port = $this->SMTP_PORT; - } - - // connect to the smtp server - $this->smtp_conn = @fsockopen($host, // the host of the server - $port, // the port to use - $errno, // error number if any - $errstr, // error message if any - $tval); // give up after ? secs - // verify we connected properly - if(empty($this->smtp_conn)) { - $this->error = array("error" => "Failed to connect to server", - "errno" => $errno, - "errstr" => $errstr); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '
'; - } - return false; - } - - // SMTP server can take longer to respond, give longer timeout for first read - // Windows does not have support for this timeout function - if(substr(PHP_OS, 0, 3) != "WIN") - socket_set_timeout($this->smtp_conn, $tval, 0); - - // get any announcement - $announce = $this->get_lines(); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $announce . $this->CRLF . '
'; - } - - return true; - } - - /** - * Initiate a TLS communication with the server. - * - * SMTP CODE 220 Ready to start TLS - * SMTP CODE 501 Syntax error (no parameters allowed) - * SMTP CODE 454 TLS not available due to temporary reason - * @access public - * @return bool success - */ - public function StartTLS() { - $this->error = null; # to avoid confusion - - if(!$this->connected()) { - $this->error = array("error" => "Called StartTLS() without being connected"); - return false; - } - - fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 220) { - $this->error = - array("error" => "STARTTLS not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - // Begin encrypted connection - if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { - return false; - } - - return true; - } - - /** - * Performs SMTP authentication. Must be run after running the - * Hello() method. Returns true if successfully authenticated. - * @access public - * @return bool - */ - public function Authenticate($username, $password) { - // Start authentication - fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => "AUTH not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - // Send encoded username - fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => "Username not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - // Send encoded password - fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 235) { - $this->error = - array("error" => "Password not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - return true; - } - - /** - * Returns true if connected to a server otherwise false - * @access public - * @return bool - */ - public function Connected() { - if(!empty($this->smtp_conn)) { - $sock_status = socket_get_status($this->smtp_conn); - if($sock_status["eof"]) { - // the socket is valid but we are not connected - if($this->do_debug >= 1) { - echo "SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"; - } - $this->Close(); - return false; - } - return true; // everything looks good - } - return false; - } - - /** - * Closes the socket and cleans up the state of the class. - * It is not considered good to use this function without - * first trying to use QUIT. - * @access public - * @return void - */ - public function Close() { - $this->error = null; // so there is no confusion - $this->helo_rply = null; - if(!empty($this->smtp_conn)) { - // close the connection and cleanup - fclose($this->smtp_conn); - $this->smtp_conn = 0; - } - } - - ///////////////////////////////////////////////// - // SMTP COMMANDS - ///////////////////////////////////////////////// - - /** - * Issues a data command and sends the msg_data to the server - * finializing the mail transaction. $msg_data is the message - * that is to be send with the headers. Each header needs to be - * on a single line followed by a with the message headers - * and the message body being seperated by and additional . - * - * Implements rfc 821: DATA - * - * SMTP CODE INTERMEDIATE: 354 - * [data] - * . - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 552,554,451,452 - * SMTP CODE FAILURE: 451,554 - * SMTP CODE ERROR : 500,501,503,421 - * @access public - * @return bool - */ - public function Data($msg_data) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Data() without being connected"); - return false; - } - - fputs($this->smtp_conn,"DATA" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 354) { - $this->error = - array("error" => "DATA command not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - /* the server is ready to accept data! - * according to rfc 821 we should not send more than 1000 - * including the CRLF - * characters on a single line so we will break the data up - * into lines by \r and/or \n then if needed we will break - * each of those into smaller lines to fit within the limit. - * in addition we will be looking for lines that start with - * a period '.' and append and additional period '.' to that - * line. NOTE: this does not count towards limit. - */ - - // normalize the line breaks so we know the explode works - $msg_data = str_replace("\r\n","\n",$msg_data); - $msg_data = str_replace("\r","\n",$msg_data); - $lines = explode("\n",$msg_data); - - /* we need to find a good way to determine is headers are - * in the msg_data or if it is a straight msg body - * currently I am assuming rfc 822 definitions of msg headers - * and if the first field of the first line (':' sperated) - * does not contain a space then it _should_ be a header - * and we can process all lines before a blank "" line as - * headers. - */ - - $field = substr($lines[0],0,strpos($lines[0],":")); - $in_headers = false; - if(!empty($field) && !strstr($field," ")) { - $in_headers = true; - } - - $max_line_length = 998; // used below; set here for ease in change - - while(list(,$line) = @each($lines)) { - $lines_out = null; - if($line == "" && $in_headers) { - $in_headers = false; - } - // ok we need to break this line up into several smaller lines - while(strlen($line) > $max_line_length) { - $pos = strrpos(substr($line,0,$max_line_length)," "); - - // Patch to fix DOS attack - if(!$pos) { - $pos = $max_line_length - 1; - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos); - } else { - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos + 1); - } - - /* if processing headers add a LWSP-char to the front of new line - * rfc 822 on long msg headers - */ - if($in_headers) { - $line = "\t" . $line; - } - } - $lines_out[] = $line; - - // send the lines to the server - while(list(,$line_out) = @each($lines_out)) { - if(strlen($line_out) > 0) - { - if(substr($line_out, 0, 1) == ".") { - $line_out = "." . $line_out; - } - } - fputs($this->smtp_conn,$line_out . $this->CRLF); - } - } - - // message data has been sent - fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => "DATA not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - return true; - } - - /** - * Sends the HELO command to the smtp server. - * This makes sure that we and the server are in - * the same known state. - * - * Implements from rfc 821: HELO - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500, 501, 504, 421 - * @access public - * @return bool - */ - public function Hello($host = '') { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Hello() without being connected"); - return false; - } - - // if hostname for HELO was not specified send default - if(empty($host)) { - // determine appropriate default to send to server - $host = "localhost"; - } - - // Send extended hello first (RFC 2821) - if(!$this->SendHello("EHLO", $host)) { - if(!$this->SendHello("HELO", $host)) { - return false; - } - } - - return true; - } - - /** - * Sends a HELO/EHLO command. - * @access private - * @return bool - */ - private function SendHello($hello, $host) { - fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER: " . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => $hello . " not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - $this->helo_rply = $rply; - - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. - * - * Implements rfc 821: MAIL FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,421 - * @access public - * @return bool - */ - public function Mail($from) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Mail() without being connected"); - return false; - } - - $useVerp = ($this->do_verp ? "XVERP" : ""); - fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => "MAIL not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - return true; - } - - /** - * Sends the quit command to the server and then closes the socket - * if there is no error or the $close_on_error argument is true. - * - * Implements from rfc 821: QUIT - * - * SMTP CODE SUCCESS: 221 - * SMTP CODE ERROR : 500 - * @access public - * @return bool - */ - public function Quit($close_on_error = true) { - $this->error = null; // so there is no confusion - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Quit() without being connected"); - return false; - } - - // send the quit command to the server - fputs($this->smtp_conn,"quit" . $this->CRLF); - - // get any good-bye messages - $byemsg = $this->get_lines(); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '
'; - } - - $rval = true; - $e = null; - - $code = substr($byemsg,0,3); - if($code != 221) { - // use e as a tmp var cause Close will overwrite $this->error - $e = array("error" => "SMTP server rejected quit command", - "smtp_code" => $code, - "smtp_rply" => substr($byemsg,4)); - $rval = false; - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '
'; - } - } - - if(empty($e) || $close_on_error) { - $this->Close(); - } - - return $rval; - } - - /** - * Sends the command RCPT to the SMTP server with the TO: argument of $to. - * Returns true if the recipient was accepted false if it was rejected. - * - * Implements from rfc 821: RCPT TO: - * - * SMTP CODE SUCCESS: 250,251 - * SMTP CODE FAILURE: 550,551,552,553,450,451,452 - * SMTP CODE ERROR : 500,501,503,421 - * @access public - * @return bool - */ - public function Recipient($to) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Recipient() without being connected"); - return false; - } - - fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250 && $code != 251) { - $this->error = - array("error" => "RCPT not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - return true; - } - - /** - * Sends the RSET command to abort and transaction that is - * currently in progress. Returns true if successful false - * otherwise. - * - * Implements rfc 821: RSET - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500,501,504,421 - * @access public - * @return bool - */ - public function Reset() { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Reset() without being connected"); - return false; - } - - fputs($this->smtp_conn,"RSET" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => "RSET failed", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. This command - * will send the message to the users terminal if they are logged - * in and send them an email. - * - * Implements rfc 821: SAML FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,502,421 - * @access public - * @return bool - */ - public function SendAndMail($from) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called SendAndMail() without being connected"); - return false; - } - - fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => "SAML not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - return true; - } - - /** - * This is an optional command for SMTP that this class does not - * support. This method is here to make the RFC821 Definition - * complete for this class and __may__ be implimented in the future - * - * Implements from rfc 821: TURN - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 502 - * SMTP CODE ERROR : 500, 503 - * @access public - * @return bool - */ - public function Turn() { - $this->error = array("error" => "This method, TURN, of the SMTP ". - "is not implemented"); - if($this->do_debug >= 1) { - echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
'; - } - return false; - } - - /** - * Get the current error - * @access public - * @return array - */ - public function getError() { - return $this->error; - } - - ///////////////////////////////////////////////// - // INTERNAL FUNCTIONS - ///////////////////////////////////////////////// - - /** - * Read in as many lines as possible - * either before eof or socket timeout occurs on the operation. - * With SMTP we can tell if we have more lines to read if the - * 4th character is '-' symbol. If it is a space then we don't - * need to read anything else. - * @access private - * @return string - */ - private function get_lines() { - $data = ""; - while($str = @fgets($this->smtp_conn,515)) { - if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
'; - echo "SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
'; - } - $data .= $str; - if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
'; - } - // if 4th character is a space, we are done reading, break the loop - if(substr($str,3,1) == " ") { break; } - } - return $data; - } - -} - +smtp_conn = 0; + $this->error = null; + $this->helo_rply = null; + + $this->do_debug = 0; + } + + ///////////////////////////////////////////////// + // CONNECTION FUNCTIONS + ///////////////////////////////////////////////// + + /** + * Connect to the server specified on the port specified. + * If the port is not specified use the default SMTP_PORT. + * If tval is specified then a connection will try and be + * established with the server for that number of seconds. + * If tval is not specified the default is 30 seconds to + * try on the connection. + * + * SMTP CODE SUCCESS: 220 + * SMTP CODE FAILURE: 421 + * @access public + * @return bool + */ + public function Connect($host, $port = 0, $tval = 30) { + // set the error val to null so there is no confusion + $this->error = null; + + // make sure we are __not__ connected + if($this->connected()) { + // already connected, generate error + $this->error = array("error" => "Already connected to a server"); + return false; + } + + if(empty($port)) { + $port = $this->SMTP_PORT; + } + + // connect to the smtp server + $this->smtp_conn = @fsockopen($host, // the host of the server + $port, // the port to use + $errno, // error number if any + $errstr, // error message if any + $tval); // give up after ? secs + // verify we connected properly + if(empty($this->smtp_conn)) { + $this->error = array("error" => "Failed to connect to server", + "errno" => $errno, + "errstr" => $errstr); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '
'; + } + return false; + } + + // SMTP server can take longer to respond, give longer timeout for first read + // Windows does not have support for this timeout function + if(substr(PHP_OS, 0, 3) != "WIN") + socket_set_timeout($this->smtp_conn, $tval, 0); + + // get any announcement + $announce = $this->get_lines(); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $announce . $this->CRLF . '
'; + } + + return true; + } + + /** + * Initiate a TLS communication with the server. + * + * SMTP CODE 220 Ready to start TLS + * SMTP CODE 501 Syntax error (no parameters allowed) + * SMTP CODE 454 TLS not available due to temporary reason + * @access public + * @return bool success + */ + public function StartTLS() { + $this->error = null; # to avoid confusion + + if(!$this->connected()) { + $this->error = array("error" => "Called StartTLS() without being connected"); + return false; + } + + fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 220) { + $this->error = + array("error" => "STARTTLS not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + // Begin encrypted connection + if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { + return false; + } + + return true; + } + + /** + * Performs SMTP authentication. Must be run after running the + * Hello() method. Returns true if successfully authenticated. + * @access public + * @return bool + */ + public function Authenticate($username, $password) { + // Start authentication + fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "AUTH not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + // Send encoded username + fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "Username not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + // Send encoded password + fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 235) { + $this->error = + array("error" => "Password not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + return true; + } + + /** + * Returns true if connected to a server otherwise false + * @access public + * @return bool + */ + public function Connected() { + if(!empty($this->smtp_conn)) { + $sock_status = socket_get_status($this->smtp_conn); + if($sock_status["eof"]) { + // the socket is valid but we are not connected + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"; + } + $this->Close(); + return false; + } + return true; // everything looks good + } + return false; + } + + /** + * Closes the socket and cleans up the state of the class. + * It is not considered good to use this function without + * first trying to use QUIT. + * @access public + * @return void + */ + public function Close() { + $this->error = null; // so there is no confusion + $this->helo_rply = null; + if(!empty($this->smtp_conn)) { + // close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = 0; + } + } + + ///////////////////////////////////////////////// + // SMTP COMMANDS + ///////////////////////////////////////////////// + + /** + * Issues a data command and sends the msg_data to the server + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being seperated by and additional . + * + * Implements rfc 821: DATA + * + * SMTP CODE INTERMEDIATE: 354 + * [data] + * . + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 552,554,451,452 + * SMTP CODE FAILURE: 451,554 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + public function Data($msg_data) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Data() without being connected"); + return false; + } + + fputs($this->smtp_conn,"DATA" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 354) { + $this->error = + array("error" => "DATA command not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + /* the server is ready to accept data! + * according to rfc 821 we should not send more than 1000 + * including the CRLF + * characters on a single line so we will break the data up + * into lines by \r and/or \n then if needed we will break + * each of those into smaller lines to fit within the limit. + * in addition we will be looking for lines that start with + * a period '.' and append and additional period '.' to that + * line. NOTE: this does not count towards limit. + */ + + // normalize the line breaks so we know the explode works + $msg_data = str_replace("\r\n","\n",$msg_data); + $msg_data = str_replace("\r","\n",$msg_data); + $lines = explode("\n",$msg_data); + + /* we need to find a good way to determine is headers are + * in the msg_data or if it is a straight msg body + * currently I am assuming rfc 822 definitions of msg headers + * and if the first field of the first line (':' sperated) + * does not contain a space then it _should_ be a header + * and we can process all lines before a blank "" line as + * headers. + */ + + $field = substr($lines[0],0,strpos($lines[0],":")); + $in_headers = false; + if(!empty($field) && !strstr($field," ")) { + $in_headers = true; + } + + $max_line_length = 998; // used below; set here for ease in change + + foreach($lines as $line) { + $lines_out = null; + if($line == "" && $in_headers) { + $in_headers = false; + } + // ok we need to break this line up into several smaller lines + while(strlen($line) > $max_line_length) { + $pos = strrpos(substr($line,0,$max_line_length)," "); + + // Patch to fix DOS attack + if(!$pos) { + $pos = $max_line_length - 1; + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos); + } else { + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos + 1); + } + + /* if processing headers add a LWSP-char to the front of new line + * rfc 822 on long msg headers + */ + if($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + // send the lines to the server + foreach($lines_out as $line_out) { + if(strlen($line_out) > 0) + { + if(substr($line_out, 0, 1) == ".") { + $line_out = "." . $line_out; + } + } + fputs($this->smtp_conn,$line_out . $this->CRLF); + } + } + + // message data has been sent + fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "DATA not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * Sends the HELO command to the smtp server. + * This makes sure that we and the server are in + * the same known state. + * + * Implements from rfc 821: HELO + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 501, 504, 421 + * @access public + * @return bool + */ + public function Hello($host = '') { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Hello() without being connected"); + return false; + } + + // if hostname for HELO was not specified send default + if(empty($host)) { + // determine appropriate default to send to server + $host = "localhost"; + } + + // Send extended hello first (RFC 2821) + if(!$this->SendHello("EHLO", $host)) { + if(!$this->SendHello("HELO", $host)) { + return false; + } + } + + return true; + } + + /** + * Sends a HELO/EHLO command. + * @access private + * @return bool + */ + private function SendHello($hello, $host) { + fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER: " . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => $hello . " not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + $this->helo_rply = $rply; + + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. + * + * Implements rfc 821: MAIL FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,421 + * @access public + * @return bool + */ + public function Mail($from) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Mail() without being connected"); + return false; + } + + $useVerp = ($this->do_verp ? "XVERP" : ""); + fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "MAIL not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * Sends the quit command to the server and then closes the socket + * if there is no error or the $close_on_error argument is true. + * + * Implements from rfc 821: QUIT + * + * SMTP CODE SUCCESS: 221 + * SMTP CODE ERROR : 500 + * @access public + * @return bool + */ + public function Quit($close_on_error = true) { + $this->error = null; // so there is no confusion + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Quit() without being connected"); + return false; + } + + // send the quit command to the server + fputs($this->smtp_conn,"quit" . $this->CRLF); + + // get any good-bye messages + $byemsg = $this->get_lines(); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '
'; + } + + $rval = true; + $e = null; + + $code = substr($byemsg,0,3); + if($code != 221) { + // use e as a tmp var cause Close will overwrite $this->error + $e = array("error" => "SMTP server rejected quit command", + "smtp_code" => $code, + "smtp_rply" => substr($byemsg,4)); + $rval = false; + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '
'; + } + } + + if(empty($e) || $close_on_error) { + $this->Close(); + } + + return $rval; + } + + /** + * Sends the command RCPT to the SMTP server with the TO: argument of $to. + * Returns true if the recipient was accepted false if it was rejected. + * + * Implements from rfc 821: RCPT TO: + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,552,553,450,451,452 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + public function Recipient($to) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Recipient() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250 && $code != 251) { + $this->error = + array("error" => "RCPT not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * Sends the RSET command to abort and transaction that is + * currently in progress. Returns true if successful false + * otherwise. + * + * Implements rfc 821: RSET + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500,501,504,421 + * @access public + * @return bool + */ + public function Reset() { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Reset() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RSET" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "RSET failed", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * + * Implements rfc 821: SAML FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + * @access public + * @return bool + */ + public function SendAndMail($from) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called SendAndMail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "SAML not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * This is an optional command for SMTP that this class does not + * support. This method is here to make the RFC821 Definition + * complete for this class and __may__ be implimented in the future + * + * Implements from rfc 821: TURN + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 502 + * SMTP CODE ERROR : 500, 503 + * @access public + * @return bool + */ + public function Turn() { + $this->error = array("error" => "This method, TURN, of the SMTP ". + "is not implemented"); + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
'; + } + return false; + } + + /** + * Get the current error + * @access public + * @return array + */ + public function getError() { + return $this->error; + } + + ///////////////////////////////////////////////// + // INTERNAL FUNCTIONS + ///////////////////////////////////////////////// + + /** + * Read in as many lines as possible + * either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * @access private + * @return string + */ + private function get_lines() { + $data = ""; + while($str = @fgets($this->smtp_conn,515)) { + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
'; + echo "SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
'; + } + $data .= $str; + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
'; + } + // if 4th character is a space, we are done reading, break the loop + if(substr($str,3,1) == " ") { break; } + } + return $data; + } + +} + ?> \ No newline at end of file From 3487ee444a885de558c8586f137cd7d477e27d65 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 14 Mar 2023 20:24:32 +0100 Subject: [PATCH 031/425] Fix regression in new confirmation Dialog Summary: Fix a JavaScript regression encapsulating the problematic part into an `if`. Other minor changes: - dedicate a variable for the confirmation messages to improve i18n in the future (but also to avoid 80 characters and make lint happy) - replace `confirm` with `window.confirm` (to make lint happy) Ref T15034 Ref D25015 Test Plan: - surf on your local Phorge - no JavaScript errors in console Reviewers: bekay, Ekubischta, O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15034 Differential Revision: https://we.phorge.it/D25076 --- .../rsrc/externals/javelin/lib/Workflow.js | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/webroot/rsrc/externals/javelin/lib/Workflow.js b/webroot/rsrc/externals/javelin/lib/Workflow.js index 7d63a3408a..6d91682dbc 100644 --- a/webroot/rsrc/externals/javelin/lib/Workflow.js +++ b/webroot/rsrc/externals/javelin/lib/Workflow.js @@ -404,14 +404,18 @@ JX.install('Workflow', { } } - var form = JX.DOM.scry(this._root, 'form', 'jx-dialog'); - if (form.length) { - JX.DOM.listen(form[0], 'keydown', null, function(e) { - if (e.getSpecialKey()) { - return; - } - JX.Stratcom.addSigil(form[0], 'dialog-keydown'); - }); + // Only when the response is a Dialog, check if the user + // is quitting with pending changes + if (this._root) { + var form = JX.DOM.scry(this._root, 'form', 'jx-dialog'); + if (form.length) { + JX.DOM.listen(form[0], 'keydown', null, function(e) { + if (e.getSpecialKey()) { + return; + } + JX.Stratcom.addSigil(form[0], 'dialog-keydown'); + }); + } } }, _push : function() { @@ -546,13 +550,20 @@ JX.install('Workflow', { return; } - var form = JX.DOM.scry(active._root, 'form', 'jx-dialog'); - if ( - form.length && - JX.Stratcom.hasSigil(form[0], 'dialog-keydown') && - !confirm('Form data may have changed. Are you sure you want to close this dialog?') - ) { - return; + // Only when the response is a Dialog, check if the user + // is quitting with pending changes + if (active._root) { + var form = JX.DOM.scry(active._root, 'form', 'jx-dialog'); + var confirmMsg = + 'Form data may have changed. ' + + 'Are you sure you want to close this dialog?'; + if ( + form.length && + JX.Stratcom.hasSigil(form[0], 'dialog-keydown') && + !window.confirm(confirmMsg) + ) { + return; + } } JX.Workflow._pop(); From 5cba56182f39a983ce7fd2a6a6b280d28b2619da Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 16 Mar 2023 15:27:34 +0100 Subject: [PATCH 032/425] Diviner: fix "Javascript" -> "JavaScript" and minor change Test Plan: - look at Diviner and say "Java..." - Diviner will look at you saying "...Script" - you land, satisfied If Diviner says "...script" instead (lowercase "S"), abandon the ship. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25075 --- src/docs/book/flavor.book | 2 +- .../flavor/javascript_object_array.diviner | 32 +++++++++++++------ src/docs/flavor/javascript_pitfalls.diviner | 16 +++++----- src/docs/flavor/php_pitfalls.diviner | 2 +- src/docs/flavor/project_history.diviner | 2 +- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/docs/book/flavor.book b/src/docs/book/flavor.book index 4404e94bf7..c9a80fae92 100644 --- a/src/docs/book/flavor.book +++ b/src/docs/book/flavor.book @@ -21,7 +21,7 @@ ], "groups": { "javascript": { - "name": "Javascript" + "name": "JavaScript" }, "lore": { "name": "Phorge Lore" diff --git a/src/docs/flavor/javascript_object_array.diviner b/src/docs/flavor/javascript_object_array.diviner index cdba029e9a..113dbb1000 100644 --- a/src/docs/flavor/javascript_object_array.diviner +++ b/src/docs/flavor/javascript_object_array.diviner @@ -1,13 +1,13 @@ -@title Javascript Object and Array +@title JavaScript Object and Array @group javascript -This document describes the behaviors of Object and Array in Javascript, and +This document describes the behaviors of Object and Array in JavaScript, and a specific approach to their use which produces basically reasonable language behavior. = Primitives = -Javascript has two native datatype primitives, Object and Array. Both are +JavaScript has two native datatype primitives, Object and Array. Both are classes, so you can use `new` to instantiate new objects and arrays: COUNTEREXAMPLE @@ -43,11 +43,11 @@ and Array are both classes, but "object" is also a primitive type. Object is = Objects are Maps, Arrays are Lists = PHP has a single `array` datatype which behaves like as both map and a list, -and a common mistake is to treat Javascript arrays (or objects) in the same way. +and a common mistake is to treat JavaScript arrays (or objects) in the same way. **Don't do this.** It sort of works until it doesn't. Instead, learn how -Javascript's native datatypes work and use them properly. +JavaScript's native datatypes work and use them properly. -In Javascript, you should think of Objects as maps ("dictionaries") and Arrays +In JavaScript, you should think of Objects as maps ("dictionaries") and Arrays as lists ("vectors"). You store keys-value pairs in a map, and store ordered values in a list. So, @@ -58,7 +58,13 @@ store key-value pairs in Objects. species: 'zebra' }; - console.log(o.name); + o.paws = 4; + + o['numberOfEars'] = 2; + + console.log(o.name); + console.log(o.paws); + console.log(o.numberOfEars); ...and store ordered values in Arrays. @@ -71,8 +77,14 @@ Don't store key-value pairs in Arrays and don't expect Objects to be ordered. var a = []; a['name'] = 'Hubert'; // No! Don't do this! -This technically works because Arrays are Objects and you think everything is -fine and dandy, but it won't do what you want and will burn you. +Technically, both work because Arrays //are// Objects and you think everything +is fine and dandy, but it won't do what you want and will burn you. For example, +using `.length` will play tricks on you. + +In short, trust me: + +* use `[]` only to create a stack of consecutive elements numerically indexed +* use `{}` to create associative maps ("associative arrays") = Iterating over Maps and Lists = @@ -140,7 +152,7 @@ The correct way to deal with this is: continue; } f(list[ii]); - } + } Avoid sparse arrays if possible. diff --git a/src/docs/flavor/javascript_pitfalls.diviner b/src/docs/flavor/javascript_pitfalls.diviner index 9276ae4712..fbc2eae05c 100644 --- a/src/docs/flavor/javascript_pitfalls.diviner +++ b/src/docs/flavor/javascript_pitfalls.diviner @@ -1,12 +1,12 @@ -@title Javascript Pitfalls +@title JavaScript Pitfalls @group javascript -This document discusses pitfalls and flaws in the Javascript language, and how +This document discusses pitfalls and flaws in the JavaScript language, and how to avoid, work around, or at least understand them. = Implicit Semicolons = -Javascript tries to insert semicolons if you forgot them. This is a pretty +JavaScript tries to insert semicolons if you forgot them. This is a pretty horrible idea. Notably, it can mask syntax errors by transforming subexpressions on their own lines into statements with no effect: @@ -46,11 +46,11 @@ you can pass `arguments` to Function.prototype.apply() without converting it. There is essentially only one reasonable, consistent way to use these primitives but it is not obvious. Navigate these troubled waters with -@{article:Javascript Object and Array}. +@{article:JavaScript Object and Array}. = typeof null == "object" = -This statement is true in Javascript: +This statement is true in JavaScript: typeof null == 'object' @@ -58,9 +58,9 @@ This is pretty much a bug in the language that can never be fixed now. = Number, String, and Boolean objects = -Like Java, Javascript has primitive versions of number, string, and boolean, +Like Java, JavaScript has primitive versions of number, string, and boolean, and object versions. In Java, there's some argument for this distinction. In -Javascript, it's pretty much completely worthless and the behavior of these +JavaScript, it's pretty much completely worthless and the behavior of these objects is wrong. String and Boolean in particular are essentially unusable: lang=js @@ -83,5 +83,5 @@ Number.prototype, etc.) and their logical behavior is at best absurd and at worst strictly wrong. **Never use** `new Number()`, `new String()` or `new Boolean()` unless -your Javascript is God Tier and you are absolutely sure you know what you are +your JavaScript is God Tier and you are absolutely sure you know what you are doing. diff --git a/src/docs/flavor/php_pitfalls.diviner b/src/docs/flavor/php_pitfalls.diviner index 3f4be45dd7..e15ca9e2bb 100644 --- a/src/docs/flavor/php_pitfalls.diviner +++ b/src/docs/flavor/php_pitfalls.diviner @@ -256,7 +256,7 @@ echo $obj->flavor; // Outputs 'coconut'. echo get_class($obj); // Outputs 'stdClass'. ``` -This is occasionally useful, mostly to force an object to become a Javascript +This is occasionally useful, mostly to force an object to become a JavaScript dictionary (vs a list) when passed to `json_encode()`. = Invoking `new` With an Argument Vector is Really Hard = diff --git a/src/docs/flavor/project_history.diviner b/src/docs/flavor/project_history.diviner index 1159304cda..6b51e203ed 100644 --- a/src/docs/flavor/project_history.diviner +++ b/src/docs/flavor/project_history.diviner @@ -34,7 +34,7 @@ it also gained a lot of performance problems, usability issues, and bugs. Through 2007 and 2008 Evan worked mostly on frontend and support infrastructure; among other things, he wrote a static resource management system called Haste. In 2009 Evan worked on the Facebook Lite site, where he built the Javelin -Javascript library and an MVC-flavored framework called Alite. +JavaScript library and an MVC-flavored framework called Alite. But by early 2010, Diffcamp was in pretty bad shape. Two years of having random features grafted onto it without real direction had left it slow and difficult From 4f356838beda6e03bbb05c94144cbc2df25ed38e Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 17 Mar 2023 08:29:35 +0100 Subject: [PATCH 033/425] FileUpload: show File page instead of binary file Summary: This modification improves a single link shown to the user. The goal is to make it easier to change file permissions or file name or whatever just after the upload of the file itself. Before this change, doing that was difficult because clicking on the popup would send you back to the binary file only. Closes T15165 Test Plan: - upload a file using drag & drop (for example in a comment) - click on the popup's link - you see the File page, not the binary file Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15165 Differential Revision: https://we.phorge.it/D25074 --- webroot/rsrc/js/core/FileUpload.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/webroot/rsrc/js/core/FileUpload.js b/webroot/rsrc/js/core/FileUpload.js index 22ba33f97a..e2c3530cbd 100644 --- a/webroot/rsrc/js/core/FileUpload.js +++ b/webroot/rsrc/js/core/FileUpload.js @@ -38,6 +38,20 @@ JX.install('PhabricatorFileUpload', { return this; }, + /** + * Get the File Monogram - like 'F123' + */ + getMonogram: function() { + return 'F' + this.getID(); + }, + + /** + * Get the File page URI - like '/F123' + */ + getPageURI: function() { + return '/' + this.getMonogram(); + }, + setChunks: function(chunks) { var chunk; for (var ii = 0; ii < chunks.length; ii++) { @@ -101,7 +115,7 @@ JX.install('PhabricatorFileUpload', { switch (this.getStatus()) { case 'done': - var link = JX.$N('a', {href: this.getURI()}, 'F' + this.getID()); + var link = JX.$N('a', {href: this.getPageURI()}, this.getMonogram()); content = [ JX.$N('strong', {}, ['Upload Complete (', link, ')']), From b33e373503c6f64118ec77bb34dc8224d54da4e3 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 17 Mar 2023 13:13:13 +0100 Subject: [PATCH 034/425] Drag & Drop: set a link as external Summary: Rest assured: external links remain evil, by default. Don't adopt them randomly by induction. Whether you believe it or not, this specific external link merited some deep thoughts on Phorge: - https://we.phorge.it/T15172 So, whenever you use a mouse, a finger, or whenever we have a confirmation dialog or not to prevent onblur disasters, this change is probably consistent with common expectations. Having said, external links remain evil - by default. Closes T15172 Test Plan: - Drag & Drop a File on a Remarkup text - click on the link inside the popup - it opens in a new tab (without risk of form loss) Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15172 Differential Revision: https://we.phorge.it/D25077 --- webroot/rsrc/js/core/FileUpload.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/webroot/rsrc/js/core/FileUpload.js b/webroot/rsrc/js/core/FileUpload.js index e2c3530cbd..ef5089a56e 100644 --- a/webroot/rsrc/js/core/FileUpload.js +++ b/webroot/rsrc/js/core/FileUpload.js @@ -115,7 +115,19 @@ JX.install('PhabricatorFileUpload', { switch (this.getStatus()) { case 'done': - var link = JX.$N('a', {href: this.getPageURI()}, this.getMonogram()); + + // In this case the File upload was successful + + var linkAttr = {}; + linkAttr.href = this.getPageURI(); + + // External links are evil as default. + // Believe it or not, but some Phorge users brainstormed + // for one hour for this specific target="_blank". + // https://we.phorge.it/T15172 + linkAttr.target = '_blank'; + + var link = JX.$N('a', linkAttr, this.getMonogram()); content = [ JX.$N('strong', {}, ['Upload Complete (', link, ')']), From c6f56b8221bae80c316a81e0b6cf7f45b9eaba85 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 18 Mar 2023 14:57:15 +0100 Subject: [PATCH 035/425] Workboard: fix CTRL+click on "Create Task" and "Edit Task" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: The CTRL+click now opens the links in a new tab, like any other normal link opened with CTRL+click. Note that the middle-click was already working. Closes T15157 Test Plan: - visit a Workboard - column > menu > mouse on "Create Task" - CTRL+click: open in new tab (→ now works) - normal click: open the pop-up (→ still works) - middle-click: open in new tab (→ still works) - column > single Task > mouse on "Edit" - CTRL+click: open in new tab (→ now works) - normal click: open the pop-up (→ still works) - middle-click: open in new tab (→ still works) Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15157 Differential Revision: https://we.phorge.it/D25072 --- .../js/application/projects/WorkboardController.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/webroot/rsrc/js/application/projects/WorkboardController.js b/webroot/rsrc/js/application/projects/WorkboardController.js index da5d177bb9..1b38638f3d 100644 --- a/webroot/rsrc/js/application/projects/WorkboardController.js +++ b/webroot/rsrc/js/application/projects/WorkboardController.js @@ -151,6 +151,13 @@ JX.install('WorkboardController', { }, _onaddcard: function(e) { + + // Allow CTRL+click and maybe other actions + if(!e.isNormalMouseEvent()) { + e.stop(); + return; + } + // We want the 'boards-dropdown-menu' behavior to see this event and // close the dropdown, but don't want to follow the link. e.prevent(); @@ -176,6 +183,13 @@ JX.install('WorkboardController', { }, _oneditcard: function(e) { + + // Allow CTRL+click and maybe other actions + if(!e.isNormalMouseEvent()) { + e.stop(); + return; + } + e.kill(); var column_node = e.getNode('project-column'); From 9bc3c16b6e7599ad513aec7347c61cc6677fe6d5 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 25 Mar 2023 12:06:22 +0100 Subject: [PATCH 036/425] Fix information page about Read-only status Summary: This change fixes the following page on your Phorge/Phabricator: /readonly/config/ If you visit that page (with or without read-only mode), the following crash will happen: Call to undefined method PlatformSymbols::getPlatformServerSymbol() Fixes T15185 Probably the regression was introduced in this point: rP4d3f0dc7c727e6fdc0e8cba0e8e8f2b8ef1bd16c This change also renamed "administrator" to "Administrator" in the messages from that specific page, both because I have great respect for Administrators and also because that is how this word is spelled in most other places in Phorge. Test Plan: - Visit the page /readonly/config/ - It does not crash anymore and you can see that beautiful message that - it seems - no person in the world has ever seen since a whole year. Take a good look at this page: it is a precious treasure. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15185 Differential Revision: https://we.phorge.it/D25091 --- resources/celerity/map.php | 82 +++++++++---------- .../PhabricatorSystemReadOnlyController.php | 6 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 71761a45e6..9323cc070d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,7 +10,7 @@ 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', 'core.pkg.css' => 'f538846d', - 'core.pkg.js' => '6a2c22c2', + 'core.pkg.js' => '66c49ca1', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '609e63d4', 'differential.pkg.js' => 'c60bec1b', @@ -257,7 +257,7 @@ 'rsrc/externals/javelin/lib/URI.js' => '2e255291', 'rsrc/externals/javelin/lib/Vector.js' => 'e9c80beb', 'rsrc/externals/javelin/lib/WebSocket.js' => 'fdc13e4e', - 'rsrc/externals/javelin/lib/Workflow.js' => 'd7ba6915', + 'rsrc/externals/javelin/lib/Workflow.js' => 'cc1553f3', 'rsrc/externals/javelin/lib/__tests__/Cookie.js' => 'ca686f71', 'rsrc/externals/javelin/lib/__tests__/DOM.js' => '4566e249', 'rsrc/externals/javelin/lib/__tests__/JSON.js' => '710377ae', @@ -265,7 +265,7 @@ 'rsrc/externals/javelin/lib/__tests__/behavior.js' => '8426ebeb', 'rsrc/externals/javelin/lib/behavior.js' => '1b6acc2a', 'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '89a1ae3a', - 'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => 'd96e47a4', + 'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '0507519c', 'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => 'a241536a', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '22ee68a5', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '23387297', @@ -423,7 +423,7 @@ 'rsrc/js/application/projects/WorkboardCard.js' => '0392a5d8', 'rsrc/js/application/projects/WorkboardCardTemplate.js' => '84f82dad', 'rsrc/js/application/projects/WorkboardColumn.js' => 'c3d24e63', - 'rsrc/js/application/projects/WorkboardController.js' => 'b9d0c2f3', + 'rsrc/js/application/projects/WorkboardController.js' => '7474d31f', 'rsrc/js/application/projects/WorkboardDropEffect.js' => '8e0aa661', 'rsrc/js/application/projects/WorkboardHeader.js' => '111bfd2d', 'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'ebe83a6b', @@ -453,7 +453,7 @@ 'rsrc/js/core/DragAndDropFileUpload.js' => '4370900d', 'rsrc/js/core/DraggableList.js' => '0169e425', 'rsrc/js/core/Favicon.js' => '7930776a', - 'rsrc/js/core/FileUpload.js' => 'ab85e184', + 'rsrc/js/core/FileUpload.js' => '331676ea', 'rsrc/js/core/Hovercard.js' => '6199f752', 'rsrc/js/core/HovercardList.js' => 'de4b4919', 'rsrc/js/core/KeyboardShortcut.js' => '1a844c06', @@ -731,7 +731,7 @@ 'javelin-sound' => 'd4cc2d2a', 'javelin-stratcom' => '0889b835', 'javelin-tokenizer' => '89a1ae3a', - 'javelin-typeahead' => 'd96e47a4', + 'javelin-typeahead' => '0507519c', 'javelin-typeahead-composite-source' => '22ee68a5', 'javelin-typeahead-normalizer' => 'a241536a', 'javelin-typeahead-ondemand-source' => '23387297', @@ -751,12 +751,12 @@ 'javelin-workboard-card' => '0392a5d8', 'javelin-workboard-card-template' => '84f82dad', 'javelin-workboard-column' => 'c3d24e63', - 'javelin-workboard-controller' => 'b9d0c2f3', + 'javelin-workboard-controller' => '7474d31f', 'javelin-workboard-drop-effect' => '8e0aa661', 'javelin-workboard-header' => '111bfd2d', 'javelin-workboard-header-template' => 'ebe83a6b', 'javelin-workboard-order-template' => '03e8891f', - 'javelin-workflow' => 'd7ba6915', + 'javelin-workflow' => 'cc1553f3', 'maniphest-report-css' => '3d53188b', 'maniphest-task-edit-css' => '272daa84', 'maniphest-task-summary-css' => '61d1667e', @@ -787,7 +787,7 @@ 'phabricator-fatal-config-template-css' => 'e689dbbd', 'phabricator-favicon' => '7930776a', 'phabricator-feed-css' => 'd8b6e3f8', - 'phabricator-file-upload' => 'ab85e184', + 'phabricator-file-upload' => '331676ea', 'phabricator-flag-css' => '2b77be8d', 'phabricator-keyboard-shortcut' => '1a844c06', 'phabricator-keyboard-shortcut-manager' => '81debc48', @@ -965,6 +965,12 @@ 'javelin-dom', 'javelin-workflow', ), + '0507519c' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-vector', + 'javelin-util', + ), '05d290ef' => array( 'javelin-install', 'javelin-util', @@ -1207,6 +1213,11 @@ 'javelin-stratcom', 'javelin-dom', ), + '331676ea' => array( + 'javelin-install', + 'javelin-dom', + 'phabricator-notification', + ), 34450586 => array( 'javelin-color', 'javelin-install', @@ -1589,6 +1600,16 @@ 'javelin-behavior', 'javelin-dom', ), + '7474d31f' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + 'javelin-stratcom', + 'javelin-workflow', + 'phabricator-drag-and-drop-file-upload', + 'javelin-workboard-board', + ), '78bc5d94' => array( 'javelin-behavior', 'javelin-uri', @@ -1856,11 +1877,6 @@ 'javelin-json', 'phuix-form-control-view', ), - 'ab85e184' => array( - 'javelin-install', - 'javelin-dom', - 'phabricator-notification', - ), 'ac10c917' => array( 'javelin-behavior', 'javelin-dom', @@ -1983,16 +1999,6 @@ 'javelin-uri', 'phabricator-notification', ), - 'b9d0c2f3' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - 'javelin-stratcom', - 'javelin-workflow', - 'phabricator-drag-and-drop-file-upload', - 'javelin-workboard-board', - ), 'bf159129' => array( 'phui-inline-comment-view-css', ), @@ -2034,6 +2040,17 @@ 'javelin-workflow', 'javelin-json', ), + 'cc1553f3' => array( + 'javelin-stratcom', + 'javelin-request', + 'javelin-dom', + 'javelin-vector', + 'javelin-install', + 'javelin-util', + 'javelin-mask', + 'javelin-uri', + 'javelin-routable', + ), 'cc2c5de5' => array( 'javelin-install', 'phuix-button-view', @@ -2063,17 +2080,6 @@ 'd4cc2d2a' => array( 'javelin-install', ), - 'd7ba6915' => array( - 'javelin-stratcom', - 'javelin-request', - 'javelin-dom', - 'javelin-vector', - 'javelin-install', - 'javelin-util', - 'javelin-mask', - 'javelin-uri', - 'javelin-routable', - ), 'd7d3ba75' => array( 'javelin-dom', 'javelin-util', @@ -2094,12 +2100,6 @@ 'javelin-util', 'phabricator-shaped-request', ), - 'd96e47a4' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-vector', - 'javelin-util', - ), 'da15d3dc' => array( 'phui-oi-list-view-css', ), diff --git a/src/applications/system/controller/PhabricatorSystemReadOnlyController.php b/src/applications/system/controller/PhabricatorSystemReadOnlyController.php index e83fe67b0e..2d00d1f405 100644 --- a/src/applications/system/controller/PhabricatorSystemReadOnlyController.php +++ b/src/applications/system/controller/PhabricatorSystemReadOnlyController.php @@ -16,7 +16,7 @@ public function handleRequest(AphrontRequest $request) { case PhabricatorEnv::READONLY_CONFIG: $title = pht('Administrative Read-Only Mode'); $body[] = pht( - 'An administrator has placed this server into read-only mode.'); + 'An Administrator has placed this server into read-only mode.'); $body[] = pht( 'This mode may be used to perform temporary maintenance, test '. 'configuration, or archive an installation permanently.'); @@ -26,7 +26,7 @@ public function handleRequest(AphrontRequest $request) { 'has been turned on by rolling your chair away from your desk and '. 'yelling "Hey! Why is %s in read-only mode??!" using '. 'your very loudest outside voice.', - PlatformSymbols::getPlatformServerSymbol()); + PlatformSymbols::getPlatformServerName()); $body[] = pht( 'This mode is active because it is enabled in the configuration '. 'option "%s".', @@ -110,7 +110,7 @@ public function handleRequest(AphrontRequest $request) { if ($viewer->getIsAdmin()) { $body[] = pht( - 'As an administrator, you can review status information from the '. + 'As an Administrator, you can review status information from the '. '%s control panel. This may provide more information about the '. 'current state of affairs.', phutil_tag( From 42a8b73adbaa1bb241abb71e9fe52f368223c952 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 25 Mar 2023 14:20:53 +0100 Subject: [PATCH 037/425] Feed Differential retitled: do not repeat the title twice Summary: This is a cosmetic modification for a specific Feed. https://we.phorge.it/T15183 If you retitle a Diff (and you do nothing else), its Feed is somehow too much verbose: ... retitled NEW_TITLE from OLD_TITLE to NEW_TITLE In this specific case, the Feed now becomes shorter so that the NEW_TITLE is not repeated twice: ... retitled NEW_TITLE from OLD_TITLE Note that, if the title changes again, the Feed naturally comes back as before so to mention the change and obviously without any repetition. Example: ... retitled CURRENT_TITLE from OLD_TITLE to NEW_TITLE Closes T15183 Test Plan: - take a Diff named "OLD_TITLE" and retitle to "NEW_TITLE" - visit the homepage and see "retitled NEW_TITLE from OLD_TITLE" (this means the new behavior works) - retitle the Diff from "NEW_TITLE" to "CURRENT_TITLE" - visit the homepage again and see "retitled CURRENT_TITLE from OLD_TITLE to NEW_TITLE" (this means the old behavior still works) Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15183 Differential Revision: https://we.phorge.it/D25094 --- .../xaction/DifferentialRevisionTitleTransaction.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/applications/differential/xaction/DifferentialRevisionTitleTransaction.php b/src/applications/differential/xaction/DifferentialRevisionTitleTransaction.php index 50d00e1892..485aec0b5c 100644 --- a/src/applications/differential/xaction/DifferentialRevisionTitleTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionTitleTransaction.php @@ -23,6 +23,17 @@ public function getTitle() { } public function getTitleForFeed() { + $obj = $this->getObject(); + + // To avoid verbose messages we mention the current title just once + if ($obj && $obj->getTitle() === $this->getNewValue()) { + return pht( + '%s retitled %s from %s', + $this->renderAuthor(), + $this->renderObject(), + $this->renderOldValue()); + } + return pht( '%s retitled %s from %s to %s.', $this->renderAuthor(), From 03c9bf575e4314f86a70276e2d3764bbc4f0bd79 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sun, 26 Mar 2023 22:08:05 +0200 Subject: [PATCH 038/425] PHP 8.2: fixes for deprecated use of ${var} in strings Summary: Note that PHP 8.2 implemented this deprecation: Using ${var} in strings is deprecated, use {$var} instead This change fixes some known cases. Ref T15196 Test Plan: - I checked with my big eyes that everything is as it should be Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15196 Differential Revision: https://we.phorge.it/D25098 --- .../diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php | 2 +- src/applications/herald/phid/HeraldTranscriptPHIDType.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php index c6ae6dbd2f..5eb20d2f7e 100644 --- a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php @@ -55,7 +55,7 @@ protected function getGitResult(ConduitAPIRequest $request) { $limit = $request->getValue('limit'); if (strlen($against_hash)) { - $commit_range = "${against_hash}..${commit_hash}"; + $commit_range = "{$against_hash}..{$commit_hash}"; } else { $commit_range = $commit_hash; } diff --git a/src/applications/herald/phid/HeraldTranscriptPHIDType.php b/src/applications/herald/phid/HeraldTranscriptPHIDType.php index 8ba3a0254c..12625d0c1a 100644 --- a/src/applications/herald/phid/HeraldTranscriptPHIDType.php +++ b/src/applications/herald/phid/HeraldTranscriptPHIDType.php @@ -35,7 +35,7 @@ public function loadHandles( $id = $xscript->getID(); $handle->setName(pht('Transcript %s', $id)); - $handle->setURI("/herald/transcript/${id}/"); + $handle->setURI("/herald/transcript/$id/"); } } From d98c47041f137e6a69978f5a4d6a14c708c53648 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 27 Mar 2023 07:06:27 +0200 Subject: [PATCH 039/425] PHP 8.2: fix deprecated creation of dynamic properties Summary: This change fixes a typo that, in PHP 8.2, causes this exception: Creation of dynamic property PhabricatorAuthPasswordException::$confirmError is deprecated Closes T15201 Test Plan: - I checked that "error" was spelled with two "r" Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15201 Differential Revision: https://we.phorge.it/D25100 --- .../auth/password/PhabricatorAuthPasswordException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/password/PhabricatorAuthPasswordException.php b/src/applications/auth/password/PhabricatorAuthPasswordException.php index d4b13bb10c..fa749722af 100644 --- a/src/applications/auth/password/PhabricatorAuthPasswordException.php +++ b/src/applications/auth/password/PhabricatorAuthPasswordException.php @@ -4,7 +4,7 @@ final class PhabricatorAuthPasswordException extends Exception { private $passwordError; - private $confirmErorr; + private $confirmError; public function __construct( $message, From 5e2b3677157889104a7e540d7772a04f13164037 Mon Sep 17 00:00:00 2001 From: dylsss Date: Mon, 27 Mar 2023 08:18:32 +0200 Subject: [PATCH 040/425] Workboard: improve the Create Task link to mention the Project slug Summary: This expands a specific link in a specific menu of Workboard Columns. You may never notice this difference unless you like to open links in another tab. If you go to a Workboard, and you open its context menu, and you hover your mouse on the Create Task action, you see this URL or a similar one: http://example.com/maniphest/task/edit/form/default/ After this change, you see this URL or a similar one: http://example.com/maniphest/task/edit/form/default/?tags=test You see that the PhutilURI class was used to add the Project slug to the 'tags' query param so that users can still open the URL in a new tab and have the form prefilled with the Project Tag. Closes T15147 Test Plan: - visit a Workboard - open the context menu of a Column (the pencil icon) - see that the Create Task link has the Project slug is in the URL of its prefilled form Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15147 Differential Revision: https://we.phorge.it/D25068 --- .../controller/PhabricatorProjectBoardViewController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index 13a75c5a73..d254936c1a 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -696,7 +696,8 @@ private function buildColumnMenu( $column_items[] = id(new PhabricatorActionView()) ->setIcon($spec['icon']) ->setName($spec['name']) - ->setHref($spec['uri']) + ->setHref(id(new PhutilURI($spec['uri'])) + ->replaceQueryParam('tags', $project->getPrimarySlug())) ->setDisabled($spec['disabled']) ->addSigil('column-add-task') ->setMetadata( From 38e3692d7bda187b724b5355d11120c1bbfb23e3 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 27 Mar 2023 20:50:37 +0200 Subject: [PATCH 041/425] Workboard: in some cases, increase menu opacity (accessibility) Summary: This change contains a very minimal graphical change for Workboards with a custom background. https://we.phorge.it/T15186 After this change, if your Workboard has a custom background color, the opacity of the floating menu of the Column is not 0.9 but 0.95, so it's increased a bit. The problem with the previous value, is that when you open the menu, you see too much text underneath, and this could worsen the readability. Now also probably, but less I hope. Closes T15186 Test Plan: - Workboard > Change Background Color > Pick a nice color - Backlog > Edit - Enjoy the extra 0.05 of opacity of that menu - NEVER ACCEPT IF YOUR EYES DETECT JUST A 0.04999998 OF EXTRA OPACITY Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15186 Differential Revision: https://we.phorge.it/D25088 --- webroot/rsrc/css/phui/workboards/phui-workboard-color.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css index 9973d64979..5aae48647a 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css +++ b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css @@ -27,7 +27,7 @@ } .phui-workboard-color .phuix-dropdown-menu { - background-color: rgba({$alphawhite},.9); + background-color: rgba({$alphawhite},.95); } .phui-workboard-color .phui-workpanel-view .phui-box-grey { From f0618d54e8b5a483e95fd4c488cae15a29be0500 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 27 Mar 2023 20:51:04 +0200 Subject: [PATCH 042/425] Installation Guide: mention that git 2.5.0 (2015) is needed server-side Summary: Note that the git version 2.5.0 is surely already assumed as required as implicit fact, so it's just better to clarify this in the documentation to prevent weird situations and unclear related exceptions. Note that if you have seen this Phabricator/Phorge error in your installation: This path was a submodule at R123:5378198ea123asdlol This is probably that kind of weird errors that will be fixed just updating your git version to 2.5.0, so that the git escape sequence ('--') is correctly parsed by git. Example of command that will fail with outdated git versions: git cat-file -t -- : If you do not want to update git, but you want to fix, you can convert that command to this one, manually patching your installation: git cat-file -t : Related material: - https://we.phorge.it/T15179 - https://unix.stackexchange.com/a/740621/85666 - https://github.com/git/git/blob/6f9504c48ea59951a2aa3ee17e7c7fc36285e225/Documentation/RelNotes/2.5.0.txt Wontfix T15179 Test Plan: - open Diviner page "Installation Guide" - see that the git version is not explicit - yup! Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15179 Differential Revision: https://we.phorge.it/D25081 --- src/docs/user/installation_guide.diviner | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index 6db0df4d7c..7306155917 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -81,6 +81,8 @@ You will also need: You will need a server with multiple databases. - **PHP**: You need PHP 5.5 or newer. Note that PHP 8.1 and above are not fully supported. + - **git**: You need git 2.5.0 or newer on the server. + No particular version is needed on your clients. You'll probably also need a **domain name**. In particular, you should read this note: From 780e86acf83b709f6cf10a1a393a4cc022c3d922 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 27 Mar 2023 20:51:34 +0200 Subject: [PATCH 043/425] Herald Rule Creation Page: fix Back button in some cases Summary: If you want to create a new Herald Rule, sometime, like for new "Object" Herald Rules, the Back button just sends you to the very same page you already were. The risk is that an employee could receive a specific instruction, such as "Hey Alfreda, Go back, now" and at this point Alfreda goes into an incredible loop continuing to click the Back button until the duration of the universe (since Phorge is so stable that it's able to handle that "Back spike" forever - really). https://we.phorge.it/T15184 Note that this also tries to avoid to change the base URI to just go Back. For example, before this change, the Back button was trying to send from /create/ to /new/, but apparently they are just aliases. Closes T15184 Test Plan: - Visit the page /herald/create/?adapter=commit - Click on "Object" - Click on "Back" - Verify that you only went back one screen, and not just forward in time Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15184 Differential Revision: https://we.phorge.it/D25087 --- src/applications/herald/controller/HeraldNewController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/herald/controller/HeraldNewController.php b/src/applications/herald/controller/HeraldNewController.php index 09b7e62c6e..93d4503754 100644 --- a/src/applications/herald/controller/HeraldNewController.php +++ b/src/applications/herald/controller/HeraldNewController.php @@ -333,8 +333,8 @@ private function newTargetForm( $cancel_params = $params; unset($cancel_params['type']); - $cancel_uri = $this->getApplicationURI('new/'); - $cancel_uri = new PhutilURI($cancel_uri, $params); + $cancel_uri = $this->getApplicationURI('create/'); + $cancel_uri = new PhutilURI($cancel_uri, $cancel_params); $form->appendChild( id(new AphrontFormSubmitControl()) From 5eac69b79dbb37b5efa29c3d7383605ea64426e0 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 27 Mar 2023 20:54:59 +0200 Subject: [PATCH 044/425] Ponder: strike code of resolved questions in query results Summary: After this change, search results with resolved questions are striked, just like any Closed Maniphest Tasks is already striked, etc. https://we.phorge.it/T15166 There is still the same thing to be done at Remarkup. Ref T15166 Test Plan: - go in the page /ponder/query/all/ and see that closed questions are now striked Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15166 Differential Revision: https://we.phorge.it/D25086 --- src/__phutil_library_map__.php | 2 ++ .../ponder/constants/PonderQuestionStatus.php | 10 ++++++++ .../query/PonderQuestionSearchEngine.php | 3 +++ .../ponder/storage/PonderQuestion.php | 8 ++++++ .../PonderQuestionStatusTestCase.php | 25 +++++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 src/applications/ponder/storage/__tests__/PonderQuestionStatusTestCase.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index fec9ac13dc..6a6bf37aed 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -5843,6 +5843,7 @@ 'PonderQuestionSearchEngine' => 'applications/ponder/query/PonderQuestionSearchEngine.php', 'PonderQuestionStatus' => 'applications/ponder/constants/PonderQuestionStatus.php', 'PonderQuestionStatusController' => 'applications/ponder/controller/PonderQuestionStatusController.php', + 'PonderQuestionStatusTestCase' => 'applications/ponder/storage/__tests__/PonderQuestionStatusTestCase.php', 'PonderQuestionStatusTransaction' => 'applications/ponder/xaction/PonderQuestionStatusTransaction.php', 'PonderQuestionTitleTransaction' => 'applications/ponder/xaction/PonderQuestionTitleTransaction.php', 'PonderQuestionTransaction' => 'applications/ponder/storage/PonderQuestionTransaction.php', @@ -12757,6 +12758,7 @@ 'PonderQuestionSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PonderQuestionStatus' => 'PonderConstants', 'PonderQuestionStatusController' => 'PonderController', + 'PonderQuestionStatusTestCase' => 'PhutilTestCase', 'PonderQuestionStatusTransaction' => 'PonderQuestionTransactionType', 'PonderQuestionTitleTransaction' => 'PonderQuestionTransactionType', 'PonderQuestionTransaction' => 'PhabricatorModularTransaction', diff --git a/src/applications/ponder/constants/PonderQuestionStatus.php b/src/applications/ponder/constants/PonderQuestionStatus.php index 55e433bf25..52497bef37 100644 --- a/src/applications/ponder/constants/PonderQuestionStatus.php +++ b/src/applications/ponder/constants/PonderQuestionStatus.php @@ -86,4 +86,14 @@ public static function getQuestionStatusClosedMap() { ); } + /** + * Check whenever a Ponder question status is Closed + * + * @param $status string + * @return bool + */ + public static function isQuestionStatusClosed($status) { + return in_array($status, self::getQuestionStatusClosedMap(), true); + } + } diff --git a/src/applications/ponder/query/PonderQuestionSearchEngine.php b/src/applications/ponder/query/PonderQuestionSearchEngine.php index 4cba28b93b..ecca1a91e5 100644 --- a/src/applications/ponder/query/PonderQuestionSearchEngine.php +++ b/src/applications/ponder/query/PonderQuestionSearchEngine.php @@ -157,6 +157,9 @@ protected function renderResultList( 'Asked by %s', $handles[$question->getAuthorPHID()]->renderLink())); + // Render Closed Questions as striked in query results + $item->setDisabled($question->isStatusClosed()); + $item->addAttribute( pht( '%s Answer(s)', diff --git a/src/applications/ponder/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php index 40ec9fbee8..b5034f94d8 100644 --- a/src/applications/ponder/storage/PonderQuestion.php +++ b/src/applications/ponder/storage/PonderQuestion.php @@ -137,6 +137,14 @@ public function getMarkupField() { return self::MARKUP_FIELD_CONTENT; } + /** + * Check whenever this Question has whatever closed status + * + * @return bool + */ + public function isStatusClosed() { + return PonderQuestionStatus::isQuestionStatusClosed($this->status); + } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ diff --git a/src/applications/ponder/storage/__tests__/PonderQuestionStatusTestCase.php b/src/applications/ponder/storage/__tests__/PonderQuestionStatusTestCase.php new file mode 100644 index 0000000000..0057dd03d4 --- /dev/null +++ b/src/applications/ponder/storage/__tests__/PonderQuestionStatusTestCase.php @@ -0,0 +1,25 @@ +setStatus($status); + $this->assertEqual(true, $question->isStatusClosed()); + } + + } + + public function testOpenedStatuses() { + $statuses = PonderQuestionStatus::getQuestionStatusOpenMap(); + foreach ($statuses as $status) { + $question = new PonderQuestion(); + $question->setStatus($status); + $this->assertEqual(false, $question->isStatusClosed()); + } + } + +} From 6036079e883aefd1707e05a127372d84e757cfc2 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 29 Mar 2023 20:36:57 +0200 Subject: [PATCH 045/425] PHP 8.2: fix deprecated use of "parent" in callables Summary: Closes T15200 Test Plan: Test 1: I was able to run `arc unit --everything` without the error thrown from T15200 Test 2: I also tried this and it still works: ``` php -a require 'src/applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php'; $asd = new PhabricatorMetaMTAReceivedMailProcessingException("1", "TEST MESSAGE"); var_dump($asd->getMessage()); ``` You get: ``` string(12) "TEST MESSAGE" ``` Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15200 Differential Revision: https://we.phorge.it/D25099 --- resources/celerity/map.php | 4 ++-- .../PhabricatorMetaMTAReceivedMailProcessingException.php | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 9323cc070d..0c05232751 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -180,7 +180,7 @@ 'rsrc/css/phui/phui-tag-view.css' => 'fb811341', 'rsrc/css/phui/phui-timeline-view.css' => '2d32d7a9', 'rsrc/css/phui/phui-two-column-view.css' => 'f96d319f', - 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308', + 'rsrc/css/phui/workboards/phui-workboard-color.css' => '3a1c21ff', 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', 'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6', 'rsrc/css/phui/workboards/phui-workpanel.css' => '3ae89b20', @@ -882,7 +882,7 @@ 'phui-theme-css' => '35883b37', 'phui-timeline-view-css' => '2d32d7a9', 'phui-two-column-view-css' => 'f96d319f', - 'phui-workboard-color-css' => 'e86de308', + 'phui-workboard-color-css' => '3a1c21ff', 'phui-workboard-view-css' => '74fc9d98', 'phui-workcard-view-css' => '913441b6', 'phui-workpanel-view-css' => '3ae89b20', diff --git a/src/applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php b/src/applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php index 5fb1209885..cc77af9595 100644 --- a/src/applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php +++ b/src/applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php @@ -14,7 +14,8 @@ public function __construct($status_code /* ... */) { $this->statusCode = $args[0]; $args = array_slice($args, 1); - call_user_func_array(array('parent', '__construct'), $args); + $parent = get_parent_class($this); + call_user_func_array(array($parent, '__construct'), $args); } } From 821df3364ec4f41f4148a28de786422b230211e6 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 30 Mar 2023 12:11:11 +0200 Subject: [PATCH 046/425] Hide the "hidden" fields on custom form previews Summary: Hide the "hidden" fields on custom form previews. Before this change, Phabricator's custom form preview doesn't actually hide the hidden fields, instead it shows them at 50% opacity. It looks cluttered and doesn't provide a very useful "preview" at all. This just hides some fields via CSS. Cherry picked from: https://phabricator.wikimedia.org/rPHABbba62cf5243538af9e37cc1211a01d247294f9f7 Upstream Task: https://phabricator.wikimedia.org/T209743 Test Plan: Tested in Wikimedia's fork. I believe it results in better UX. Reviewers: #blessed_committers, O1 Blessed Committers, Cigaryno, avivey, Matthew, valerio.bozzolan Reviewed By: #blessed_committers, O1 Blessed Committers, Cigaryno, avivey, valerio.bozzolan Subscribers: avivey, Cigaryno, speck, tobiaswiese, valerio.bozzolan, Matthew Maniphest Tasks: T15081 Differential Revision: https://we.phorge.it/D25037 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/phui/phui-form-view.css | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 0c05232751..d11174a1d2 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'f538846d', + 'core.pkg.css' => 'bb377509', 'core.pkg.js' => '66c49ca1', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '609e63d4', @@ -154,7 +154,7 @@ 'rsrc/css/phui/phui-document.css' => '52b748a5', 'rsrc/css/phui/phui-feed-story.css' => 'a0c05029', 'rsrc/css/phui/phui-fontkit.css' => '1ec937e5', - 'rsrc/css/phui/phui-form-view.css' => '01b796c0', + 'rsrc/css/phui/phui-form-view.css' => '7536aef9', 'rsrc/css/phui/phui-form.css' => 'd1adb52c', 'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed', 'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', @@ -847,7 +847,7 @@ 'phui-font-icon-base-css' => '303c9b87', 'phui-fontkit-css' => '1ec937e5', 'phui-form-css' => 'd1adb52c', - 'phui-form-view-css' => '01b796c0', + 'phui-form-view-css' => '7536aef9', 'phui-formation-view-css' => 'd2dec8ed', 'phui-head-thing-view-css' => 'd7f293df', 'phui-header-view-css' => '36c86a58', diff --git a/webroot/rsrc/css/phui/phui-form-view.css b/webroot/rsrc/css/phui/phui-form-view.css index accce86819..47f7d1bb2c 100644 --- a/webroot/rsrc/css/phui/phui-form-view.css +++ b/webroot/rsrc/css/phui/phui-form-view.css @@ -525,7 +525,7 @@ properly, and submit values. */ } .aphront-form-preview-hidden { - opacity: 0.5; + display: none; } .aphront-form-error .phui-icon-view { From fc9bbb9949f8792de009376ed030045aa9eaef9c Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 31 Mar 2023 10:40:45 +0200 Subject: [PATCH 047/425] Workboard: fix regression caused by tags= prefiller Summary: This change fixes a regression introduced here: rP5e2b3677157889104a7e540d7772a04f13164037 Thank you to the user @dadalha for auditing that commit and sharing a stack trace to easily fix the issue. In short, if you see this exception you are affected: ``` EXCEPTION: (InvalidArgumentException) Value provided to "replaceQueryParam()" for key "tags" is NULL. Use "removeQueryParam()" to remove a query parameter. at [/src/parser/PhutilURI.php:341] ``` This change just fixes that specific problem. Closes T15221 Test Plan: 1. Create a new Project 2. Create a new Milestone inside 3. Create the Milestone's Workboard 4. Visit the Milestone's Workboard and note that now it works Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno, dadalha Tags: #workboard Maniphest Tasks: T15221 Differential Revision: https://we.phorge.it/D25103 --- .../PhabricatorProjectBoardViewController.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index d254936c1a..a24c911b9f 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -693,11 +693,19 @@ private function buildColumnMenu( ->newCreateActionSpecifications(array()); foreach ($specs as $spec) { + + // Prefill tags= when you open the Column menu + // https://we.phorge.it/T15147 + $spec_href = new PhutilURI($spec['uri']); + $spec_slug = $project->getPrimarySlug(); + if ($spec_slug !== null) { + $spec_href->replaceQueryParam('tags', $spec_slug); + } + $column_items[] = id(new PhabricatorActionView()) ->setIcon($spec['icon']) ->setName($spec['name']) - ->setHref(id(new PhutilURI($spec['uri'])) - ->replaceQueryParam('tags', $project->getPrimarySlug())) + ->setHref($spec_href) ->setDisabled($spec['disabled']) ->addSigil('column-add-task') ->setMetadata( From d25d630fe5dd5bf9c1521bb7b067a8810aca114b Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 31 Mar 2023 22:01:56 +0200 Subject: [PATCH 048/425] PHP 8.2: fixes for strlen() not accepting NULL anymore, part 1 Summary: This change avoids some unnecessary uses of the strlen() function, actually fixing some deprecation warnings in PHP 8.2. In short, this is the suggested universal replace: -if(strlen($v)) +if(phutil_nonempty_string($v)) And, if you know PHP, this is also another adoptable replace, but only for cases where you are sure that the string "0" is not useful: -if(strlen($v)) +if($v)) As usual the optimal solution depends on the contest. Other similar patches will probably follow. Closes T15222 Ref T15190 Test Plan: - for the first time in my life, with this change, the unit tests are passed in PHP 8.2 - check with your big eyes that there are no obvious typos Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15199, T15190, T15222 Differential Revision: https://we.phorge.it/D25104 --- .../check/PhabricatorStorageSetupCheck.php | 8 ++++---- .../engine/PhabricatorS3FileStorageEngine.php | 12 ++++++------ .../files/storage/PhabricatorFile.php | 4 ++-- .../engine/PhabricatorMailEmailEngine.php | 4 ++-- .../client/PhabricatorNotificationServerRef.php | 2 +- .../engine/PhabricatorRepositoryPullEngine.php | 2 +- .../repository/storage/PhabricatorRepository.php | 3 ++- .../cluster/PhabricatorDatabaseRef.php | 2 +- src/infrastructure/env/PhabricatorEnv.php | 16 ++++++++-------- src/infrastructure/log/PhabricatorSSHLog.php | 2 +- 10 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/applications/config/check/PhabricatorStorageSetupCheck.php b/src/applications/config/check/PhabricatorStorageSetupCheck.php index 86aa1f2ebd..75639f2f43 100644 --- a/src/applications/config/check/PhabricatorStorageSetupCheck.php +++ b/src/applications/config/check/PhabricatorStorageSetupCheck.php @@ -151,19 +151,19 @@ private function checkS3() { $how_many = 0; - if (strlen($access_key)) { + if (phutil_nonempty_string($access_key)) { $how_many++; } - if (strlen($secret_key)) { + if (phutil_nonempty_string($secret_key)) { $how_many++; } - if (strlen($region)) { + if (phutil_nonempty_string($region)) { $how_many++; } - if (strlen($endpoint)) { + if (phutil_nonempty_string($endpoint)) { $how_many++; } diff --git a/src/applications/files/engine/PhabricatorS3FileStorageEngine.php b/src/applications/files/engine/PhabricatorS3FileStorageEngine.php index 443a5a9ddc..95cdfc737b 100644 --- a/src/applications/files/engine/PhabricatorS3FileStorageEngine.php +++ b/src/applications/files/engine/PhabricatorS3FileStorageEngine.php @@ -31,11 +31,11 @@ public function canWriteFiles() { $endpoint = PhabricatorEnv::getEnvConfig('amazon-s3.endpoint'); $region = PhabricatorEnv::getEnvConfig('amazon-s3.region'); - return (strlen($bucket) && - strlen($access_key) && - strlen($secret_key) && - strlen($endpoint) && - strlen($region)); + return phutil_nonempty_string($bucket) && + phutil_nonempty_string($access_key) && + phutil_nonempty_string($secret_key) && + phutil_nonempty_string($endpoint) && + phutil_nonempty_string($region); } @@ -57,7 +57,7 @@ public function writeFile($data, array $params) { $parts[] = 'phabricator'; $instance_name = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance_name)) { + if (phutil_nonempty_string($instance_name)) { $parts[] = $instance_name; } diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index cc1b701dcd..355aa2d215 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -856,7 +856,7 @@ public function getCDNURI($request_kind) { // instance identity in the path allows us to distinguish between requests // originating from different instances but served through the same CDN. $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $parts[] = '@'.$instance; } @@ -903,7 +903,7 @@ private function getTransformedURI($transform) { $parts[] = 'xform'; $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $parts[] = '@'.$instance; } diff --git a/src/applications/metamta/engine/PhabricatorMailEmailEngine.php b/src/applications/metamta/engine/PhabricatorMailEmailEngine.php index 6c9cf1b356..4278aa96a1 100644 --- a/src/applications/metamta/engine/PhabricatorMailEmailEngine.php +++ b/src/applications/metamta/engine/PhabricatorMailEmailEngine.php @@ -507,7 +507,7 @@ private function newEmailAddress($address, $name = null) { public function newDefaultEmailAddress() { $raw_address = PhabricatorEnv::getEnvConfig('metamta.default-address'); - if (!strlen($raw_address)) { + if (!$raw_address) { $domain = $this->newMailDomain(); $raw_address = "noreply@{$domain}"; } @@ -527,7 +527,7 @@ public function newVoidEmailAddress() { private function newMailDomain() { $domain = PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain'); - if (strlen($domain)) { + if ($domain) { return $domain; } diff --git a/src/applications/notification/client/PhabricatorNotificationServerRef.php b/src/applications/notification/client/PhabricatorNotificationServerRef.php index 714ad5f5d7..c5b0f2f8aa 100644 --- a/src/applications/notification/client/PhabricatorNotificationServerRef.php +++ b/src/applications/notification/client/PhabricatorNotificationServerRef.php @@ -152,7 +152,7 @@ public function getURI($to_path = null) { ->setPath($full_path); $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $uri->replaceQueryParam('instance', $instance); } diff --git a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php index 12aee9bc51..5bcf4554fd 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php @@ -238,7 +238,7 @@ private function getHookContextIdentifier(PhabricatorRepository $repository) { $identifier = $repository->getPHID(); $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $identifier = "{$identifier}:{$instance}"; } diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index c68eb22668..5c7c9ff33d 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -2480,7 +2480,8 @@ public function newBuiltinURIs() { $has_https = false; } - $has_ssh = (bool)strlen(PhabricatorEnv::getEnvConfig('phd.user')); + $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); + $has_ssh = phutil_nonempty_string($phd_user); $protocol_map = array( PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH => $has_ssh, diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRef.php b/src/infrastructure/cluster/PhabricatorDatabaseRef.php index 1eb232ad86..e526eb1639 100644 --- a/src/infrastructure/cluster/PhabricatorDatabaseRef.php +++ b/src/infrastructure/cluster/PhabricatorDatabaseRef.php @@ -229,7 +229,7 @@ public function getRefKey() { $host = $this->getHost(); $port = $this->getPort(); - if (strlen($port)) { + if ($port) { return "{$host}:{$port}"; } diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index ca478ed0a5..617fb57af9 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -125,7 +125,7 @@ private static function initializeCommonEnvironment($config_optional) { // If an instance identifier is defined, write it into the environment so // it's available to subprocesses. $instance = self::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { putenv('PHABRICATOR_INSTANCE='.$instance); $_ENV['PHABRICATOR_INSTANCE'] = $instance; } @@ -432,7 +432,7 @@ public static function isSelfURI($raw_uri) { $uri = new PhutilURI($raw_uri); $host = $uri->getDomain(); - if (!strlen($host)) { + if (!phutil_nonempty_string($host)) { return false; } @@ -455,7 +455,7 @@ private static function getSelfURIMap() { $self_map = array(); foreach ($self_uris as $self_uri) { $host = id(new PhutilURI($self_uri))->getDomain(); - if (!strlen($host)) { + if (!phutil_nonempty_string($host)) { continue; } @@ -661,7 +661,7 @@ public static function isValidURIForLink($uri) { public static function isValidLocalURIForLink($uri) { $uri = (string)$uri; - if (!strlen($uri)) { + if (!phutil_nonempty_string($uri)) { return false; } @@ -726,7 +726,7 @@ public static function requireValidRemoteURIForLink($raw_uri) { $uri = new PhutilURI($raw_uri); $proto = $uri->getProtocol(); - if (!strlen($proto)) { + if (!$proto) { throw new Exception( pht( 'URI "%s" is not a valid linkable resource. A valid linkable '. @@ -745,7 +745,7 @@ public static function requireValidRemoteURIForLink($raw_uri) { } $domain = $uri->getDomain(); - if (!strlen($domain)) { + if (!$domain) { throw new Exception( pht( 'URI "%s" is not a valid linkable resource. A valid linkable '. @@ -793,7 +793,7 @@ public static function requireValidRemoteURIForFetch( $uri = new PhutilURI($raw_uri); $proto = $uri->getProtocol(); - if (!strlen($proto)) { + if (!$proto) { throw new Exception( pht( 'URI "%s" is not a valid fetchable resource. A valid fetchable '. @@ -812,7 +812,7 @@ public static function requireValidRemoteURIForFetch( } $domain = $uri->getDomain(); - if (!strlen($domain)) { + if (!$domain) { throw new Exception( pht( 'URI "%s" is not a valid fetchable resource. A valid fetchable '. diff --git a/src/infrastructure/log/PhabricatorSSHLog.php b/src/infrastructure/log/PhabricatorSSHLog.php index de4c270f72..e3c15de6bf 100644 --- a/src/infrastructure/log/PhabricatorSSHLog.php +++ b/src/infrastructure/log/PhabricatorSSHLog.php @@ -24,7 +24,7 @@ public static function getLog() { ); $sudo_user = PhabricatorEnv::getEnvConfig('phd.user'); - if (strlen($sudo_user)) { + if (phutil_nonempty_string($sudo_user)) { $data['S'] = $sudo_user; } From 96ae4ba13acbf0e2f8932e950a92af0495f034d7 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 1 Apr 2023 15:19:43 +0200 Subject: [PATCH 049/425] PHP 8.1: fixes for strlen() not accepting NULL anymore, part 2 Summary: This is a fix for PHP 8.1 deprecation of strlen(NULL), for these Phorge components: - scripts - aphront - project The strlen() was used in Phabricator to check if a generic value was a non-empty string. For this reason, Phorge adopts phutil_nonempty_string() that checks that. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If your phutil_nonempty_string() throws an exception, just report it to Phorge to evaluate and fix together that specific corner case. Closes T15223 Ref T15190 Ref T15064 Test Plan: - check with your big eyes that there are no obvious typos Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15223, T15190, T15064 Differential Revision: https://we.phorge.it/D25105 --- scripts/repository/commit_hook.php | 8 ++++---- scripts/ssh/ssh-auth.php | 8 ++++---- scripts/ssh/ssh-exec.php | 4 ++-- scripts/symbols/generate_ctags_symbols.php | 2 +- scripts/symbols/generate_php_symbols.php | 2 +- src/aphront/AphrontRequest.php | 8 ++++---- .../configuration/AphrontApplicationConfiguration.php | 2 +- src/aphront/response/AphrontAjaxResponse.php | 2 +- src/aphront/response/AphrontFileResponse.php | 2 +- src/aphront/response/AphrontWebpageResponse.php | 2 +- src/aphront/sink/AphrontPHPHTTPSink.php | 2 +- src/aphront/site/AphrontSite.php | 2 +- src/aphront/site/PhabricatorPlatformSite.php | 2 +- src/aphront/site/PhabricatorResourceSite.php | 2 +- src/aphront/site/PhabricatorShortSite.php | 2 +- .../project/phid/PhabricatorProjectProjectPHIDType.php | 2 +- support/startup/preamble-utils.php | 2 +- 17 files changed, 27 insertions(+), 27 deletions(-) diff --git a/scripts/repository/commit_hook.php b/scripts/repository/commit_hook.php index ccbfde08ff..28829edeb2 100755 --- a/scripts/repository/commit_hook.php +++ b/scripts/repository/commit_hook.php @@ -119,7 +119,7 @@ exit($err); } else if ($repository->isGit() || $repository->isHg()) { $username = getenv(DiffusionCommitHookEngine::ENV_USER); - if (!strlen($username)) { + if (!phutil_nonempty_string($username)) { throw new Exception( pht( 'No Direct Pushes: You are pushing directly to a hosted repository. '. @@ -181,17 +181,17 @@ $engine->setOriginalArgv(array_slice($argv, 2)); $remote_address = getenv(DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS); -if (strlen($remote_address)) { +if (phutil_nonempty_string($remote_address)) { $engine->setRemoteAddress($remote_address); } $remote_protocol = getenv(DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL); -if (strlen($remote_protocol)) { +if (phutil_nonempty_string($remote_protocol)) { $engine->setRemoteProtocol($remote_protocol); } $request_identifier = getenv(DiffusionCommitHookEngine::ENV_REQUEST); -if (strlen($request_identifier)) { +if (phutil_nonempty_string($request_identifier)) { $engine->setRequestIdentifier($request_identifier); } diff --git a/scripts/ssh/ssh-auth.php b/scripts/ssh/ssh-auth.php index 45e9a823db..2b3b52cf4b 100755 --- a/scripts/ssh/ssh-auth.php +++ b/scripts/ssh/ssh-auth.php @@ -36,7 +36,7 @@ $authstruct = null; -if (strlen($authstruct_raw)) { +if (phutil_nonempty_string($authstruct_raw)) { try { $authstruct = phutil_json_decode($authstruct_raw); } catch (Exception $ex) { @@ -82,13 +82,13 @@ // Strip out newlines and other nonsense from the key type and key body. $type = $ssh_key->getKeyType(); $type = preg_replace('@[\x00-\x20]+@', '', $type); - if (!strlen($type)) { + if (!phutil_nonempty_string($type)) { continue; } $key = $ssh_key->getKeyBody(); $key = preg_replace('@[\x00-\x20]+@', '', $key); - if (!strlen($key)) { + if (!phutil_nonempty_string($key)) { continue; } @@ -135,7 +135,7 @@ $cmd = csprintf('%s %Ls', $bin, $key_argv); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $cmd = csprintf('PHABRICATOR_INSTANCE=%s %C', $instance, $cmd); } diff --git a/scripts/ssh/ssh-exec.php b/scripts/ssh/ssh-exec.php index 70c95d28da..7b5b6adb8f 100755 --- a/scripts/ssh/ssh-exec.php +++ b/scripts/ssh/ssh-exec.php @@ -103,7 +103,7 @@ '--phabricator-ssh-device', $user_name, $device_name)); - } else if (strlen($user_name)) { + } else if (phutil_nonempty_string($user_name)) { $user = id(new PhabricatorPeopleQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withUsernames(array($user_name)) @@ -117,7 +117,7 @@ id(new PhabricatorAuthSessionEngine()) ->willServeRequestForUser($user); - } else if (strlen($device_name)) { + } else if (phutil_nonempty_string($device_name)) { if (!$remote_address) { throw new Exception( pht( diff --git a/scripts/symbols/generate_ctags_symbols.php b/scripts/symbols/generate_ctags_symbols.php index e93b0c5cbc..13972385ab 100755 --- a/scripts/symbols/generate_ctags_symbols.php +++ b/scripts/symbols/generate_ctags_symbols.php @@ -39,7 +39,7 @@ $futures = array(); foreach (explode("\n", trim($input)) as $file) { - if (!strlen($file)) { + if (!phutil_nonempty_string($file)) { continue; } diff --git a/scripts/symbols/generate_php_symbols.php b/scripts/symbols/generate_php_symbols.php index af87d580d8..cc8cab3817 100755 --- a/scripts/symbols/generate_php_symbols.php +++ b/scripts/symbols/generate_php_symbols.php @@ -27,7 +27,7 @@ $futures = array(); foreach (explode("\n", trim($input)) as $file) { - if (!strlen($file)) { + if (!phutil_nonempty_string($file)) { continue; } diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index f35f3f1279..58fc6f9346 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -66,7 +66,7 @@ public function getURILineRange($key, $limit) { } public static function parseURILineRange($range, $limit) { - if (!strlen($range)) { + if (!phutil_nonempty_string($range)) { return null; } @@ -234,7 +234,7 @@ public function getJSONMap($name, $default = array()) { $raw_data = phutil_string_cast($this->requestData[$name]); $raw_data = trim($raw_data); - if (!strlen($raw_data)) { + if (!phutil_nonempty_string($raw_data)) { return $default; } @@ -499,7 +499,7 @@ private function getCookieDomainURI() { // domain is. This makes setup easier, and we'll tell administrators to // configure a base domain during the setup process. $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); - if (!strlen($base_uri)) { + if (!phutil_nonempty_string($base_uri)) { return new PhutilURI('http://'.$host.'/'); } @@ -956,7 +956,7 @@ public function updateEphemeralCookies() { $submit_cookie = PhabricatorCookies::COOKIE_SUBMIT; $submit_key = $this->getCookie($submit_cookie); - if (strlen($submit_key)) { + if (phutil_nonempty_string($submit_key)) { $this->clearCookie($submit_cookie); $this->submitKey = $submit_key; } diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php index 550a5a0316..3bdd2c00bc 100644 --- a/src/aphront/configuration/AphrontApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontApplicationConfiguration.php @@ -823,7 +823,7 @@ private static function readHTTPPOSTData() { $raw_input = PhabricatorStartup::getRawInput(); $parser = new PhutilQueryStringParser(); - if (strlen($raw_input)) { + if (phutil_nonempty_string($raw_input)) { $content_type = idx($_SERVER, 'CONTENT_TYPE'); $is_multipart = preg_match('@^multipart/form-data@i', $content_type); if ($is_multipart) { diff --git a/src/aphront/response/AphrontAjaxResponse.php b/src/aphront/response/AphrontAjaxResponse.php index 2187defc8f..b9e60809fc 100644 --- a/src/aphront/response/AphrontAjaxResponse.php +++ b/src/aphront/response/AphrontAjaxResponse.php @@ -64,7 +64,7 @@ public function buildResponseString() { if ($viewer) { $postprocessor_key = $viewer->getUserSetting( PhabricatorAccessibilitySetting::SETTINGKEY); - if (strlen($postprocessor_key)) { + if (phutil_nonempty_string($postprocessor_key)) { $response->setPostprocessorKey($postprocessor_key); } } diff --git a/src/aphront/response/AphrontFileResponse.php b/src/aphront/response/AphrontFileResponse.php index 6bae4c808f..980d1ee5dd 100644 --- a/src/aphront/response/AphrontFileResponse.php +++ b/src/aphront/response/AphrontFileResponse.php @@ -19,7 +19,7 @@ public function addAllowOrigin($origin) { } public function setDownload($download) { - if (!strlen($download)) { + if (!phutil_nonempty_string($download)) { $download = 'untitled'; } $this->download = $download; diff --git a/src/aphront/response/AphrontWebpageResponse.php b/src/aphront/response/AphrontWebpageResponse.php index de642f9b19..00a953ece8 100644 --- a/src/aphront/response/AphrontWebpageResponse.php +++ b/src/aphront/response/AphrontWebpageResponse.php @@ -21,7 +21,7 @@ public function getUnexpectedOutput() { public function buildResponseString() { $unexpected_output = $this->getUnexpectedOutput(); - if (strlen($unexpected_output)) { + if (phutil_nonempty_string($unexpected_output)) { $style = array( 'background: linear-gradient(180deg, #eeddff, #ddbbff);', 'white-space: pre-wrap;', diff --git a/src/aphront/sink/AphrontPHPHTTPSink.php b/src/aphront/sink/AphrontPHPHTTPSink.php index 4953b6e610..1aaa1810ff 100644 --- a/src/aphront/sink/AphrontPHPHTTPSink.php +++ b/src/aphront/sink/AphrontPHPHTTPSink.php @@ -8,7 +8,7 @@ final class AphrontPHPHTTPSink extends AphrontHTTPSink { protected function emitHTTPStatus($code, $message = '') { if ($code != 200) { $header = "HTTP/1.0 {$code}"; - if (strlen($message)) { + if (phutil_nonempty_string($message)) { $header .= " {$message}"; } header($header); diff --git a/src/aphront/site/AphrontSite.php b/src/aphront/site/AphrontSite.php index 85bf42fe6c..08813462c6 100644 --- a/src/aphront/site/AphrontSite.php +++ b/src/aphront/site/AphrontSite.php @@ -15,7 +15,7 @@ public function new404Controller(AphrontRequest $request) { protected function isHostMatch($host, array $uris) { foreach ($uris as $uri) { - if (!strlen($uri)) { + if (!phutil_nonempty_string($uri)) { continue; } diff --git a/src/aphront/site/PhabricatorPlatformSite.php b/src/aphront/site/PhabricatorPlatformSite.php index a3193e65d1..e6c733684b 100644 --- a/src/aphront/site/PhabricatorPlatformSite.php +++ b/src/aphront/site/PhabricatorPlatformSite.php @@ -14,7 +14,7 @@ public function newSiteForRequest(AphrontRequest $request) { // If no base URI has been configured yet, match this site so the user // can follow setup instructions. $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); - if (!strlen($base_uri)) { + if (!phutil_nonempty_string($base_uri)) { return new PhabricatorPlatformSite(); } diff --git a/src/aphront/site/PhabricatorResourceSite.php b/src/aphront/site/PhabricatorResourceSite.php index 88f7777607..3fca474a17 100644 --- a/src/aphront/site/PhabricatorResourceSite.php +++ b/src/aphront/site/PhabricatorResourceSite.php @@ -14,7 +14,7 @@ public function newSiteForRequest(AphrontRequest $request) { $host = $request->getHost(); $uri = PhabricatorEnv::getEnvConfig('security.alternate-file-domain'); - if (!strlen($uri)) { + if (!phutil_nonempty_string($uri)) { return null; } diff --git a/src/aphront/site/PhabricatorShortSite.php b/src/aphront/site/PhabricatorShortSite.php index d4c36aecbe..05cf2c3584 100644 --- a/src/aphront/site/PhabricatorShortSite.php +++ b/src/aphront/site/PhabricatorShortSite.php @@ -14,7 +14,7 @@ public function newSiteForRequest(AphrontRequest $request) { $host = $request->getHost(); $uri = PhabricatorEnv::getEnvConfig('phurl.short-uri'); - if (!strlen($uri)) { + if (!phutil_nonempty_string($uri)) { return null; } diff --git a/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php b/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php index 9247966d75..4a9cfbb8a6 100644 --- a/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php +++ b/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php @@ -43,7 +43,7 @@ public function loadHandles( $handle->setName($name); - if (strlen($slug)) { + if (phutil_nonempty_string($slug)) { $handle->setObjectName('#'.$slug); $handle->setMailStampName('#'.$slug); $handle->setURI("/tag/{$slug}/"); diff --git a/support/startup/preamble-utils.php b/support/startup/preamble-utils.php index 8dd3b502d6..dfb5619a43 100644 --- a/support/startup/preamble-utils.php +++ b/support/startup/preamble-utils.php @@ -21,7 +21,7 @@ function preamble_trust_x_forwarded_for_header($layers = 1) { } $forwarded_for = $_SERVER['HTTP_X_FORWARDED_FOR']; - if (!strlen($forwarded_for)) { + if (!phutil_nonempty_string($forwarded_for)) { return; } From 306ce1c0b71ad81c76ce695244024b9514e18c56 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 5 Apr 2023 15:36:20 +0200 Subject: [PATCH 050/425] Fix "Search" Application description Summary: This description was somehow broken since 2019: {F276845} Probably this was caused by a Remarkup breaking change introduced by this specific XSS security fix: {54bcbdaba94a3573e128c6498816dbfa41d3a9cb} Maybe in the future the backtick operator will be supported again in Remarkup. Or, maybe not. So, let's just workaround to make that specific page "less exploded" again. Closes T15230 Test Plan: - visit the page /applications/view/PhabricatorSearchApplication/ - check that now the Remarkup is rendered perfectly Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15230 Differential Revision: https://we.phorge.it/D25110 --- .../PhabricatorSearchApplicationStorageEnginePanel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php b/src/applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php index 6dd0786c0c..e8bc9854b7 100644 --- a/src/applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php +++ b/src/applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php @@ -48,7 +48,7 @@ public function buildConfigurationPagePanel() { } $instructions = pht( - 'To configure the search engines, edit [[ %s | `%s` ]] configuration. '. + 'To configure the search engines, edit [[ %s | %s ]] configuration. '. 'See **[[ %s | %s ]]** for documentation.', '/config/edit/cluster.search/', 'cluster.search', From 787a84969fa04c519a9291eaac3f7ebbbb3585fa Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 5 Apr 2023 15:39:25 +0200 Subject: [PATCH 051/425] Phriction: clarify its search results as "Wiki page" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: This patch changes a bit how your search results from Phriction are described in the autocomplete component (the "Typehead"): {F276843} After this change, Phriction search results will also contain the word "Wiki page" so that users can better understand what the result actually is. Just like a Diffusion repository mentions that it's a "Repository", and just like a Project mentions that it's a "Project" and so on. Before this change, the Typehead entries were only mentioning the slug of that wiki page. | Before | After | | {F274820} | {F272278} | It's unclear if the previous behavior was a mistake (since the internal parameter of the Typehead is called "type", and so, it is supposed to mention the application type, not the slug. Anyway, as a compromise, the slug is still mentioned. To be honest this is just an excuse to put the middle dot / aka interpunct character in this project again. Yeah, here the middle point was used as separator. The apparent reason is: because the middle point was already in use elsewhere in Phorge. The real reason is: I'm a lobbyist in a corporation that sells interpuncts worldwide as our core business, and so, I designed thisvil plan to put the following middle point - again - in Phorge (evil laugh). Closes T15213 Test Plan: - Search "Change Log" in the up-right bar (or similar) - You see "Wiki Page · change_log/" (or similar) Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Tags: #phriction Maniphest Tasks: T15213 Differential Revision: https://we.phorge.it/D25102 --- .../typeahead/PhrictionDocumentDatasource.php | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php b/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php index 09056fe761..8e16051840 100644 --- a/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php +++ b/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php @@ -18,6 +18,9 @@ public function getDatasourceApplicationClass() { public function loadResults() { $viewer = $this->getViewer(); + $app_type = pht('Wiki Document'); + $mid_dot = "\xC2\xB7"; + $query = id(new PhrictionDocumentQuery()) ->setViewer($viewer) ->needContent(true); @@ -34,15 +37,25 @@ public function loadResults() { foreach ($documents as $document) { $content = $document->getContent(); - if (!$document->isActive()) { - $closed = $document->getStatusDisplayName(); - } else { + if ($document->isActive()) { $closed = null; + } else { + $closed = $document->getStatusDisplayName(); } $slug = $document->getSlug(); $title = $content->getTitle(); + // For some time the search result was + // just mentioning the document slug. + // Now, it also mentions the application type. + // Example: "Wiki Document - /foo/bar" + $display_type = sprintf( + '%s %s %s', + $app_type, + $mid_dot, + $slug); + $sprite = 'phabricator-search-icon phui-font-fa phui-icon-view fa-book'; $autocomplete = '[[ '.$slug.' ]]'; @@ -51,7 +64,7 @@ public function loadResults() { ->setDisplayName($title) ->setURI($document->getURI()) ->setPHID($document->getPHID()) - ->setDisplayType($slug) + ->setDisplayType($display_type) ->setPriorityType('wiki') ->setImageSprite($sprite) ->setAutocomplete($autocomplete) From 524579fe64a62adfed8964233be8a32a376b80c5 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 6 Apr 2023 10:58:05 +0200 Subject: [PATCH 052/425] Diffusion: fix missing mobile "Actions" from Manage Repo Summary: After this change, the page "/manage/" related to Diffusion repositories has an "Actions" button for mobile users: {F276113} Before this change, that button was missing only from this specific page, and so it was really difficult for them (if not impossible) to edit repository's basic information: {F276115} Do not confuse the hamburger menu on the sidebar with this "Actions". The hamburger contains queries like Active Repo, All Repo, etc. Instead the Actions contains Edit Basic Info, Allow Dangerous Changes, etc. It seems the cause of this problem is originated from an apparently incomplete rollback occurred in Sep 6 2017 from Phabricator: e1fd74ddb545a2de3963fd60a6c5f4c1d5075814 As you can see, the DiffusionRepositoryBasicsManagementPanel was probably not completely restored from the previous version, at least for the part related to the method newActionList(): bb369c7b711f6eb5f6b3ca9a0c8b85a088b05dc1 The method newActionList() was restored in every other panel but no the one related to the Basics Management. So, a new PhabricatorActionListView object was manually created, but without generating an ID from the Celerity utilities. In short, this change assigns an ID to this Curtain, in order to correctly generates a related "Actions" menu again. Otherwise, on mobile the curtain (with all the actions) is automatically hidden, without any alternative. Closes T15067 Test Plan: - Repository > Actions > Manage Repository - Reduce the page until "Actions" appears (yeeh!) Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Tags: #diffusion, #mobile Maniphest Tasks: T15067 Differential Revision: https://we.phorge.it/D25109 --- .../management/DiffusionRepositoryBasicsManagementPanel.php | 3 +-- .../management/DiffusionRepositoryManagementPanel.php | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php index 02df82c7c0..fdd477f874 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php @@ -31,8 +31,7 @@ public function buildManagementPanelCurtain() { $repository = $this->getRepository(); $viewer = $this->getViewer(); - $action_list = id(new PhabricatorActionListView()) - ->setViewer($viewer); + $action_list = $this->newActionList(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, diff --git a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php index 1a58ef6a49..d938ed834c 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php @@ -111,6 +111,9 @@ public function getPanelNavigationURI() { final protected function newActionList() { $viewer = $this->getViewer(); + + // Generating this ID allows to spawn the "Actions" menu + // on mobile on the header $action_id = celerity_generate_unique_node_id(); return id(new PhabricatorActionListView()) From 3a8ee271d9df1797c8a0926f18a97b8ba9970c9e Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 6 Apr 2023 14:44:02 +0200 Subject: [PATCH 053/425] Render all Removed Comments a little more docile Summary: This small restyle makes any Removed Comment a little less prominent than normal ones, with the goal of decreasing a bit your in-page distractions and increase your individual productivity in your business by at least 250 milliseconds every 48 hours of hard work in front of your monitor. | Before | After | |---------------------|---------------------| | {F274834,size=full} | {F274835,size=full} | This implementation (which is called "Kasper on Diet") contains these specific changes for Removed Comments: - user icon visibility: reduced by ~50% (-> Kasper) - black "trash" icon: reduced by ~50% (-> Diet) - texts: visibility reduced by ~50% - vertical padding: reduced from 16px down to 4px Note that if your Phorge is under the Serious Business Mode, it seems it is still technically possible to manually activate the "Decaying Curse" proposal mentioned in the Task. Closes T15192 Test Plan: - Add a Comment "I love Phorge" - Add a Comment "I love Phabricator" - Mark the second Comment as Removed - Call a person at your desk - Plug that person to an eyeball tracker If the general attention focuses first on a normal Comment and then on the Removed Comment, this change works perfectly. Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Tags: #comments Maniphest Tasks: T15192 Differential Revision: https://we.phorge.it/D25096 --- resources/celerity/map.php | 6 +++--- .../PhabricatorApplicationTransaction.php | 2 +- .../PhabricatorApplicationTransactionView.php | 5 +++++ webroot/rsrc/css/phui/phui-timeline-view.css | 20 +++++++++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index d11174a1d2..89425d6cc4 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'bb377509', + 'core.pkg.css' => '6590cb9f', 'core.pkg.js' => '66c49ca1', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '609e63d4', @@ -178,7 +178,7 @@ 'rsrc/css/phui/phui-spacing.css' => 'b05cadc3', 'rsrc/css/phui/phui-status.css' => '293b5dad', 'rsrc/css/phui/phui-tag-view.css' => 'fb811341', - 'rsrc/css/phui/phui-timeline-view.css' => '2d32d7a9', + 'rsrc/css/phui/phui-timeline-view.css' => 'cc554a79', 'rsrc/css/phui/phui-two-column-view.css' => 'f96d319f', 'rsrc/css/phui/workboards/phui-workboard-color.css' => '3a1c21ff', 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', @@ -880,7 +880,7 @@ 'phui-status-list-view-css' => '293b5dad', 'phui-tag-view-css' => 'fb811341', 'phui-theme-css' => '35883b37', - 'phui-timeline-view-css' => '2d32d7a9', + 'phui-timeline-view-css' => 'cc554a79', 'phui-two-column-view-css' => 'f96d319f', 'phui-workboard-color-css' => '3a1c21ff', 'phui-workboard-view-css' => '74fc9d98', diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index e32fe92983..56079efaf2 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -539,7 +539,7 @@ public function getColor() { case PhabricatorTransactions::TYPE_COMMENT; $comment = $this->getComment(); if ($comment && $comment->getIsRemoved()) { - return 'black'; + return 'grey'; } break; case PhabricatorTransactions::TYPE_EDGE: diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php index ee2020f890..180ba071a9 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php @@ -493,6 +493,11 @@ private function renderEvent( $xaction->getComment() && $xaction->getComment()->getIsRemoved(); + // Make designers happy to make CSS customizations + if ($has_removed_comment) { + $event->addClass('phui-timeline-shell-removed'); + } + if ($xaction->getCommentVersion() > 1 && !$has_removed_comment) { $event->setIsEdited(true); } diff --git a/webroot/rsrc/css/phui/phui-timeline-view.css b/webroot/rsrc/css/phui/phui-timeline-view.css index e08373d589..59caec7fea 100644 --- a/webroot/rsrc/css/phui/phui-timeline-view.css +++ b/webroot/rsrc/css/phui/phui-timeline-view.css @@ -189,9 +189,29 @@ overflow-x: auto; } +/* + * Start Customization for removed comments + * https://we.phorge.it/T15192 + */ + .phui-timeline-core-content .comment-deleted { font-style: italic; } +.phui-timeline-shell-removed .phui-timeline-image { + opacity: 0.5; +} +.phui-timeline-shell-removed, +.phui-timeline-shell-removed a, +.phui-timeline-shell-removed .phui-timeline-title { + color:#888; /* grey */ +} +.phui-timeline-shell-removed + .phui-timeline-major-event + .phui-timeline-content .phui-timeline-core-content { + padding:4px 16px; /* reduce vertical space from 16px */ +} + +/* End Customization for removed comments */ .device .phui-timeline-event-view { min-height: 23px; From dad1b15a9c5a75e56b8e19a6d91fa74be55fd98a Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 7 Apr 2023 00:21:28 +0200 Subject: [PATCH 054/425] User Badges: better integrate with Removed Comments Summary: The left part of a Removed Comment can contains User Badges that, after this change, are more consistent with the new general softness. | Before | Proposed | |------------|-----------| | {F277371} | {F277373} | Probably five people in a million will notice this change, BUT, these people will have a huge, deep, breath of relief, knowing that Phorge takes care about their obsessive-compulsive impulses, to have everything nice and consistent and neat. You are welcome! Closes T15235 Test Plan: - do something to deserve a Badge - share a Comment somewhere (Maniphest?) - delete that comment - check that the badge is slightly softer Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15235 Differential Revision: https://we.phorge.it/D25112 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/phui/phui-timeline-view.css | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 89425d6cc4..c0805e16a6 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '6590cb9f', + 'core.pkg.css' => 'b239afaa', 'core.pkg.js' => '66c49ca1', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '609e63d4', @@ -178,7 +178,7 @@ 'rsrc/css/phui/phui-spacing.css' => 'b05cadc3', 'rsrc/css/phui/phui-status.css' => '293b5dad', 'rsrc/css/phui/phui-tag-view.css' => 'fb811341', - 'rsrc/css/phui/phui-timeline-view.css' => 'cc554a79', + 'rsrc/css/phui/phui-timeline-view.css' => '7f8659ec', 'rsrc/css/phui/phui-two-column-view.css' => 'f96d319f', 'rsrc/css/phui/workboards/phui-workboard-color.css' => '3a1c21ff', 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', @@ -880,7 +880,7 @@ 'phui-status-list-view-css' => '293b5dad', 'phui-tag-view-css' => 'fb811341', 'phui-theme-css' => '35883b37', - 'phui-timeline-view-css' => 'cc554a79', + 'phui-timeline-view-css' => '7f8659ec', 'phui-two-column-view-css' => 'f96d319f', 'phui-workboard-color-css' => '3a1c21ff', 'phui-workboard-view-css' => '74fc9d98', diff --git a/webroot/rsrc/css/phui/phui-timeline-view.css b/webroot/rsrc/css/phui/phui-timeline-view.css index 59caec7fea..b9d95ccb9e 100644 --- a/webroot/rsrc/css/phui/phui-timeline-view.css +++ b/webroot/rsrc/css/phui/phui-timeline-view.css @@ -197,7 +197,8 @@ .phui-timeline-core-content .comment-deleted { font-style: italic; } -.phui-timeline-shell-removed .phui-timeline-image { +.phui-timeline-shell-removed .phui-timeline-image, +.phui-timeline-shell-removed .phui-timeline-badges { opacity: 0.5; } .phui-timeline-shell-removed, From b587865ce78af2213ea5037edc0829a2406ee869 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 8 Apr 2023 09:45:05 +0200 Subject: [PATCH 055/425] People: add a permalink to list Administrators Summary: This change introduces a builting query to easily list Administrators of your beautiful Phorge instance: {F277912} Note that any builtin query can be disabled anytime or reordered, so this is not a problem if you already have a similar saved query. But probably you have not. The goal is not to just "have a link" (this was already possible) but to have a predictable link to be used in any instance-agnostic document, including but not limited to Diviner or Phorge's user interface messages. Closes T15238 Test Plan: Visit /people/query/admin/ and enjoy. Reviewers: O1 Blessed Committers, Cigaryno, Matthew Reviewed By: O1 Blessed Committers, Cigaryno, Matthew Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15238 Differential Revision: https://we.phorge.it/D25116 --- .../people/query/PhabricatorPeopleSearchEngine.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/applications/people/query/PhabricatorPeopleSearchEngine.php b/src/applications/people/query/PhabricatorPeopleSearchEngine.php index 57ed133df4..bdeab953ec 100644 --- a/src/applications/people/query/PhabricatorPeopleSearchEngine.php +++ b/src/applications/people/query/PhabricatorPeopleSearchEngine.php @@ -200,6 +200,7 @@ protected function getURI($path) { protected function getBuiltinQueryNames() { $names = array( 'active' => pht('Active'), + 'admin' => pht('Administrators'), 'all' => pht('All'), ); @@ -221,6 +222,9 @@ public function buildSavedQueryFromBuiltin($query_key) { case 'active': return $query ->setParameter('isDisabled', false); + case 'admin': + return $query + ->setParameter('isAdmin', true); case 'approval': return $query ->setParameter('needsApproval', true) From d46aab8321187eb989c9b9db4700d4f805847daf Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Sun, 9 Apr 2023 05:27:42 -0700 Subject: [PATCH 056/425] Add GitHub mirror to list of known mirrors Summary: Ref T15046 Test Plan: Clone phorge from github mirror, visit `/config/`, see correct branch-point in `Version Information` Reviewers: O1 Blessed Committers, Cigaryno, valerio.bozzolan Reviewed By: O1 Blessed Committers, Cigaryno, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15046 Differential Revision: https://we.phorge.it/D25115 --- .../config/controller/PhabricatorConfigConsoleController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/applications/config/controller/PhabricatorConfigConsoleController.php b/src/applications/config/controller/PhabricatorConfigConsoleController.php index 8d60459837..4773a8ab60 100644 --- a/src/applications/config/controller/PhabricatorConfigConsoleController.php +++ b/src/applications/config/controller/PhabricatorConfigConsoleController.php @@ -178,6 +178,7 @@ private function loadVersions(PhabricatorUser $viewer) { '('. implode('|', array( 'we\.phorge\.it/', + 'github\.com/phorgeit/', 'github\.com/phacility/', 'secure\.phabricator\.com/', )). From 0a82ccb8a4b085d124ff8ad01f85cef43970b55f Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sun, 9 Apr 2023 22:45:36 +0200 Subject: [PATCH 057/425] AphrontFileResponse: avoid alien usages of setDownload() Summary: I noticed that - historically - setDownload() could also be used with false, true, or other weird values to activate a download filename. So, this change continues to give full compatibility to PHP 8.1 but with extra validations. This also adds a bit of inline documentation to put this more explicit. For more context see the previous version: {5952b0a31b6aac0718bc23aefe43560b9bfe8cc5} {96ae4ba13acbf0e2f8932e950a92af0495f034d7} Ref T15190 Test Plan: - Drop a file in a comment, click, download - See that it still works Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15190 Differential Revision: https://we.phorge.it/D25113 --- src/aphront/response/AphrontFileResponse.php | 27 +++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/aphront/response/AphrontFileResponse.php b/src/aphront/response/AphrontFileResponse.php index 980d1ee5dd..6508ad6020 100644 --- a/src/aphront/response/AphrontFileResponse.php +++ b/src/aphront/response/AphrontFileResponse.php @@ -8,7 +8,16 @@ final class AphrontFileResponse extends AphrontResponse { private $compressResponse; private $mimeType; + + /** + * Download filename + * + * This is NULL as default or a string. + * + * @var string|null + */ private $download; + private $rangeMin; private $rangeMax; private $allowOrigins = array(); @@ -18,14 +27,30 @@ public function addAllowOrigin($origin) { return $this; } + /** + * Set a download filename + * + * @param $download string + * @return self + */ public function setDownload($download) { + + // Make sure we have a populated string if (!phutil_nonempty_string($download)) { $download = 'untitled'; } + $this->download = $download; return $this; } + /** + * Get the download filename + * + * If this was never set, NULL is given. + * + * @return string|null + */ public function getDownload() { return $this->download; } @@ -113,7 +138,7 @@ public function getHeaders() { $headers[] = array('Content-Length', $content_len); } - if (strlen($this->getDownload())) { + if (phutil_nonempty_string($this->getDownload())) { $headers[] = array('X-Download-Options', 'noopen'); $filename = $this->getDownload(); From a7b472284be65cdc3037a1f22cf8ec76f7913c25 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sun, 9 Apr 2023 22:47:43 +0200 Subject: [PATCH 058/425] Mobile: show Homepage contents and avoid duplicate menus Summary: After this change, the Homepage finally shows contents on Mobile, instead of just showing the sidebar menu twice. Closes T15216 Test Plan: To test this change in general: - resize the window horizontally and enjoy I tested it in: - tested homepage of Phorge - Audit - tested homepage of Audit - Auth - tested Auth homepage - tested Login page - tested Forgot your password page - Config - tested Config homepage - tested page Core Settings - tested page Unresolved Setup Issues - (Services) - tested page Database Status - tested page Cache Status - tested page Notification Servers - tested page Repository Services - tested page Search Servers - (Extensions/Modules) - tested page Constants: Differential - tested page Content Sources - Conpherence - tested open-close menu - tested persistent chat - tested Rooms list - tested single Chat - Dashboards - tested general homepage of Dashboards - tested specific Dashboard - view page - tested specific Dashboard - edit page - Differential - tested general homepage of Differential - tested Create Diff page - Diffusion - tested general homepage of Diffusion - tested specific Diffusion repository - view page - tested specific Diffusion repository - Manage page - tested specific Diffusion repository - Policy page - tested specific Diffusion repository - Automation page - tested specific commit page - view page - tested specific commit page - Edit Commit - Diviner - tested general homepage of Diviner - tested specific Diviner book - tested list of technical Classes - tested specific technical Class - tested search of All Atoms - Feed - tested Feed homepage ("All Stories") - tested specific feed - view page - Files - tested general homepage of Files - tested specific File - view page - tested specific File - Edit File - tested Award Token popup - tested Flag for Later - tested View Transforms - Form Engine - tested View Forms Configurations page - tested specific View Form Configuration - tested Use Form - Maniphest - tested general homepage of Maniphest - tested specific Maniphest Task - view page - tested specific Maniphest Task - Edit mode - tested specific Maniphest Task - Award Token popup - Menu - tested page Configure Menu - tested page Personal Menu Items - tested page Global Menu Items - Notifications - tested open action - tested close action - tested click action - People - tested general homepage of People - tested specific User - view page - tested specific User - Settings page - home - tested specific User - Settings page - Date & Time - tested specific User - Settings page - External Editor - tested specific User - Settings page - External Editor - tested specific User - Settings page - SSH Public Keys - tested specific User - Settings page - SSH Public Keys - upload popup - tested specific User - Settings page - SSH Public Keys - view history - Pholio - tested general homepage of Pholio - tested Create a Mock page - tested specific Pholio - view - tested specific Pholio - Edit page - tested specific Pholio - Award Token popup - tested specific Pholio - Edit Related Tasks popup - tested specific Pholio - add a Comment - Phriction - tested specific page of Phriction - view - tested specific page of Phriction - Create Page popup - tested specific page of Phriction - Create Page dedicated page - tested specific page of Phriction - Edit Page - tested specific page of Phriction - Award Token popup - Project - tested general homepage of Projects - tested Create Project page - Search - tested search results All Results - tested Advanced Search page - TOTP - tested input screen - Workboard - tested specific Workboard - tested Create Task popup - tested Add Column popup - tested Manage Workboard page - tested Edit Details page - test Flag for Later popup - test Edit Picture page Reviewers: O1 Blessed Committers, Cigaryno, speck Reviewed By: O1 Blessed Committers, Cigaryno, speck Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Tags: #mobile, #ux Maniphest Tasks: T15216 Differential Revision: https://we.phorge.it/D25107 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/phui/phui-basic-nav-view.css | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index c0805e16a6..5fd7c5961b 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'b239afaa', + 'core.pkg.css' => '2ba11212', 'core.pkg.js' => '66c49ca1', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '609e63d4', @@ -138,7 +138,7 @@ 'rsrc/css/phui/phui-action-list.css' => '1b0085b2', 'rsrc/css/phui/phui-action-panel.css' => '6c386cbf', 'rsrc/css/phui/phui-badge.css' => '666e25ad', - 'rsrc/css/phui/phui-basic-nav-view.css' => '56ebd66d', + 'rsrc/css/phui/phui-basic-nav-view.css' => 'a5693cf0', 'rsrc/css/phui/phui-big-info-view.css' => '362ad37b', 'rsrc/css/phui/phui-box.css' => '5ed3b8cb', 'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30', @@ -822,7 +822,7 @@ 'phriction-document-css' => '03380da0', 'phui-action-panel-css' => '6c386cbf', 'phui-badge-view-css' => '666e25ad', - 'phui-basic-nav-view-css' => '56ebd66d', + 'phui-basic-nav-view-css' => 'a5693cf0', 'phui-big-info-view-css' => '362ad37b', 'phui-box-css' => '5ed3b8cb', 'phui-bulk-editor-css' => '374d5e30', diff --git a/webroot/rsrc/css/phui/phui-basic-nav-view.css b/webroot/rsrc/css/phui/phui-basic-nav-view.css index a545ad35ce..f718c673ab 100644 --- a/webroot/rsrc/css/phui/phui-basic-nav-view.css +++ b/webroot/rsrc/css/phui/phui-basic-nav-view.css @@ -23,7 +23,7 @@ overflow: hidden; } -.phabricator-home.device-phone .phabricator-nav-content { +.phabricator-home.device-phone .phui-navigation-shell .phabricator-nav-local { display: none; } From 9c759fe23a8011a26310fb978c9230deddfcab94 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Sun, 9 Apr 2023 11:10:49 -0700 Subject: [PATCH 059/425] Fix preamble-support Summary: Phutil is not yet loaded during preamble, so we can't use it. This change fixes a regression introduced here, fixing PHP 8.1 support: {96ae4ba13acbf0e2f8932e950a92af0495f034d7} Ref T15064 Test Plan: Included test script. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15064 Differential Revision: https://we.phorge.it/D25114 --- .../__tests__/PreambleUtilsTestCase.php | 123 ++++++++++++++++++ support/startup/preamble-utils.php | 2 +- 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100755 support/startup/__tests__/PreambleUtilsTestCase.php diff --git a/support/startup/__tests__/PreambleUtilsTestCase.php b/support/startup/__tests__/PreambleUtilsTestCase.php new file mode 100755 index 0000000000..25062e3701 --- /dev/null +++ b/support/startup/__tests__/PreambleUtilsTestCase.php @@ -0,0 +1,123 @@ +#!/usr/bin/env php + 'abc', + $null_value => $undefined, + '' => $undefined, + + // Strange, unexpected cases: + 144 => '144', + ); + + foreach ($test_cases as $input => $expected) { + switch ($input) { + case $undefined: + unset($_SERVER['HTTP_X_FORWARDED_FOR']); + break; + + case $null_value: + $_SERVER['HTTP_X_FORWARDED_FOR'] = null; + break; + + default: + $_SERVER['HTTP_X_FORWARDED_FOR'] = $input; + break; + } + + unset($_SERVER['REMOTE_ADDR']); + + preamble_trust_x_forwarded_for_header(); + + if (!isset($_SERVER['REMOTE_ADDR'])) { + if ($expected === $undefined) { + // test pass + continue; + } else { + $this->failTest("Failed for input {$input} - result is not defined!"); + } + } + + $actual = $_SERVER['REMOTE_ADDR']; + + if ($actual !== $expected) { + var_dump($actual); + + $this->failTest( + "Failed for input {$input} - actual output is {$actual}"); + } + + } + + } + + + private function failTest($message = null) { + echo $message; + echo "\n"; + throw new Exception(); + } + + /** + * Run all tests in this class. + * + * Return: True if all tests passed; False if any test failed. + */ + final public function run() { + $reflection = new ReflectionClass($this); + $methods = $reflection->getMethods(); + + $any_fail = false; + + // Try to ensure that poorly-written tests which depend on execution order + // (and are thus not properly isolated) will fail. + shuffle($methods); + + foreach ($methods as $method) { + $name = $method->getName(); + if (!preg_match('/^test/', $name)) { + continue; + } + + try { + call_user_func_array( + array($this, $name), + array()); + echo "Test passed: {$name}\n"; + } catch (Throwable $ex) { + $any_fail = true; + echo "Failed test: {$name}\n"; + } + } + return !$any_fail; + } + +} + +require_once dirname(__DIR__).'/preamble-utils.php'; + +$test_case = new PreambleUtilsTestCase(); +$good = $test_case->run(); + +if (!$good) { + exit(3); +} diff --git a/support/startup/preamble-utils.php b/support/startup/preamble-utils.php index dfb5619a43..8dd3b502d6 100644 --- a/support/startup/preamble-utils.php +++ b/support/startup/preamble-utils.php @@ -21,7 +21,7 @@ function preamble_trust_x_forwarded_for_header($layers = 1) { } $forwarded_for = $_SERVER['HTTP_X_FORWARDED_FOR']; - if (!phutil_nonempty_string($forwarded_for)) { + if (!strlen($forwarded_for)) { return; } From 1b9da964e9707aeefe2cdb810e1b308948d63c86 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 10 Apr 2023 11:02:31 +0200 Subject: [PATCH 060/425] Add setup check to avoid git version 2.5.0 and below (May 2015) Summary: After this change, if you have a deprecated git version, now you see a related information config warning: {F276840} Having said that the git version 1.8.3.1 - that for example is provided by CentOS 7 - is surely problematic; Some sources indicate all git versions *before* 2.5.0 as not able to handle the '--' escape argument. The '--' arg is used to separate normal git flags from user arguments, so that they cannot be mistakenly exchanged. Kind of: git -- The problem is, Phabricator/Phorge at the moment executes this kind of git commands when you surf a git repository from the web interface: git cat-file -t -- : But, if your git version does not support "--", you can get yourself into unhappy situations since "--" could be just interpreted as a "wrong value", causing other considerations and misleading exceptions, such as: This path was a submodule at : If you have seen the above error while surfing a git repo, and if you have 2.5.0 or before, and if you are sure that the mentioned path it's not a submodule, you probably need a git update. It is not yet clear whether Phabricator/Phorge should support the possibility of this lack of support for "--". In the meanwhile, just update your git on the server. AFAIK no particular version is required on your clients. Related information: - https://unix.stackexchange.com/a/740621/85666 - https://github.com/git/git/commit/b48158ac94cf725834b70b4a5ab7f2d152a741d4 If you disagree, please do not simply ignore the warning but share your experience in the Task. Ref T15179 Test Plan: - open Phorge, you see no warnings since you are already up to date. Nice. - Otherwise, you see a nice config message, and you can ignore it as usual. Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15179 Differential Revision: https://we.phorge.it/D25089 --- .../config/check/PhabricatorBinariesSetupCheck.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/applications/config/check/PhabricatorBinariesSetupCheck.php b/src/applications/config/check/PhabricatorBinariesSetupCheck.php index b87282fcc7..9bbf8a367e 100644 --- a/src/applications/config/check/PhabricatorBinariesSetupCheck.php +++ b/src/applications/config/check/PhabricatorBinariesSetupCheck.php @@ -104,7 +104,17 @@ protected function executeChecks() { switch ($vcs['versionControlSystem']) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - $bad_versions = array(); + $bad_versions = array( + // We need 2.5.0 to use "git cat-file -t -- :" + // https://we.phorge.it/T15179 + '< 2.5.0' => pht( + 'The minimum supported version of Git on the server is %s, '. + 'which was released in %s. In older versions, the Git server '. + 'may not be able to escape arguments with the "--" operator. '. + 'Note: your users do not require a particular version of Git.', + '2.5.0', + '2015'), + ); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $bad_versions = array( From a5c93dea568bcdf59122e52bd8bd5e8c849bcfd7 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 14 Apr 2023 22:08:15 +0200 Subject: [PATCH 061/425] Fix InvalidArgumentException on commit hook Summary: Fix a regression introduced here: 96ae4ba13acbf0e2f8932e950a92af0495f034d7 I reproduced this exception executing "svn commit" on a hosted repository. That crash happened because the PHP getenv() function can return false. But, that is a very terrible value that blasts the non-string-empty check. So, now the default getenv() value is skipped, without causing problems. Closes T15253 Ref T15190 Test Plan: - I've run `svn commit` and I have not encountered any issue now Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15253, T15190 Differential Revision: https://we.phorge.it/D25122 --- scripts/repository/commit_hook.php | 32 +++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/scripts/repository/commit_hook.php b/scripts/repository/commit_hook.php index 28829edeb2..08f4600b9b 100755 --- a/scripts/repository/commit_hook.php +++ b/scripts/repository/commit_hook.php @@ -119,12 +119,14 @@ exit($err); } else if ($repository->isGit() || $repository->isHg()) { $username = getenv(DiffusionCommitHookEngine::ENV_USER); - if (!phutil_nonempty_string($username)) { - throw new Exception( - pht( - 'No Direct Pushes: You are pushing directly to a hosted repository. '. - 'This will not work. See "No Direct Pushes" in the documentation '. - 'for more information.')); + if ($username !== false) { + if (!phutil_nonempty_string($username)) { + throw new Exception( + pht( + 'No Direct Pushes: You are pushing directly to a hosted repository. '. + 'This will not work. See "No Direct Pushes" in the documentation '. + 'for more information.')); + } } if ($repository->isHg()) { @@ -181,18 +183,24 @@ $engine->setOriginalArgv(array_slice($argv, 2)); $remote_address = getenv(DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS); -if (phutil_nonempty_string($remote_address)) { - $engine->setRemoteAddress($remote_address); +if ($remote_address !== false) { + if (phutil_nonempty_string($remote_address)) { + $engine->setRemoteAddress($remote_address); + } } $remote_protocol = getenv(DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL); -if (phutil_nonempty_string($remote_protocol)) { - $engine->setRemoteProtocol($remote_protocol); +if ($remote_protocol !== false) { + if (phutil_nonempty_string($remote_protocol)) { + $engine->setRemoteProtocol($remote_protocol); + } } $request_identifier = getenv(DiffusionCommitHookEngine::ENV_REQUEST); -if (phutil_nonempty_string($request_identifier)) { - $engine->setRequestIdentifier($request_identifier); +if ($request_identifier !== false) { + if (phutil_nonempty_string($request_identifier)) { + $engine->setRequestIdentifier($request_identifier); + } } try { From 996a853fc4a473134519f3fe8c4b2de52efccab9 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Tue, 18 Apr 2023 08:15:15 -0700 Subject: [PATCH 062/425] Update "how to file bugs" Summary: We want users to use Ponder as primary support vector. T15084 Test Plan: Generated diviner, saw that the reference works correctly. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25125 --- src/docs/contributor/bug_reports.diviner | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/docs/contributor/bug_reports.diviner b/src/docs/contributor/bug_reports.diviner index 3ded817a48..6fed325c5c 100644 --- a/src/docs/contributor/bug_reports.diviner +++ b/src/docs/contributor/bug_reports.diviner @@ -144,7 +144,13 @@ to file a report. It is **particularly critical** that you include reproduction steps. -You can file a report [[ https://we.phorge.it/maniphest/task/edit/form/2/ | on this instance]]. +Community Members - that is, members of #community - can +[[ https://we.phorge.it/maniphest/task/edit/form/2/ | file a report directly +on this instance]]. + +Potential members - i.e., everyone else - please use Ponder to +[[ https://we.phorge.it/ponder/question/create/ | Ask for support ]] from the +Community Members. Next Steps From 1ba5c8c260950ce8bc77f261b41aa82ad5d776e2 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 20 Apr 2023 18:00:04 +0200 Subject: [PATCH 063/425] Fix some PHP 8.1 issues in Diviner generator Summary: After this change I was able to generate again the Diviner documentation from PHP 8.2 using the related command: ./bin/diviner generate Closes T15255 Test Plan: - run `./bin/diviner generate` - great success - tested the advanced search in various ways - it still works Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: Ekubischta, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15226, T15255 Differential Revision: https://we.phorge.it/D25124 --- src/applications/diviner/query/DivinerBookQuery.php | 4 ++-- src/applications/diviner/storage/DivinerLiveSymbol.php | 2 +- src/applications/diviner/workflow/DivinerGenerateWorkflow.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/applications/diviner/query/DivinerBookQuery.php b/src/applications/diviner/query/DivinerBookQuery.php index 2d6527ec96..e809099c7e 100644 --- a/src/applications/diviner/query/DivinerBookQuery.php +++ b/src/applications/diviner/query/DivinerBookQuery.php @@ -133,7 +133,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { $this->phids); } - if (strlen($this->nameLike)) { + if (phutil_nonempty_string($this->nameLike)) { $where[] = qsprintf( $conn, 'name LIKE %~', @@ -147,7 +147,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { $this->names); } - if (strlen($this->namePrefix)) { + if (phutil_nonempty_string($this->namePrefix)) { $where[] = qsprintf( $conn, 'name LIKE %>', diff --git a/src/applications/diviner/storage/DivinerLiveSymbol.php b/src/applications/diviner/storage/DivinerLiveSymbol.php index 38b99208a7..d62e6a1e18 100644 --- a/src/applications/diviner/storage/DivinerLiveSymbol.php +++ b/src/applications/diviner/storage/DivinerLiveSymbol.php @@ -182,7 +182,7 @@ public function getTitle() { public function setTitle($value) { $this->writeField('title', $value); - if (strlen($value)) { + if (phutil_nonempty_string($value)) { $slug = DivinerAtomRef::normalizeTitleString($value); $hash = PhabricatorHash::digestForIndex($slug); $this->titleSlugHash = $hash; diff --git a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php index 86b75c8480..866e8b9ee6 100644 --- a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php +++ b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php @@ -194,7 +194,7 @@ private function generateBook($book, PhutilArgumentParser $args) { $identifier = $args->getArg('repository'); $repository = null; - if (strlen($identifier)) { + if (phutil_nonempty_string($identifier)) { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withIdentifiers(array($identifier)) From 7ed35123a347a05c70c97eba2bec2b36eb2b3218 Mon Sep 17 00:00:00 2001 From: Mukunda Modell Date: Sun, 23 Apr 2023 15:15:30 -0500 Subject: [PATCH 064/425] Countdown: add a capability to decide who can Create Summary: This change adds the `countdown.create` capability to allow to limit countdown creation. This change comes from the Wikimedia Phabricator and was originally proposed by v.bozzolan and implemented by the kind user 20after4. https://phabricator.wikimedia.org/T258599 Closes T15208 Ref T15081 Test Plan: - go to the page /applications/view/PhabricatorCountdownApplication/ - play a bit with the "Can Create Countdowns" option - note that it is respected Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, Matthew, Cigaryno Tags: #countdown, #policy_archived Maniphest Tasks: T15208, T15081 Differential Revision: https://we.phorge.it/D25101 --- .../PhabricatorCountdownApplication.php | 4 ++++ .../PhabricatorCountdownCreateCapability.php | 16 ++++++++++++++++ .../editor/PhabricatorCountdownEditEngine.php | 5 +++++ 3 files changed, 25 insertions(+) create mode 100644 src/applications/countdown/capability/PhabricatorCountdownCreateCapability.php diff --git a/src/applications/countdown/application/PhabricatorCountdownApplication.php b/src/applications/countdown/application/PhabricatorCountdownApplication.php index b3b5a79da2..e205ba64c3 100644 --- a/src/applications/countdown/application/PhabricatorCountdownApplication.php +++ b/src/applications/countdown/application/PhabricatorCountdownApplication.php @@ -50,6 +50,10 @@ public function getRoutes() { protected function getCustomCapabilities() { return array( + PhabricatorCountdownCreateCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_USER, + 'caption' => pht('Default create policy for countdowns.'), + ), PhabricatorCountdownDefaultViewCapability::CAPABILITY => array( 'caption' => pht('Default view policy for new countdowns.'), 'template' => PhabricatorCountdownCountdownPHIDType::TYPECONST, diff --git a/src/applications/countdown/capability/PhabricatorCountdownCreateCapability.php b/src/applications/countdown/capability/PhabricatorCountdownCreateCapability.php new file mode 100644 index 0000000000..057b6c22d9 --- /dev/null +++ b/src/applications/countdown/capability/PhabricatorCountdownCreateCapability.php @@ -0,0 +1,16 @@ +getURI(); } + protected function getCreateNewObjectPolicy() { + return $this->getApplication()->getPolicy( + PhabricatorCountdownCreateCapability::CAPABILITY); + } + protected function buildCustomEditFields($object) { $epoch_value = $object->getEpoch(); if ($epoch_value === null) { From 90f9da643d1622bcfff3a8903159aeefd1b42520 Mon Sep 17 00:00:00 2001 From: Dylan F Date: Sun, 23 Apr 2023 23:35:27 +0000 Subject: [PATCH 065/425] Add and use new RemarkupMetadata class Summary: Add a new RemarkupMetadata class and use with upload button as well as drag and drop pathways. With this change, files are now attached automatically to the object. This this could solve several issues, for example when a person uploads a file but only that author is able to see it. Ref T15106 Test Plan: - Drag and drop file, upload file with button. Check that both files are attached with "attachedFilePHIDs" values. - drop file in a Task description (now works) - drop file in a Task comment (now works) - drop file in an edited Task comment (still not supported) - This was already tested in Wikimedia Foundation: https://phabricator.wikimedia.org/D1203 Reviewers: O1 Blessed Committers, avivey, Matthew Reviewed By: O1 Blessed Committers, avivey, Matthew Subscribers: MCPCN, avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Tags: #remarkup Maniphest Tasks: T15106 Differential Revision: https://we.phorge.it/D25052 --- resources/celerity/map.php | 56 +++++++++++-------- .../control/PhabricatorRemarkupControl.php | 2 + webroot/rsrc/js/core/RemarkupMetadata.js | 49 ++++++++++++++++ .../core/behavior-drag-and-drop-textarea.js | 29 ++-------- .../behavior-phabricator-remarkup-assist.js | 7 +++ 5 files changed, 95 insertions(+), 48 deletions(-) create mode 100644 webroot/rsrc/js/core/RemarkupMetadata.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 5fd7c5961b..e5588c0b7a 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,10 +10,10 @@ 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', 'core.pkg.css' => '2ba11212', - 'core.pkg.js' => '66c49ca1', + 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '609e63d4', - 'differential.pkg.js' => 'c60bec1b', + 'differential.pkg.js' => 'f68ea9d1', 'diffusion.pkg.css' => '42c75c37', 'diffusion.pkg.js' => '78c9885d', 'maniphest.pkg.css' => '35995d6d', @@ -461,6 +461,7 @@ 'rsrc/js/core/MultirowRowManager.js' => '5b54c823', 'rsrc/js/core/Notification.js' => 'a9b91e3f', 'rsrc/js/core/Prefab.js' => '5793d835', + 'rsrc/js/core/RemarkupMetadata.js' => 'e40c4991', 'rsrc/js/core/ShapedRequest.js' => '995f5102', 'rsrc/js/core/TextAreaUtils.js' => 'f340a484', 'rsrc/js/core/Title.js' => '43bc9360', @@ -473,7 +474,7 @@ 'rsrc/js/core/behavior-copy.js' => 'cf32921f', 'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94', 'rsrc/js/core/behavior-device.js' => 'ac2b1e01', - 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '3277c62d', + 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '29e221f6', 'rsrc/js/core/behavior-fancy-datepicker.js' => 'b545d0a0', 'rsrc/js/core/behavior-form.js' => '55d7b788', 'rsrc/js/core/behavior-gesture.js' => 'b58d1a2a', @@ -489,7 +490,7 @@ 'rsrc/js/core/behavior-more.js' => '506aa3f4', 'rsrc/js/core/behavior-object-selector.js' => '98ef467f', 'rsrc/js/core/behavior-oncopy.js' => 'da8f5259', - 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '54262396', + 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '6d347847', 'rsrc/js/core/behavior-read-only-warning.js' => 'b9109f8f', 'rsrc/js/core/behavior-redirect.js' => '407ee861', 'rsrc/js/core/behavior-refresh-csrf.js' => '46116c01', @@ -589,7 +590,7 @@ 'javelin-behavior-aphlict-listen' => '4e61fa88', 'javelin-behavior-aphlict-status' => 'c3703a16', 'javelin-behavior-aphront-basic-tokenizer' => '3b4899b0', - 'javelin-behavior-aphront-drag-and-drop-textarea' => '3277c62d', + 'javelin-behavior-aphront-drag-and-drop-textarea' => '29e221f6', 'javelin-behavior-aphront-form-disable-on-submit' => '55d7b788', 'javelin-behavior-aphront-more' => '506aa3f4', 'javelin-behavior-audio-source' => '3dc5ad43', @@ -653,7 +654,7 @@ 'javelin-behavior-phabricator-notification-example' => '29819b75', 'javelin-behavior-phabricator-object-selector' => '98ef467f', 'javelin-behavior-phabricator-oncopy' => 'da8f5259', - 'javelin-behavior-phabricator-remarkup-assist' => '54262396', + 'javelin-behavior-phabricator-remarkup-assist' => '6d347847', 'javelin-behavior-phabricator-reveal-content' => 'b105a3a6', 'javelin-behavior-phabricator-search-typeahead' => '1cb7d027', 'javelin-behavior-phabricator-show-older-transactions' => '8b5c7d65', @@ -800,6 +801,7 @@ 'phabricator-phtize' => '2f1db1ed', 'phabricator-prefab' => '5793d835', 'phabricator-remarkup-css' => '5baa3bd9', + 'phabricator-remarkup-metadata' => 'e40c4991', 'phabricator-search-results-css' => '9ea70ace', 'phabricator-shaped-request' => '995f5102', 'phabricator-slowvote-css' => '1694baed', @@ -1163,6 +1165,13 @@ 'javelin-stratcom', 'javelin-behavior', ), + '29e221f6' => array( + 'javelin-behavior', + 'javelin-dom', + 'phabricator-drag-and-drop-file-upload', + 'phabricator-textareautils', + 'phabricator-remarkup-metadata', + ), '2a8b62d9' => array( 'multirow-row-manager', 'javelin-install', @@ -1201,13 +1210,6 @@ 'javelin-install', 'javelin-util', ), - '3277c62d' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-json', - 'phabricator-drag-and-drop-file-upload', - 'phabricator-textareautils', - ), '32db8374' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1424,17 +1426,6 @@ '541f81c3' => array( 'javelin-install', ), - 54262396 => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'phabricator-phtize', - 'phabricator-textareautils', - 'javelin-workflow', - 'javelin-vector', - 'phuix-autocomplete', - 'javelin-mask', - ), '548567f6' => array( 'syntax-default-css', ), @@ -1575,6 +1566,18 @@ 'javelin-install', 'javelin-util', ), + '6d347847' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'phabricator-phtize', + 'phabricator-textareautils', + 'phabricator-remarkup-metadata', + 'javelin-workflow', + 'javelin-vector', + 'phuix-autocomplete', + 'javelin-mask', + ), 70245195 => array( 'javelin-behavior', 'javelin-stratcom', @@ -2126,6 +2129,11 @@ 'javelin-dom', 'phuix-dropdown-menu', ), + 'e40c4991' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-json', + ), 'e4c7622a' => array( 'javelin-magical-init', 'javelin-install', diff --git a/src/view/form/control/PhabricatorRemarkupControl.php b/src/view/form/control/PhabricatorRemarkupControl.php index 67afd10630..ba7950bcc9 100644 --- a/src/view/form/control/PhabricatorRemarkupControl.php +++ b/src/view/form/control/PhabricatorRemarkupControl.php @@ -128,6 +128,8 @@ protected function renderInput() { 'disabled' => $this->getDisabled(), 'sendOnEnter' => $this->getSendOnEnter(), 'rootID' => $root_id, + 'remarkupMetadataID' => $metadata_id, + 'remarkupMetadataValue' => $metadata_value, 'autocompleteMap' => (object)array( 64 => array( // "@" 'datasourceURI' => $user_datasource->getDatasourceURI(), diff --git a/webroot/rsrc/js/core/RemarkupMetadata.js b/webroot/rsrc/js/core/RemarkupMetadata.js new file mode 100644 index 0000000000..14469b25f5 --- /dev/null +++ b/webroot/rsrc/js/core/RemarkupMetadata.js @@ -0,0 +1,49 @@ +/** + * @requires javelin-install + * javelin-dom + * javelin-json + * @provides phabricator-remarkup-metadata + * @javelin + */ + +JX.install('RemarkupMetadata', { + + construct: function(metadataValue, metadataID) { + if (JX.RemarkupMetadata._metadataValue == null) { + JX.RemarkupMetadata._metadataValue = {}; + } + if (!JX.RemarkupMetadata._metadataValue.hasOwnProperty(metadataID)) { + JX.RemarkupMetadata._metadataValue[metadataID] = metadataValue; + } + this._metadataNode = JX.$(metadataID); + this._metadataID = metadataID; + }, + + statics: { + _metadataValue: null + }, + + members: { + _metadataNode: null, + _metadataID: null, + + _writeMetadata: function() { + this._metadataNode.value = JX.JSON.stringify( + JX.RemarkupMetadata._metadataValue[this._metadataID]); + }, + + getMetadata: function(key, default_value) { + if (JX.RemarkupMetadata._metadataValue[this._metadataID] + .hasOwnProperty(key)) { + return JX.RemarkupMetadata._metadataValue[this._metadataID][key]; + } + return default_value; + }, + + setMetadata: function(key, value) { + JX.RemarkupMetadata._metadataValue[this._metadataID][key] = value; + this._writeMetadata(); + } + } + +}); diff --git a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js index 8cf14349b0..879a6a086d 100644 --- a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js +++ b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js @@ -2,36 +2,15 @@ * @provides javelin-behavior-aphront-drag-and-drop-textarea * @requires javelin-behavior * javelin-dom - * javelin-json * phabricator-drag-and-drop-file-upload * phabricator-textareautils + * phabricator-remarkup-metadata */ JX.behavior('aphront-drag-and-drop-textarea', function(config) { var target = JX.$(config.target); - var metadata_node = JX.$(config.remarkupMetadataID); - var metadata_value = config.remarkupMetadataValue; - - function set_metadata(key, value) { - metadata_value[key] = value; - write_metadata(); - } - - function get_metadata(key, default_value) { - if (metadata_value.hasOwnProperty(key)) { - return metadata_value[key]; - } - return default_value; - } - - function write_metadata() { - metadata_node.value = JX.JSON.stringify(metadata_value); - } - - write_metadata(); - if (JX.PhabricatorDragAndDropFileUpload.isSupported()) { var drop = new JX.PhabricatorDragAndDropFileUpload(target) .setURI(config.uri) @@ -48,9 +27,11 @@ JX.behavior('aphront-drag-and-drop-textarea', function(config) { drop.listen('didUpload', function(file) { JX.TextAreaUtils.insertFileReference(target, file); - var phids = get_metadata('attachedFilePHIDs', []); + var metadata = new JX.RemarkupMetadata(config.remarkupMetadataValue, + config.remarkupMetadataID); + var phids = metadata.getMetadata('attachedFilePHIDs', []); phids.push(file.getPHID()); - set_metadata('attachedFilePHIDs', phids); + metadata.setMetadata('attachedFilePHIDs', phids); }); drop.start(); diff --git a/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js b/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js index 89d23ef60f..b93075c36e 100644 --- a/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js +++ b/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js @@ -5,6 +5,7 @@ * javelin-dom * phabricator-phtize * phabricator-textareautils + * phabricator-remarkup-metadata * javelin-workflow * javelin-vector * phuix-autocomplete @@ -255,6 +256,12 @@ JX.behavior('phabricator-remarkup-assist', function(config) { .setURI(file.uri); JX.TextAreaUtils.insertFileReference(area, upload); + + var metadata = new JX.RemarkupMetadata( + config.remarkupMetadataValue, config.remarkupMetadataID); + var phids = metadata.getMetadata('attachedFilePHIDs', []); + phids.push(file.phid); + metadata.setMetadata('attachedFilePHIDs', phids); } }) .start(); From 98c1267e5f16b9cf236f17ede1049ab9478e1412 Mon Sep 17 00:00:00 2001 From: Dylan F Date: Tue, 25 Apr 2023 06:33:02 -0700 Subject: [PATCH 066/425] Countdown: fix PhutilMissingSymbolException Summary: A previous change added a couple of Countdown-related classes: 7ed35123a347a05c70c97eba2bec2b36eb2b3218 This change updates the related library map (arc liberate) in order to fix any related regression (like PhutilMissingSymbolException). Closes T15271 Test Plan: - you run `arc liberate` and the file does not change - you do not see any `PhutilMissingSymbolException` exception Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15271 Differential Revision: https://we.phorge.it/D25133 --- src/__phutil_library_map__.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 6a6bf37aed..3d8570b4f7 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2975,6 +2975,7 @@ 'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php', 'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php', 'PhabricatorCountdownCountdownPHIDType' => 'applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php', + 'PhabricatorCountdownCreateCapability' => 'applications/countdown/capability/PhabricatorCountdownCreateCapability.php', 'PhabricatorCountdownDAO' => 'applications/countdown/storage/PhabricatorCountdownDAO.php', 'PhabricatorCountdownDefaultEditCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultEditCapability.php', 'PhabricatorCountdownDefaultViewCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultViewCapability.php', @@ -9376,6 +9377,7 @@ 'PhabricatorCountdownApplication' => 'PhabricatorApplication', 'PhabricatorCountdownController' => 'PhabricatorController', 'PhabricatorCountdownCountdownPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorCountdownCreateCapability' => 'PhabricatorPolicyCapability', 'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO', 'PhabricatorCountdownDefaultEditCapability' => 'PhabricatorPolicyCapability', 'PhabricatorCountdownDefaultViewCapability' => 'PhabricatorPolicyCapability', From 935d7120ee32b8c15a5e045dcbac40571a72073b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 25 Apr 2023 15:44:39 +0200 Subject: [PATCH 067/425] PHP 8.1 "preg_match(null)" exception in javelin/markup.php when http_action not set Summary: Accessing a project's workboard URL of a non-existing workboard shows RunTimeException in PHP 8.1: preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated Closes T15262 Test Plan: Page whether to create a workboard for a project was displayed after this change Reviewers: O1 Blessed Committers, avivey, valerio.bozzolan Reviewed By: O1 Blessed Committers, avivey, valerio.bozzolan Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15262 Differential Revision: https://we.phorge.it/D25131 --- src/aphront/AphrontRequest.php | 2 +- src/infrastructure/javelin/markup.php | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index 58fc6f9346..2561e397b6 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -448,7 +448,7 @@ public function setCookiePrefix($prefix) { } private function getPrefixedCookieName($name) { - if (strlen($this->cookiePrefix)) { + if (phutil_nonempty_string($this->cookiePrefix)) { return $this->cookiePrefix.'_'.$name; } else { return $name; diff --git a/src/infrastructure/javelin/markup.php b/src/infrastructure/javelin/markup.php index 533baad65e..673869a4c5 100644 --- a/src/infrastructure/javelin/markup.php +++ b/src/infrastructure/javelin/markup.php @@ -77,7 +77,10 @@ function phabricator_form(PhabricatorUser $user, $attributes, $content) { $is_post = (strcasecmp($http_method, 'POST') === 0); $http_action = idx($attributes, 'action'); - $is_absolute_uri = preg_match('#^(https?:|//)#', $http_action); + $is_absolute_uri = 0; + if (phutil_nonempty_string($http_action)) { + $is_absolute_uri = preg_match('#^(https?:|//)#', $http_action); + } if ($is_post) { From a89b4ff5b8bc5524c59629abfad080631f06be5e Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 25 Apr 2023 20:52:51 +0200 Subject: [PATCH 068/425] Members page of Parent Subproject: less dead-end Summary: When you visit the Members page of a Parent Subproject, some actions are greyed out by design. And, if you click on them, nothing really useful happens to continue with what you want to do. For example, see this page and click on "Join Project": https://we.phorge.it/project/members/1/ After this change, the interface at least provides a link to proceed to the next step of what you wanted to do - that is - allowing to quickly list direct Subprojects and do something more meaningful than that. A/B for the "Join Project" view: | Before | After | |-----------|-----------| | {F282337} | {F282331} | A/B for the "Add Members" view: | Before | After | |-----------|-----------| | {F282338} | {F282332} | Just to be nice, this change adds some inline documentation on some methods of AphrontDialogView used here. Closes T15258 Test Plan: - Create a Project (A) with a Subproject (B) - Visit Members page of (A) - Click on Join Project and click on the Blue Button - Click on the Add Member and click on Blue Button - in both cases it should get you to Subprojects list Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15258 Differential Revision: https://we.phorge.it/D25127 --- ...PhabricatorProjectMembersAddController.php | 5 ++++ .../PhabricatorProjectUpdateController.php | 5 ++++ src/view/AphrontDialogView.php | 26 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/applications/project/controller/PhabricatorProjectMembersAddController.php b/src/applications/project/controller/PhabricatorProjectMembersAddController.php index 79c66dc5b1..572e71f51a 100644 --- a/src/applications/project/controller/PhabricatorProjectMembersAddController.php +++ b/src/applications/project/controller/PhabricatorProjectMembersAddController.php @@ -27,9 +27,14 @@ public function handleRequest(AphrontRequest $request) { $copy = pht('Parent projects and milestones do not support adding '. 'members. You can add members directly to any non-parent subproject.'); + $subprojects_uri = "/project/subprojects/{$id}/"; + return $this->newDialog() ->setTitle(pht('Unsupported Project')) ->appendParagraph($copy) + ->setSubmitURI($subprojects_uri) + ->addSubmitButton(pht('See Subprojects')) + ->setDisableWorkflowOnSubmit(true) ->addCancelButton($done_uri); } diff --git a/src/applications/project/controller/PhabricatorProjectUpdateController.php b/src/applications/project/controller/PhabricatorProjectUpdateController.php index 7cbd77b4cb..5707693654 100644 --- a/src/applications/project/controller/PhabricatorProjectUpdateController.php +++ b/src/applications/project/controller/PhabricatorProjectUpdateController.php @@ -38,9 +38,14 @@ public function handleRequest(AphrontRequest $request) { $copy = pht('Parent projects and milestones do not support adding '. 'members. You can add members directly to any non-parent subproject.'); + $subprojects_uri = "/project/subprojects/{$id}/"; + return $this->newDialog() ->setTitle(pht('Unsupported Project')) ->appendParagraph($copy) + ->setSubmitURI($subprojects_uri) + ->addSubmitButton(pht('See Subprojects')) + ->setDisableWorkflowOnSubmit(true) ->addCancelButton($done_uri); } diff --git a/src/view/AphrontDialogView.php b/src/view/AphrontDialogView.php index b8b00a6b3e..14aa4a19b6 100644 --- a/src/view/AphrontDialogView.php +++ b/src/view/AphrontDialogView.php @@ -51,6 +51,15 @@ public function getIsStandalone() { return $this->isStandalone; } + /** + * Set the URI associated to the Submit Button + * + * If you want a normal link and not any form submission, + * see also: setDisableWorkflowOnSubmit(false). + * + * @param string $uri + * @return self + */ public function setSubmitURI($uri) { $this->submitURI = $uri; return $this; @@ -92,6 +101,15 @@ public function getResizeX() { return $this->resizeX; } + /** + * Add a Submit Button and specify its text + * + * If you want to associate an URI for this Button, + * see also: setSubmitURI(). + * + * @param string $text Text shown for that button + * @return self + */ public function addSubmitButton($text = null) { if (!$text) { $text = pht('Okay'); @@ -228,6 +246,14 @@ public function appendForm(AphrontFormView $form) { return $this->appendChild($form->buildLayoutView()); } + /** + * Enable or Disable a Workflow on Submit + * + * For example, if your Submit Button should be a normal link, + * without activating any Workflow, you can set false. + * @param bool $disable_workflow_on_submit + * @return self + */ public function setDisableWorkflowOnSubmit($disable_workflow_on_submit) { $this->disableWorkflowOnSubmit = $disable_workflow_on_submit; return $this; From 7bd4089a269490941236c020a040f104b9748ebe Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 26 Apr 2023 09:49:35 +0200 Subject: [PATCH 069/425] Drag & Drop: fix JavaScript error related to JX.$(undefined) Summary: This change fixes the following recent JavaScript error: Error: Empty ID passed to JX.$()! The regression was introduced here: 90f9da643d1622bcfff3a8903159aeefd1b42520 Closes T15272 Test Plan: - with this change I was able to open Conpherence Persistent Chat and Drop a file Reviewers: O1 Blessed Committers, Dylsss, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: Dylsss, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15272 Differential Revision: https://we.phorge.it/D25134 --- .../js/core/behavior-drag-and-drop-textarea.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js index 879a6a086d..4d8629cd8c 100644 --- a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js +++ b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js @@ -27,11 +27,15 @@ JX.behavior('aphront-drag-and-drop-textarea', function(config) { drop.listen('didUpload', function(file) { JX.TextAreaUtils.insertFileReference(target, file); - var metadata = new JX.RemarkupMetadata(config.remarkupMetadataValue, - config.remarkupMetadataID); - var phids = metadata.getMetadata('attachedFilePHIDs', []); - phids.push(file.getPHID()); - metadata.setMetadata('attachedFilePHIDs', phids); + if(config.remarkupMetadataID) { + // Try to auto-attach files by default + // https://we.phorge.it/T15106 + var metadata = new JX.RemarkupMetadata(config.remarkupMetadataValue, + config.remarkupMetadataID); + var phids = metadata.getMetadata('attachedFilePHIDs', []); + phids.push(file.getPHID()); + metadata.setMetadata('attachedFilePHIDs', phids); + } }); drop.start(); From 1e27a7b89944eed25324c34bb67507a8fe3566b5 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 26 Apr 2023 12:23:49 +0200 Subject: [PATCH 070/425] Fix outdated Celerity Summary: Ref D25134 Ref T15276 Test Plan: - make unit test happy Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15276 Differential Revision: https://we.phorge.it/D25136 --- resources/celerity/map.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index e5588c0b7a..a53735823b 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '609e63d4', - 'differential.pkg.js' => 'f68ea9d1', + 'differential.pkg.js' => '46fcb3af', 'diffusion.pkg.css' => '42c75c37', 'diffusion.pkg.js' => '78c9885d', 'maniphest.pkg.css' => '35995d6d', @@ -474,7 +474,7 @@ 'rsrc/js/core/behavior-copy.js' => 'cf32921f', 'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94', 'rsrc/js/core/behavior-device.js' => 'ac2b1e01', - 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '29e221f6', + 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '6bc7ccf7', 'rsrc/js/core/behavior-fancy-datepicker.js' => 'b545d0a0', 'rsrc/js/core/behavior-form.js' => '55d7b788', 'rsrc/js/core/behavior-gesture.js' => 'b58d1a2a', @@ -590,7 +590,7 @@ 'javelin-behavior-aphlict-listen' => '4e61fa88', 'javelin-behavior-aphlict-status' => 'c3703a16', 'javelin-behavior-aphront-basic-tokenizer' => '3b4899b0', - 'javelin-behavior-aphront-drag-and-drop-textarea' => '29e221f6', + 'javelin-behavior-aphront-drag-and-drop-textarea' => '6bc7ccf7', 'javelin-behavior-aphront-form-disable-on-submit' => '55d7b788', 'javelin-behavior-aphront-more' => '506aa3f4', 'javelin-behavior-audio-source' => '3dc5ad43', @@ -1165,13 +1165,6 @@ 'javelin-stratcom', 'javelin-behavior', ), - '29e221f6' => array( - 'javelin-behavior', - 'javelin-dom', - 'phabricator-drag-and-drop-file-upload', - 'phabricator-textareautils', - 'phabricator-remarkup-metadata', - ), '2a8b62d9' => array( 'multirow-row-manager', 'javelin-install', @@ -1558,6 +1551,13 @@ 'javelin-workflow', 'javelin-magical-init', ), + '6bc7ccf7' => array( + 'javelin-behavior', + 'javelin-dom', + 'phabricator-drag-and-drop-file-upload', + 'phabricator-textareautils', + 'phabricator-remarkup-metadata', + ), '6cfa0008' => array( 'javelin-dom', 'javelin-dynval', From 7b080db57c5bed6d06ab61e2f3ef93f23a8a2122 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 27 Apr 2023 09:25:58 +0200 Subject: [PATCH 071/425] Rename default SSH key name from Phabricator to Phorge Summary: Rename default SSH key name from Phabricator to Phorge Closes T15274 Test Plan: Go to https://we.phorge.it/settings/panel/ssh/ and select `SSH Key Actions > Generate Keypair` Reviewers: O1 Blessed Committers, avivey, valerio.bozzolan Reviewed By: O1 Blessed Committers, avivey, valerio.bozzolan Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15274 Differential Revision: https://we.phorge.it/D25135 --- src/applications/people/storage/PhabricatorUser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 5edfb2d537..82f1affe82 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -1159,7 +1159,7 @@ public function getSSHPublicKeyManagementURI(PhabricatorUser $viewer) { } public function getSSHKeyDefaultName() { - return 'id_rsa_phabricator'; + return 'id_rsa_phorge'; } public function getSSHKeyNotifyPHIDs() { From f3b37413167ecbf6bb6a8f7ecd0d6cdc371b2bfd Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 27 Apr 2023 10:49:06 +0200 Subject: [PATCH 072/425] Fix PHP 8.1 "strlen(null)" exception inCelerityResourceController to make CSS/JS load Summary: On a fresh installation with PHP 8.1, CSS does not load on the account creation page. Closes T15283 Test Plan: After applying this change (and D25137), the account creation page is rendered correctly. See the screenshot in T15283 Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15283 Differential Revision: https://we.phorge.it/D25138 --- .../celerity/controller/CelerityResourceController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/celerity/controller/CelerityResourceController.php b/src/applications/celerity/controller/CelerityResourceController.php index 702fcefb2c..547c881df8 100644 --- a/src/applications/celerity/controller/CelerityResourceController.php +++ b/src/applications/celerity/controller/CelerityResourceController.php @@ -113,7 +113,7 @@ protected function serveResource(array $spec) { $range = AphrontRequest::getHTTPHeader('Range'); - if (strlen($range)) { + if (phutil_nonempty_string($range)) { $response->setContentLength(strlen($data)); list($range_begin, $range_end) = $response->parseHTTPRange($range); From b56d86e48d0f63028cb5739c179d61ece2a24bb1 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 27 Apr 2023 14:07:51 +0200 Subject: [PATCH 073/425] Fix numerous PHP 8.1 "strlen(null)" exceptions preventing homepage to display Summary: Fix numerous PHP 8.1 RuntimeExceptions caused by the deprecation of strlen(null). The strlen() was used in Phabricator to check if a generic value was a non-empty string. For this reason, Phorge adopts phutil_nonempty_string() that checks that. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If your phutil_nonempty_string() throws an exception, just report it to Phorge to evaluate and fix together that specific corner case. Closes T15264 Test Plan: Phorge homepage is displayed on PHP 8.1 after applying these changes Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15264 Differential Revision: https://we.phorge.it/D25132 --- .../config/check/PhabricatorDaemonsSetupCheck.php | 2 +- .../files/view/PhabricatorGlobalUploadTargetView.php | 2 +- .../home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php | 2 +- .../search/engine/PhabricatorProfileMenuEngine.php | 2 +- .../search/engine/PhabricatorProfileMenuItemView.php | 4 ++-- .../search/menuitem/PhabricatorDashboardProfileMenuItem.php | 2 +- .../search/menuitem/PhabricatorEditEngineProfileMenuItem.php | 2 +- .../search/menuitem/PhabricatorManageProfileMenuItem.php | 2 +- src/view/page/PhabricatorStandardPageView.php | 2 +- src/view/page/menu/PhabricatorMainMenuView.php | 2 +- src/view/phui/PHUIObjectItemListView.php | 2 +- src/view/phui/PHUIObjectItemView.php | 3 ++- 12 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php index 608bac675e..df5821665c 100644 --- a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php +++ b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php @@ -53,7 +53,7 @@ protected function executeChecks() { } $expect_user = PhabricatorEnv::getEnvConfig('phd.user'); - if (strlen($expect_user)) { + if (phutil_nonempty_string($expect_user)) { try { $all_daemons = id(new PhabricatorDaemonLogQuery()) diff --git a/src/applications/files/view/PhabricatorGlobalUploadTargetView.php b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php index 76aacbe36f..cd65c9a197 100644 --- a/src/applications/files/view/PhabricatorGlobalUploadTargetView.php +++ b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php @@ -67,7 +67,7 @@ public function render() { require_celerity_resource('global-drag-and-drop-css'); $hint_text = $this->getHintText(); - if (!strlen($hint_text)) { + if (!phutil_nonempty_string($hint_text)) { $hint_text = "\xE2\x87\xAA ".pht('Drop Files to Upload'); } diff --git a/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php b/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php index dbf1586366..c3ebd825db 100644 --- a/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php +++ b/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php @@ -31,7 +31,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php index aedbcc787f..3845c0f127 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php +++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php @@ -1308,7 +1308,7 @@ private function selectViewItem( // render the default view instead. $selected_view = null; - if (strlen($item_id)) { + if (phutil_nonempty_string($item_id)) { $item_views = $view_list->getViewsWithItemIdentifier($item_id); if ($item_views) { $selected_view = head($item_views); diff --git a/src/applications/search/engine/PhabricatorProfileMenuItemView.php b/src/applications/search/engine/PhabricatorProfileMenuItemView.php index d947afcba6..7f0dd1d397 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuItemView.php +++ b/src/applications/search/engine/PhabricatorProfileMenuItemView.php @@ -140,7 +140,7 @@ public function newListItemView() { ->setName($this->getName()); $uri = $this->getURI(); - if (strlen($uri)) { + if (phutil_nonempty_string($uri)) { if ($this->getIsExternalLink()) { if (!PhabricatorEnv::isValidURIForLink($uri)) { $uri = '#'; @@ -176,7 +176,7 @@ public function newListItemView() { } $tooltip = $this->getTooltip(); - if (strlen($tooltip)) { + if (phutil_nonempty_string($tooltip)) { $view->setTooltip($tooltip); } diff --git a/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php index 9d89c52ff8..717bba7c39 100644 --- a/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php @@ -117,7 +117,7 @@ public function getDisplayName( return pht('Archived Dashboard'); } - if (strlen($this->getName($config))) { + if (phutil_nonempty_string($this->getName($config))) { return $this->getName($config); } else { return $dashboard->getName(); diff --git a/src/applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php index 71e3d7e8a5..23d5ad3f30 100644 --- a/src/applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php @@ -71,7 +71,7 @@ public function getDisplayName( if (!$form) { return pht('(Restricted/Invalid Form)'); } - if (strlen($this->getName($config))) { + if (phutil_nonempty_string($this->getName($config))) { return $this->getName($config); } else { return $form->getName(); diff --git a/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php index 89ac4a5633..83c3133705 100644 --- a/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php @@ -31,7 +31,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index 6f6ce56cad..17ce1db8bb 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -188,7 +188,7 @@ public function getTitle() { } } - if (strlen($prefix)) { + if (phutil_nonempty_string($prefix)) { $title = $prefix.' '.$title; } diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php index 6a69c4feaa..f0b2bbbe83 100644 --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -333,7 +333,7 @@ private function renderPhabricatorLogo() { $wordmark_text = PhabricatorCustomLogoConfigType::getLogoWordmark(); - if (!strlen($wordmark_text)) { + if (!phutil_nonempty_string($wordmark_text)) { $wordmark_text = PlatformSymbols::getPlatformServerName(); } diff --git a/src/view/phui/PHUIObjectItemListView.php b/src/view/phui/PHUIObjectItemListView.php index fbc3904586..629feb9139 100644 --- a/src/view/phui/PHUIObjectItemListView.php +++ b/src/view/phui/PHUIObjectItemListView.php @@ -120,7 +120,7 @@ protected function getTagContent() { require_celerity_resource('phui-oi-color-css'); $header = null; - if (strlen($this->header)) { + if (phutil_nonempty_string($this->header)) { $header = phutil_tag( 'h1', array( diff --git a/src/view/phui/PHUIObjectItemView.php b/src/view/phui/PHUIObjectItemView.php index 4bb9c3ea6a..6563101f98 100644 --- a/src/view/phui/PHUIObjectItemView.php +++ b/src/view/phui/PHUIObjectItemView.php @@ -659,7 +659,8 @@ protected function getTagContent() { $this->getImageIcon()); } - if ($image && (strlen($this->href) || strlen($this->imageHref))) { + if ($image && (phutil_nonempty_string($this->href) || + phutil_nonempty_string($this->imageHref))) { $image_href = ($this->imageHref) ? $this->imageHref : $this->href; $image = phutil_tag( 'a', From 74dfe6f971afad00af3ae04839eba2dce37b5739 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 28 Apr 2023 03:41:20 -0700 Subject: [PATCH 074/425] Fix is_absolute test in markup Summary: See Q59. Fixes rP935d7120ee32. Call sites should be happy to use PhutilURI when possible. Test Plan: visit any Repository page. Before - exception, now - data. Reviewers: O1 Blessed Committers!, valerio.bozzolan Reviewed By: valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25139 --- src/infrastructure/javelin/markup.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/javelin/markup.php b/src/infrastructure/javelin/markup.php index 673869a4c5..8a040031cc 100644 --- a/src/infrastructure/javelin/markup.php +++ b/src/infrastructure/javelin/markup.php @@ -77,9 +77,27 @@ function phabricator_form(PhabricatorUser $user, $attributes, $content) { $is_post = (strcasecmp($http_method, 'POST') === 0); $http_action = idx($attributes, 'action'); - $is_absolute_uri = 0; - if (phutil_nonempty_string($http_action)) { + + if ($http_action === null) { + // Not sure what this is. + $is_absolute_uri = false; + + } else if ($http_action instanceof PhutilURI) { + // This is the happy path, I think + + // For now, this is close enough - I suspect we'll stay with "https" schema + // for the rest of eternity. + $protocol = $http_action->getProtocol(); + $is_absolute_uri = ($protocol == 'http' || $protocol == 'https'); + + } else if (is_string($http_action)) { + // Also good path? $is_absolute_uri = preg_match('#^(https?:|//)#', $http_action); + } else { + throw new Exception( + pht( + 'Unexpected object type provided as `action` - %s', + gettype($http_action))); } if ($is_post) { From c37412df45da5bfc7f51f89b5d5e2e711cbb62d2 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 29 Apr 2023 14:39:08 +0200 Subject: [PATCH 075/425] Fix numerous PHP 8.1 "strlen(null)" exceptions trying access Configuration page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If your phutil_nonempty_string() throws an exception, just report it to Phorge to evaluate and fix together that specific corner case. Closes T15287 Test Plan: Applied these three changes and `/config/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15287 Differential Revision: https://we.phorge.it/D25141 --- .../config/controller/PhabricatorConfigConsoleController.php | 4 ++-- src/view/control/AphrontTableView.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/applications/config/controller/PhabricatorConfigConsoleController.php b/src/applications/config/controller/PhabricatorConfigConsoleController.php index 4773a8ab60..f70398bc5f 100644 --- a/src/applications/config/controller/PhabricatorConfigConsoleController.php +++ b/src/applications/config/controller/PhabricatorConfigConsoleController.php @@ -85,14 +85,14 @@ public function newLibraryVersionTable() { $rows = array(); foreach ($versions as $name => $info) { $branchpoint = $info['branchpoint']; - if (strlen($branchpoint)) { + if (phutil_nonempty_string($branchpoint)) { $branchpoint = substr($branchpoint, 0, 12); } else { $branchpoint = null; } $version = $info['hash']; - if (strlen($version)) { + if (phutil_nonempty_string($version)) { $version = substr($version, 0, 12); } else { $version = pht('Unknown'); diff --git a/src/view/control/AphrontTableView.php b/src/view/control/AphrontTableView.php index a3c0a49be4..0e06b97b4a 100644 --- a/src/view/control/AphrontTableView.php +++ b/src/view/control/AphrontTableView.php @@ -135,7 +135,7 @@ public function render() { $col_classes = array(); foreach ($this->columnClasses as $key => $class) { - if (strlen($class)) { + if (phutil_nonempty_string($class)) { $col_classes[] = $class; } else { $col_classes[] = null; From ac99285c57a06ed5699b69242fd6588edd950112 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 29 Apr 2023 14:40:59 +0200 Subject: [PATCH 076/425] Fix numerous PHP 8.1 "strlen(null)" exceptions trying to create a project Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If your phutil_nonempty_string() throws an exception, just report it to Phorge to evaluate and fix together that specific corner case. Closes T15286 Test Plan: Applied these five changes and `/project/view/1/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15286 Differential Revision: https://we.phorge.it/D25140 --- .../menuitem/PhabricatorProjectManageProfileMenuItem.php | 2 +- .../menuitem/PhabricatorProjectMembersProfileMenuItem.php | 2 +- .../menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php | 2 +- .../menuitem/PhabricatorProjectWorkboardProfileMenuItem.php | 2 +- .../editor/PhabricatorApplicationTransactionEditor.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/applications/project/menuitem/PhabricatorProjectManageProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectManageProfileMenuItem.php index 9b8a769318..656bbbe9a3 100644 --- a/src/applications/project/menuitem/PhabricatorProjectManageProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectManageProfileMenuItem.php @@ -31,7 +31,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/project/menuitem/PhabricatorProjectMembersProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectMembersProfileMenuItem.php index 11a57d3a5b..e3c7f9fceb 100644 --- a/src/applications/project/menuitem/PhabricatorProjectMembersProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectMembersProfileMenuItem.php @@ -21,7 +21,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php index b1782e8f1c..39b25c1f00 100644 --- a/src/applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php @@ -29,7 +29,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php index 34152f85e7..2f0fbb284b 100644 --- a/src/applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php @@ -38,7 +38,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 77b5fbfbb6..908c76a106 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -617,7 +617,7 @@ private function getTransactionNewValue( return true; case PhabricatorTransactions::TYPE_SPACE: $space_phid = $xaction->getNewValue(); - if (!strlen($space_phid)) { + if (!phutil_nonempty_string($space_phid)) { // If an install has no Spaces or the Spaces controls are not visible // to the viewer, we might end up with the empty string here instead // of a strict `null`, because some controller just used `getStr()` From 562d36ef5f9e83dba5e3ca0244f9c4af9e1f7b2b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 29 Apr 2023 21:53:11 +0200 Subject: [PATCH 077/425] Fix PHP 8.1 "strlen(null)" exceptions to render the Account Creation page Summary: Fix numerous PHP 8.1 "strlen(null)" exceptions which block rendering the initial Account Creation page in a fresh Phorge installation. The strlen() was used in Phabricator to check if a generic value was a non-empty string. For this reason, Phorge adopts phutil_nonempty_string() that checks that. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If your phutil_nonempty_string() throws an exception, just report it to Phorge to evaluate and fix together that specific corner case. Closes T15279 Test Plan: After these code changes the account creation page got displayed (though without CSS and JS). Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15279 Differential Revision: https://we.phorge.it/D25137 --- .../auth/controller/PhabricatorAuthRegisterController.php | 6 +++--- src/applications/base/controller/PhabricatorController.php | 2 +- src/view/form/control/AphrontFormControl.php | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/applications/auth/controller/PhabricatorAuthRegisterController.php b/src/applications/auth/controller/PhabricatorAuthRegisterController.php index 9fbf30d092..a71e576476 100644 --- a/src/applications/auth/controller/PhabricatorAuthRegisterController.php +++ b/src/applications/auth/controller/PhabricatorAuthRegisterController.php @@ -18,7 +18,7 @@ public function handleRequest(AphrontRequest $request) { $invite = $this->loadInvite(); $is_setup = false; - if (strlen($account_key)) { + if (phutil_nonempty_string($account_key)) { $result = $this->loadAccountForRegistrationOrLinking($account_key); list($account, $provider, $response) = $result; $is_default = false; @@ -244,9 +244,9 @@ public function handleRequest(AphrontRequest $request) { $require_real_name = PhabricatorEnv::getEnvConfig('user.require-real-name'); - $e_username = strlen($value_username) ? null : true; + $e_username = phutil_nonempty_string($value_username) ? null : true; $e_realname = $require_real_name ? true : null; - $e_email = strlen($value_email) ? null : true; + $e_email = phutil_nonempty_string($value_email) ? null : true; $e_password = true; $e_captcha = true; diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php index db9d456094..d1b1fcfc8e 100644 --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -74,7 +74,7 @@ public function willBeginExecution() { $session_engine = new PhabricatorAuthSessionEngine(); $phsid = $request->getCookie(PhabricatorCookies::COOKIE_SESSION); - if (strlen($phsid)) { + if (phutil_nonempty_string($phsid)) { $session_user = $session_engine->loadUserForSession( PhabricatorAuthSession::TYPE_WEB, $phsid); diff --git a/src/view/form/control/AphrontFormControl.php b/src/view/form/control/AphrontFormControl.php index 0125fa87b3..d891636230 100644 --- a/src/view/form/control/AphrontFormControl.php +++ b/src/view/form/control/AphrontFormControl.php @@ -172,7 +172,7 @@ final public function render() { $this->renderInput()); $error = null; - if (strlen($this->getError())) { + if ($this->getError()) { $error = $this->getError(); if ($error === true) { $error = phutil_tag( @@ -187,7 +187,7 @@ final public function render() { } } - if (strlen($this->getLabel())) { + if (phutil_nonempty_string($this->getLabel())) { $label = phutil_tag( 'label', array( @@ -203,7 +203,7 @@ final public function render() { $custom_class .= ' aphront-form-control-nolabel'; } - if (strlen($this->getCaption())) { + if (phutil_nonempty_string($this->getCaption())) { $caption = phutil_tag( 'div', array('class' => 'aphront-form-caption'), From 1b82943419160bd230b8216b2cba9fa9ddf62da7 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 30 Apr 2023 15:12:30 +0200 Subject: [PATCH 078/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering the Diffusion page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15300 Test Plan: Applied these three changes (on top of D25144, D25145, D25146, D25147) and `/diffusion/` finally rendered in web browser. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15300 Differential Revision: https://we.phorge.it/D25150 --- .../diffusion/controller/DiffusionController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index b3595c1b72..aa4f7e3f1e 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -97,19 +97,19 @@ protected function getRepositoryIdentifierFromRequest( AphrontRequest $request) { $short_name = $request->getURIData('repositoryShortName'); - if (strlen($short_name)) { + if (phutil_nonempty_string($short_name)) { // If the short name ends in ".git", ignore it. $short_name = preg_replace('/\\.git\z/', '', $short_name); return $short_name; } $identifier = $request->getURIData('repositoryCallsign'); - if (strlen($identifier)) { + if (phutil_nonempty_string($identifier)) { return $identifier; } $id = $request->getURIData('repositoryID'); - if (strlen($id)) { + if (phutil_nonempty_string($id)) { return (int)$id; } From 456ba873ca018789fa49c06e7a4434c369d77a16 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 30 Apr 2023 15:14:36 +0200 Subject: [PATCH 079/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering the "Add Panel to Dashboard" overlay dialog Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15305 Test Plan: Applied these two changes (on top of `D25144`, `D25145`, `D25146`, `D25147`, `D25150`, `D25151`, `D25152`, `D25153`, `D25154`)) and "Use Results -> Add Panel to Dashboard" overlay dialog finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15305 Differential Revision: https://we.phorge.it/D25155 --- .../PhabricatorDashboardQueryPanelInstallController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php b/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php index a229fcb41c..845ce9ee06 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php @@ -13,12 +13,12 @@ public function handleRequest(AphrontRequest $request) { $e_name = true; $v_engine = $request->getStr('engine'); - if (!strlen($v_engine)) { + if (!phutil_nonempty_string($v_engine)) { $v_engine = $request->getURIData('engineKey'); } $v_query = $request->getStr('query'); - if (!strlen($v_query)) { + if (!phutil_nonempty_string($v_query)) { $v_query = $request->getURIData('queryKey'); } From 4c758f29ae83eeedbeb5338867bc6bf110d12f28 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 30 Apr 2023 15:17:27 +0200 Subject: [PATCH 080/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering Config's Module page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15308 Test Plan: Applied this change (on top of `D25144`, `D25145`, `D25146`, `D25147`, `D25150`, `D25151`, `D25152`, `D25153`, `D25154`, `D25155`) and `/config/module/` rendered in web browser. T15308 Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15308 Differential Revision: https://we.phorge.it/D25158 --- .../controller/module/PhabricatorConfigModuleController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/config/controller/module/PhabricatorConfigModuleController.php b/src/applications/config/controller/module/PhabricatorConfigModuleController.php index 4f3f9c57cb..355166fea6 100644 --- a/src/applications/config/controller/module/PhabricatorConfigModuleController.php +++ b/src/applications/config/controller/module/PhabricatorConfigModuleController.php @@ -9,7 +9,7 @@ public function handleRequest(AphrontRequest $request) { $all_modules = PhabricatorConfigModule::getAllModules(); - if (!strlen($key)) { + if (!phutil_nonempty_string($key)) { $key = head_key($all_modules); } From e65ac7b8800d3397abfced7824a8d316ec573d8f Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 30 Apr 2023 18:24:18 +0200 Subject: [PATCH 081/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering the Conpherence page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15304 Test Plan: Applied these four changes (on top of `D25144`, `D25145`, `D25146`, `D25147`, `D25150`, `D25151`, `D25152`, `D25153`) and `/conpherence/` page finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15304 Differential Revision: https://we.phorge.it/D25154 --- .../conpherence/query/ConpherenceThreadQuery.php | 7 ++++--- .../conpherence/query/ConpherenceThreadSearchEngine.php | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/applications/conpherence/query/ConpherenceThreadQuery.php b/src/applications/conpherence/query/ConpherenceThreadQuery.php index 99fd18878e..34bbf7e6b0 100644 --- a/src/applications/conpherence/query/ConpherenceThreadQuery.php +++ b/src/applications/conpherence/query/ConpherenceThreadQuery.php @@ -135,7 +135,8 @@ protected function loadPage() { } protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { - if ($this->participantPHIDs !== null || strlen($this->fulltext)) { + if ($this->participantPHIDs !== null || + phutil_nonempty_string($this->fulltext)) { return qsprintf($conn_r, 'GROUP BY thread.id'); } else { return $this->buildApplicationSearchGroupClause($conn_r); @@ -152,7 +153,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { id(new ConpherenceParticipant())->getTableName()); } - if (strlen($this->fulltext)) { + if (phutil_nonempty_string($this->fulltext)) { $joins[] = qsprintf( $conn, 'JOIN %T idx ON idx.threadPHID = thread.phid', @@ -234,7 +235,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $this->participantPHIDs); } - if (strlen($this->fulltext)) { + if (phutil_nonempty_string($this->fulltext)) { $where[] = qsprintf( $conn, 'MATCH(idx.corpus) AGAINST (%s IN BOOLEAN MODE)', diff --git a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php index cbaf43b0a9..77e54157cf 100644 --- a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php +++ b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php @@ -106,7 +106,7 @@ protected function renderResultList( $engines = array(); $fulltext = $query->getParameter('fulltext'); - if (strlen($fulltext) && $conpherences) { + if (phutil_nonempty_string($fulltext) && $conpherences) { $context = $this->loadContextMessages($conpherences, $fulltext); $author_phids = array(); From d8d45d343ed4cf52d3a865bb0f6d1bc252b08ed0 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 30 Apr 2023 18:27:43 +0200 Subject: [PATCH 082/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering the Conduit page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15307 Test Plan: Applied these two changes (on top `D25144`, `D25145`, `D25146`, `D25147`, `D25150`, `D25151`, `D25152`, `D25153`, `D25154` locally applied) and `/conduit/` finally rendered in web browser. Also the page /conduit/query/. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15307 Differential Revision: https://we.phorge.it/D25156 --- .../conduit/query/PhabricatorConduitSearchEngine.php | 2 +- src/view/phui/PHUIObjectItemView.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php index 06ba536dfe..3dc5617bb5 100644 --- a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php @@ -39,7 +39,7 @@ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { $query->withIsInternal(false); $contains = $saved->getParameter('nameContains'); - if (strlen($contains)) { + if (phutil_nonempty_string($contains)) { $query->withNameContains($contains); } diff --git a/src/view/phui/PHUIObjectItemView.php b/src/view/phui/PHUIObjectItemView.php index 6563101f98..24f0d7743f 100644 --- a/src/view/phui/PHUIObjectItemView.php +++ b/src/view/phui/PHUIObjectItemView.php @@ -874,7 +874,7 @@ private function renderStatusIcon($icon, $label) { 'class' => 'phui-oi-status-icon', ); - if (strlen($label)) { + if (phutil_nonempty_string($label)) { $options['sigil'] = 'has-tooltip'; $options['meta'] = array('tip' => $label, 'size' => 300); } From 5a0d82df7f86f3f0d99461f8aa85baf246d72a39 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 30 Apr 2023 19:42:16 +0200 Subject: [PATCH 083/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering creation form pages for Legalpad, Macro, Settings Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15309 Test Plan: Applied this change (on top of `D25144`, `D25145`, `D25146`, `D25147`, `D25150`, `D25151`, `D25152`, `D25153`, `D25154`, `D25155`, `D25156`, `D25158` locally applied) and the pages `/legalpad/edit/`, `/macro/create/`, `/settings/builtin/global/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15309 Differential Revision: https://we.phorge.it/D25159 --- .../transactions/editengine/PhabricatorEditEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 60ccdaa401..3ebf6377f2 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -941,7 +941,7 @@ final public function buildResponse() { } } else { $form_key = $request->getURIData('formKey'); - if (strlen($form_key)) { + if (phutil_nonempty_string($form_key)) { $config = $this->loadEditEngineConfigurationWithIdentifier($form_key); if (!$config) { From e3ad37f79259378ceaf2b75dd4937d106145a0f4 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 01:07:22 +0200 Subject: [PATCH 084/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering Upload SSH Public key settings dialog Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15311 Test Plan: Applied this change (on top of D25144, D25145, D25146, D25147, D25151, D25152, D25153) and `Upload SSH Public key` dialog rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15311 Differential Revision: https://we.phorge.it/D25161 --- .../auth/controller/PhabricatorAuthSSHKeyEditController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php b/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php index 6dfc957f48..88bbcb5f8e 100644 --- a/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php +++ b/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php @@ -54,7 +54,7 @@ public function handleRequest(AphrontRequest $request) { $cancel_uri); $v_name = $key->getName(); - $e_name = strlen($v_name) ? null : true; + $e_name = phutil_nonempty_string($v_name) ? null : true; $v_key = $key->getEntireKey(); $e_key = strlen($v_key) ? null : true; From 788aa453a2c00312d3d100a5956cec4f8f48edac Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 15:34:03 +0200 Subject: [PATCH 085/425] Fix numerous PHP 8.1 "strlen(null)" exceptions which block rendering the Applications page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15294 Test Plan: Applied these three changes and `/applications/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15294 Differential Revision: https://we.phorge.it/D25144 --- src/applications/meta/query/PhabricatorAppSearchEngine.php | 2 +- .../controller/PhabricatorApplicationSearchController.php | 2 +- src/view/layout/AphrontSideNavFilterView.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/applications/meta/query/PhabricatorAppSearchEngine.php b/src/applications/meta/query/PhabricatorAppSearchEngine.php index ee938abbbb..e68570fe1c 100644 --- a/src/applications/meta/query/PhabricatorAppSearchEngine.php +++ b/src/applications/meta/query/PhabricatorAppSearchEngine.php @@ -45,7 +45,7 @@ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { ->withUnlisted(false); $name = $saved->getParameter('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { $query->withNameContains($name); } diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index 290623e149..b8394459ba 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -115,7 +115,7 @@ private function processSearchRequest() { if ($this->queryKey == 'advanced') { $run_query = false; $query_key = $request->getStr('query'); - } else if (!strlen($this->queryKey)) { + } else if (!phutil_nonempty_string($this->queryKey)) { $found_query_data = false; if ($request->isHTTPGet() || $request->isQuicksand()) { diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php index b2d5a716d4..6d255f2b30 100644 --- a/src/view/layout/AphrontSideNavFilterView.php +++ b/src/view/layout/AphrontSideNavFilterView.php @@ -145,7 +145,7 @@ public function getBaseURI() { public function selectFilter($key, $default = null) { $this->selectedFilter = $default; - if ($this->menu->getItem($key) && strlen($key)) { + if ($this->menu->getItem($key) && phutil_nonempty_string($key)) { $this->selectedFilter = $key; } return $this->selectedFilter; From f8ae17bb6a1ef22403ae2b5ce36ea708d2abe896 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 15:37:11 +0200 Subject: [PATCH 086/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering the Home page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15296 Test Plan: Applied this change and Home page rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15296 Differential Revision: https://we.phorge.it/D25145 --- .../home/menuitem/PhabricatorHomeProfileMenuItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php b/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php index a002b59da5..9a53e8fad7 100644 --- a/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php +++ b/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php @@ -26,7 +26,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } From bccd4f5981a7e2c347e8e26cf24d8394c882724f Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 15:43:57 +0200 Subject: [PATCH 087/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering the Dashboard page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15295 Test Plan: Applied these four changes (on top of D25144 and D25145) and `/dashboard/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15295 Differential Revision: https://we.phorge.it/D25146 --- .../controller/PhabricatorApplicationSearchController.php | 2 +- .../search/engine/PhabricatorApplicationSearchEngine.php | 2 +- src/view/form/control/AphrontFormTokenizerControl.php | 2 +- src/view/phui/PHUIInfoView.php | 8 +++++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index b8394459ba..b7b512156f 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -775,7 +775,7 @@ private function renderNewUserView( $force_nux) { // Don't render NUX if the user has clicked away from the default page. - if (strlen($this->getQueryKey())) { + if (phutil_nonempty_string($this->getQueryKey())) { return null; } diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 95f1ffafa3..a81e055a56 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -179,7 +179,7 @@ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $original) { $order = $saved->getParameter('order'); $builtin = $query->getBuiltinOrderAliasMap(); - if (strlen($order) && isset($builtin[$order])) { + if (phutil_nonempty_string($order) && isset($builtin[$order])) { $query->setOrder($order); } else { // If the order is invalid or not available, we choose the first diff --git a/src/view/form/control/AphrontFormTokenizerControl.php b/src/view/form/control/AphrontFormTokenizerControl.php index fe80c86f81..7af8fcae32 100644 --- a/src/view/form/control/AphrontFormTokenizerControl.php +++ b/src/view/form/control/AphrontFormTokenizerControl.php @@ -68,7 +68,7 @@ protected function renderInput() { $datasource->setViewer($this->getUser()); $placeholder = null; - if (!strlen($this->placeholder)) { + if (!phutil_nonempty_string($this->placeholder)) { $placeholder = $datasource->getPlaceholderText(); } diff --git a/src/view/phui/PHUIInfoView.php b/src/view/phui/PHUIInfoView.php index cefeedbe5f..58898d40d2 100644 --- a/src/view/phui/PHUIInfoView.php +++ b/src/view/phui/PHUIInfoView.php @@ -19,6 +19,12 @@ final class PHUIInfoView extends AphrontTagView { private $flush; private $icon; + /** + * Set a title + * + * @param string|null $title + * @return self + */ public function setTitle($title) { $this->title = $title; return $this; @@ -147,7 +153,7 @@ protected function getTagContent() { } $title = $this->title; - if ($title || strlen($title)) { + if ($title || phutil_nonempty_string($title)) { $title = phutil_tag( 'h1', array( From 313d3b7bf2ea3aaee587c2af73bf2afa3858f696 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 15:51:48 +0200 Subject: [PATCH 088/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering the Maniphest task creation page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15302 Test Plan: Applied these five changes (on top of D25144, D25145, D25146, D25147, D25151) and `/maniphest/task/edit/form/default/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15302 Differential Revision: https://we.phorge.it/D25152 --- .../transactions/editengine/PhabricatorEditEngine.php | 6 +++--- .../transactions/editfield/PhabricatorEditField.php | 2 +- .../transactions/editfield/PhabricatorTextEditField.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 3ebf6377f2..f70e172a99 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -971,14 +971,14 @@ final public function buildResponse() { } $page_key = $request->getURIData('pageKey'); - if (!strlen($page_key)) { + if (!phutil_nonempty_string($page_key)) { $pages = $this->getPages($object); if ($pages) { $page_key = head_key($pages); } } - if (strlen($page_key)) { + if (phutil_nonempty_string($page_key)) { $page = $this->selectPage($object, $page_key); if (!$page) { return new Aphront404Response(); @@ -1169,7 +1169,7 @@ private function buildEditResponse($object) { if ($this->getIsCreate()) { $template = $request->getStr('template'); - if (strlen($template)) { + if (phutil_nonempty_string($template)) { $template_object = $this->newObjectFromIdentifier( $template, array( diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php index 7eafbc60cf..4457e03bae 100644 --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -418,7 +418,7 @@ public function appendToForm(AphrontFormView $form) { } $instructions = $this->getControlInstructions(); - if (strlen($instructions)) { + if (phutil_nonempty_string($instructions)) { $form->appendRemarkupInstructions($instructions); } diff --git a/src/applications/transactions/editfield/PhabricatorTextEditField.php b/src/applications/transactions/editfield/PhabricatorTextEditField.php index 68854cf2e2..d915d4db75 100644 --- a/src/applications/transactions/editfield/PhabricatorTextEditField.php +++ b/src/applications/transactions/editfield/PhabricatorTextEditField.php @@ -18,7 +18,7 @@ protected function newControl() { $control = new AphrontFormTextControl(); $placeholder = $this->getPlaceholder(); - if (strlen($placeholder)) { + if (phutil_nonempty_string($placeholder)) { $control->setPlaceholder($placeholder); } From 6e8852837004628cbbdb20b1bd72689379276d04 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 15:55:22 +0200 Subject: [PATCH 089/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering list of Active/All projects Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15312 Test Plan: Applied this change (on top of D25144, D25145, D25146, D25147, D25151, D25152, D25153 and `/project/query/active/` rendered correctly in web browser. Tested surfing various pages with and without Maniphest's `maniphest.subtypes`. If you need some example custom fields, this is an example: ``` [ { "key": "default", "name": "Task" }, { "key": "bug", "name": "Bug" }, { "key": "log", "name": "LogSpam" }, { "key": "deadline", "name": "Deadline" } ] ``` Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15312 Differential Revision: https://we.phorge.it/D25162 --- .../PhabricatorEditEngineSubtype.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php b/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php index d177595a2b..0d1b6cf425 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php @@ -43,11 +43,27 @@ public function getIcon() { return $this->icon; } + /** + * Set the text of the tag + * + * This is usually the 'name' key of your subtype map. + * Sometime this is an uppercase text like 'BUG' for a 'bug' subtype name. + * + * @param string|null $text + * @return self + */ public function setTagText($text) { $this->tagText = $text; return $this; } + /** + * Get the text of the tag + * + * @see PhabricatorEditEngineSubtype::setTagText() + * + * @return string|null + */ public function getTagText() { return $this->tagText; } @@ -89,7 +105,7 @@ public function getMutations() { } public function hasTagView() { - return (bool)strlen($this->getTagText()); + return phutil_nonempty_string($this->getTagText()); } public function newTagView() { From 5a76e8d262327c740f8faf8b280fb896f869c4d5 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 22:00:54 +0200 Subject: [PATCH 090/425] Fix PHP 8.1 "strlen(null)" exception which blocks loading icons when creating new Diffusion repository Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15301 Test Plan: Applied this change (on top of D25150) and icons rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15301 Differential Revision: https://we.phorge.it/D25151 --- .../files/controller/PhabricatorFileDataController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php index 8189b30a21..0cc12a85ec 100644 --- a/src/applications/files/controller/PhabricatorFileDataController.php +++ b/src/applications/files/controller/PhabricatorFileDataController.php @@ -69,7 +69,7 @@ public function handleRequest(AphrontRequest $request) { // an initial request for bytes 0-1 of the audio file, and things go south // if we can't respond with a 206 Partial Content. $range = $request->getHTTPHeader('range'); - if (strlen($range)) { + if (phutil_nonempty_string($range)) { list($begin, $end) = $response->parseHTTPRange($range); } From 9aa8d09d3138abb8a5a25f249dace0eabf7ed836 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 22:03:09 +0200 Subject: [PATCH 091/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering the People page of a user Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15318 Test Plan: Applied these five changes on top of D25147 and D25153, and both `/p/username/` and `/people/manage/userid/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15318 Differential Revision: https://we.phorge.it/D25167 --- .../people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php | 2 +- .../people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php | 2 +- .../people/menuitem/PhabricatorPeopleManageProfileMenuItem.php | 2 +- .../menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php | 2 +- .../people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php index 71f3aa1392..470eb52f12 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php index b6c1c446cc..20e1b1f35a 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php index 43d2271a79..127d48ae39 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php index cfa760fcd6..1c823ec091 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php index 5dea58cb29..baffadc6d8 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } From 255325bd046bc502fad9d030dc0dcf245cd8bc3b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 22:05:13 +0200 Subject: [PATCH 092/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering user's Edit Profile page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15319 Test Plan: Applied this change (on top of D25147, D25153, D25167) and `/people/editprofile/userid/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15319 Differential Revision: https://we.phorge.it/D25168 --- .../customfield/field/PhabricatorCustomFieldList.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php index 8ae5529f95..60a5171cdd 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php @@ -89,7 +89,7 @@ public function appendFieldsToForm(AphrontFormView $form) { $field_handles = array_select_keys($handles, $phids[$field_key]); $instructions = $field->getInstructionsForEdit(); - if (strlen($instructions)) { + if (phutil_nonempty_string($instructions)) { $form->appendRemarkupInstructions($instructions); } From 63647f41f826e8061d4802f7d1cba1f014c8aa5e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 22:13:12 +0200 Subject: [PATCH 093/425] Fix PHP 8.1 "strlen(null)" exception[s] which blocks rendering External Editor user settings page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15310 Test Plan: Applied this change (on top of D25144, D25145, D25146, D25147, D25151, D25152, D25153) and `/settings/panel/editor/` correctly rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15310 Differential Revision: https://we.phorge.it/D25160 --- .../settings/panel/PhabricatorExternalEditorSettingsPanel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php b/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php index 3a49b6c3c3..2782851d93 100644 --- a/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php @@ -39,7 +39,7 @@ public function newSettingsPanelEditFormHeadContent( $viewer = $this->getViewer(); $pattern = $viewer->getUserSetting(PhabricatorEditorSetting::SETTINGKEY); - if (!strlen($pattern)) { + if (!phutil_nonempty_string($pattern)) { return null; } From b44665aefe52cc52ef094fd5f09f74e6a2409858 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 22:17:47 +0200 Subject: [PATCH 094/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering Notifications' Advanced Search page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15320 Test Plan: Applied this change and `/notification/query/advanced/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15320 Differential Revision: https://we.phorge.it/D25169 --- .../search/engine/PhabricatorApplicationSearchEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index a81e055a56..effb20eb34 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -872,7 +872,7 @@ protected function readListFromRequest( protected function readBoolFromRequest( AphrontRequest $request, $key) { - if (!strlen($request->getStr($key))) { + if (!phutil_nonempty_string($request->getStr($key))) { return null; } return $request->getBool($key); From 4d88095f2a5321b8f30c609e0caf066686a208f3 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 22:22:18 +0200 Subject: [PATCH 095/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering the Projects page (and log alien values) Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a general replacement. In this specific case we use `phutil_nonempty_stringlike()` since we are not sure if the variable `href` should be just a string or other objects. In order not to leave these cases to chance, we have added a log line, which can be removed in the future. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15303 Ref T15316 Test Plan: Applied this change (on top of D25144, D25145, D25146, D25147, D25150, D25142) and `/project/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15303, T15316 Differential Revision: https://we.phorge.it/D25153 --- src/view/phui/PHUITagView.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/view/phui/PHUITagView.php b/src/view/phui/PHUITagView.php index cd6321a852..dc837c7156 100644 --- a/src/view/phui/PHUITagView.php +++ b/src/view/phui/PHUITagView.php @@ -100,7 +100,24 @@ public function setName($name) { return $this; } + /** + * Set the href attribute + * + * @param string|null $href + * @return self + */ public function setHref($href) { + + // We have not a very clear idea about what this method should receive + // We suspect that PhutilURI should be allowed... but let's log everything! + // https://we.phorge.it/T15316 + if (is_object($href)) { + phlog(sprintf( + 'Received unexpected type for href: %s. '. + 'Please paste this log as comment in https://we.phorge.it/T15316', + get_class($href))); + } + $this->href = $href; return $this; } @@ -126,7 +143,7 @@ public function setSlimShady($is_eminem) { } protected function getTagName() { - return strlen($this->href) ? 'a' : 'span'; + return phutil_nonempty_stringlike($this->href) ? 'a' : 'span'; } public function setContextObject($context_object) { From dff04ba91d45307143cae47cd43a73ae4e067423 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 22:26:09 +0200 Subject: [PATCH 096/425] Fix some PHP 8.1 "strlen(null)" exceptions which block rendering a project page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15313 Test Plan: Applied these three changes (with D25144, D25145, D25146, D25147, D25151, D25152, D25153 on top). Afterwards end up with the unresolved exception covered by T15263. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15313 Differential Revision: https://we.phorge.it/D25163 --- .../project/state/PhabricatorWorkboardViewState.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/applications/project/state/PhabricatorWorkboardViewState.php b/src/applications/project/state/PhabricatorWorkboardViewState.php index 04f8498d49..0d5d94f1f2 100644 --- a/src/applications/project/state/PhabricatorWorkboardViewState.php +++ b/src/applications/project/state/PhabricatorWorkboardViewState.php @@ -41,7 +41,7 @@ public function readFromRequest(AphrontRequest $request) { $this->requestState['filter'] = $request->getStr('filter'); } - if (strlen($request->getURIData('queryKey'))) { + if (phutil_nonempty_string($request->getURIData('queryKey'))) { $this->requestState['filter'] = $request->getURIData('queryKey'); } @@ -169,7 +169,7 @@ public function getOrder() { public function getQueryKey() { $request_query = idx($this->requestState, 'filter'); - if (strlen($request_query)) { + if (phutil_nonempty_string($request_query)) { return $request_query; } @@ -203,7 +203,7 @@ private function getDefaultQueryKey() { $default_query = $project->getDefaultWorkboardFilter(); - if (strlen($default_query)) { + if (phutil_nonempty_string($default_query)) { return $default_query; } From aea1d473795427a83ec4e3ad2c9f5ac56fef0318 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 22:28:57 +0200 Subject: [PATCH 097/425] Fix PHP 8.1 "strlen(null)" and "array_slice(null)" exceptions which block typeahead completion proposals Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. `array_slice()` no longer accepts passing `null` as a parameter. This behavior is deprecated since PHP 8.1. Adding an if clause; not using a Null Coalescing Operator (PHP 7+) as Phorge currently still supports PHP 5.5. Closes T15321 Test Plan: Applied these two changes on top of D25147. Afterwards, typeahead autocompletion proposal dropdowns for the three fields "Assigned To", "Subscribers", "Tags" got displayed in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15321 Differential Revision: https://we.phorge.it/D25170 --- .../PhabricatorTypeaheadModularDatasourceController.php | 2 +- .../datasource/PhabricatorTypeaheadCompositeDatasource.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php index 2d55c5f663..5753c3cded 100644 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php @@ -39,7 +39,7 @@ public function handleRequest(AphrontRequest $request) { $parameters = array(); $raw_parameters = $request->getStr('parameters'); - if (strlen($raw_parameters)) { + if (phutil_nonempty_string($raw_parameters)) { try { $parameters = phutil_json_decode($raw_parameters); } catch (PhutilJSONParserException $ex) { diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php index 5594044c42..d344425d30 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -207,7 +207,11 @@ private function getResultPhase(PhabricatorTypeaheadResult $result) { } protected function sliceResults(array $results) { - $offset = $this->getOffset(); + if ($this->getOffset()) { + $offset = $this->getOffset(); + } else { + $offset = 0; + } $limit = $this->getLimit(); if ($offset || $limit) { From 8f669ea08289d144dfba24f19c6c94e2e444979d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 May 2023 22:31:09 +0200 Subject: [PATCH 098/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering the People page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a general replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Use `phutil_nonempty_scalar()` instead in `PhabricatorSearchDateField.php` as input could only be integer instead of string. Closes T15297 Test Plan: Applied these four changes (on top of D25144, D25145, D25146) and `/people/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15297 Differential Revision: https://we.phorge.it/D25147 --- .../controller/PhabricatorFileDataController.php | 2 +- .../search/field/PhabricatorSearchDateField.php | 4 ++-- src/view/layout/AphrontSideNavFilterView.php | 11 ++++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php index 0cc12a85ec..b292b6f4b8 100644 --- a/src/applications/files/controller/PhabricatorFileDataController.php +++ b/src/applications/files/controller/PhabricatorFileDataController.php @@ -29,7 +29,7 @@ public function handleRequest(AphrontRequest $request) { $request_kind = $request->getURIData('kind'); $is_download = ($request_kind === 'download'); - if (!strlen($alt) || $main_domain == $alt_domain) { + if (!phutil_nonempty_string($alt) || $main_domain == $alt_domain) { // No alternate domain. $should_redirect = false; $is_alternate_domain = false; diff --git a/src/applications/search/field/PhabricatorSearchDateField.php b/src/applications/search/field/PhabricatorSearchDateField.php index 41decd9503..8f43494222 100644 --- a/src/applications/search/field/PhabricatorSearchDateField.php +++ b/src/applications/search/field/PhabricatorSearchDateField.php @@ -17,7 +17,7 @@ public function getValueForQuery($value) { } protected function validateControlValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_scalar($value)) { return; } @@ -32,7 +32,7 @@ protected function validateControlValue($value) { } protected function parseDateTime($value) { - if (!strlen($value)) { + if (!phutil_nonempty_scalar($value)) { return null; } diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php index 6d255f2b30..c7c8b5b534 100644 --- a/src/view/layout/AphrontSideNavFilterView.php +++ b/src/view/layout/AphrontSideNavFilterView.php @@ -92,12 +92,21 @@ public function newLink($key) { return $this->getMenuView()->getItem($key); } + /** + * Add a thing in the menu + * + * @param string $key Internal name + * @param string $name Human name + * @param mixed $uri Destination URI. For example as string or as PhutilURI. + * @param string $type Item type. For example see PHUIListItemView constants. + * @param string $icon Icon name + */ private function addThing($key, $name, $uri, $type, $icon = null) { $item = id(new PHUIListItemView()) ->setName($name) ->setType($type); - if (strlen($icon)) { + if (phutil_nonempty_string($icon)) { $item->setIcon($icon); } From 1d8483a9bd850c2e1762d07b1e691a2a6ace88fc Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 3 May 2023 10:20:24 +0200 Subject: [PATCH 099/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering the "Manage Menu" page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. However, the parameter `$item_id` is a numeric value when populated. Thus replace `strlen()` with `phutil_nonempty_scalar()` as `phutil_nonempty_string()` could break a Conduit API consumer sending a numeric value. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_scalar() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15317 Test Plan: Applied this change and `/home/menu/configure/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15317 Differential Revision: https://we.phorge.it/D25166 --- src/applications/search/engine/PhabricatorProfileMenuEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php index 3845c0f127..8799ae8f0b 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php +++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php @@ -135,7 +135,7 @@ public function buildResponse() { if ($is_view) { $selected_item = $this->selectViewItem($view_list, $item_id); } else { - if (!strlen($item_id)) { + if (!phutil_nonempty_scalar($item_id)) { $item_id = self::ITEM_MANAGE; } $selected_item = $this->selectEditItem($view_list, $item_id); From 0ceb44c38af528387c1c417c2c0883f729eea7f0 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 3 May 2023 10:24:32 +0200 Subject: [PATCH 100/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering Configuration's Settings page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15324 Test Plan: Applied this change and `/config/settings/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15324 Differential Revision: https://we.phorge.it/D25171 --- .../settings/PhabricatorConfigSettingsListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php b/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php index 8513150134..0d34250bb8 100644 --- a/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php +++ b/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php @@ -7,7 +7,7 @@ public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $filter = $request->getURIData('filter'); - if (!strlen($filter)) { + if (!phutil_nonempty_string($filter)) { $filter = 'settings'; } From b0cfb6ca6aca0a30c977257b9c8e901059e3c526 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 3 May 2023 12:49:10 +0200 Subject: [PATCH 101/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering project's Configure Menu page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15327 Test Plan: Applied this change (on top of D25166) and `/project/1/item/configure/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15327 Differential Revision: https://we.phorge.it/D25173 --- .../menuitem/PhabricatorProjectDetailsProfileMenuItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/project/menuitem/PhabricatorProjectDetailsProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectDetailsProfileMenuItem.php index a3021e0239..ec7b88d599 100644 --- a/src/applications/project/menuitem/PhabricatorProjectDetailsProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectDetailsProfileMenuItem.php @@ -31,7 +31,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } From 8a3e063000c593c2730a01687723b5b02301c3f8 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 3 May 2023 12:50:27 +0200 Subject: [PATCH 102/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering Auth's Customize Messages page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15325 Test Plan: Applied these three changes and all seven subpages (e.g. `/auth/message/user.edit.username/`) finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15325 Differential Revision: https://we.phorge.it/D25172 --- .../message/PhabricatorAuthMessageViewController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php b/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php index 5665744463..a1abb6d37c 100644 --- a/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php +++ b/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php @@ -103,7 +103,7 @@ private function buildPropertiesView(PhabricatorAuthMessage $message) { ->setViewer($viewer); $full_description = $message_type->getFullDescription(); - if (strlen($full_description)) { + if (phutil_nonempty_string($full_description)) { $view->addTextContent(new PHUIRemarkupView($viewer, $full_description)); } else { $short_description = $message_type->getShortDescription(); @@ -111,7 +111,7 @@ private function buildPropertiesView(PhabricatorAuthMessage $message) { } $message_text = $message->getMessageText(); - if (strlen($message_text)) { + if (phutil_nonempty_string($message_text)) { $view->addSectionHeader( pht('Message Preview'), PHUIPropertyListView::ICON_SUMMARY); @@ -120,7 +120,7 @@ private function buildPropertiesView(PhabricatorAuthMessage $message) { } $default_text = $message_type->getDefaultMessageText(); - if (strlen($default_text)) { + if (phutil_nonempty_string($default_text)) { $view->addSectionHeader( pht('Default Message'), PHUIPropertyListView::ICON_SUMMARY); From 953726d71bfa28f7ab570e9b1bdc8bd53d0bffa0 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 3 May 2023 19:01:46 +0200 Subject: [PATCH 103/425] Fix PHP 8.1 "strlen(null)" exception which blocks creating a project with an empty Description field Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes second half of T15331 Test Plan: Applied these two changes (one in Arcanist, one in Phorge). Project with empty Description field was created and `/project/view/projectid/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15331 Differential Revision: https://we.phorge.it/D25177 --- .../standard/PhabricatorStandardCustomFieldRemarkup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php index b0b9a3ef8e..54f54c7503 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php @@ -30,7 +30,7 @@ public function getApplicationTransactionRemarkupBlocks( public function renderPropertyViewValue(array $handles) { $value = $this->getFieldValue(); - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return null; } From 8eaa7c1ccf3d75d438e83e074a28ea7de1f15d37 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 4 May 2023 10:48:10 +0200 Subject: [PATCH 104/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering a config page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15336 Test Plan: Applied this change and `/config/edit/load-libraries/` correctly rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15336 Differential Revision: https://we.phorge.it/D25181 --- .../config/option/PhabricatorConfigOption.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/applications/config/option/PhabricatorConfigOption.php b/src/applications/config/option/PhabricatorConfigOption.php index 6d7f88bdfb..6b38bbb6d6 100644 --- a/src/applications/config/option/PhabricatorConfigOption.php +++ b/src/applications/config/option/PhabricatorConfigOption.php @@ -156,11 +156,22 @@ public function getSummary() { return $this->summary; } + /** + * Set the human Description of this Config + * + * @param string|null $description Description as raw Remarkup + * @return self + */ public function setDescription($description) { $this->description = $description; return $this; } + /** + * Get the human Description of this Config + * + * @return string|null Description as raw Remarkup + */ public function getDescription() { return $this->description; } @@ -205,7 +216,7 @@ public function setCustomData($data) { public function newDescriptionRemarkupView(PhabricatorUser $viewer) { $description = $this->getDescription(); - if (!strlen($description)) { + if (!phutil_nonempty_string($description)) { return null; } From b0044bad621ba70fb85304d2945e5d64f2405381 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 5 May 2023 12:52:15 +0200 Subject: [PATCH 105/425] Fix PHP 8.1 "strlen(null)" exception which blocks creating personal and global Herald rules Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15348 Test Plan: Applied this change (on top of D25157) and creation form at `/herald/edit/?content_type=HeraldPreCommitRefAdapter&rule_type=personal` rendered in web browser. Same applies for other types of personal and global Herald rules. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15348 Differential Revision: https://we.phorge.it/D25190 --- .../contentsource/PhabricatorContentSource.php | 7 +++++++ .../contentsource/PhabricatorUnknownContentSource.php | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/infrastructure/contentsource/PhabricatorContentSource.php b/src/infrastructure/contentsource/PhabricatorContentSource.php index ee77052113..de36f50b1f 100644 --- a/src/infrastructure/contentsource/PhabricatorContentSource.php +++ b/src/infrastructure/contentsource/PhabricatorContentSource.php @@ -81,6 +81,13 @@ final public function serialize() { )); } + /** + * Get the internal source name + * + * This is usually coming from a SOURCECONST constant. + * + * @return string|null + */ final public function getSource() { return $this->source; } diff --git a/src/infrastructure/contentsource/PhabricatorUnknownContentSource.php b/src/infrastructure/contentsource/PhabricatorUnknownContentSource.php index a0dfd042d7..7b8c063b8a 100644 --- a/src/infrastructure/contentsource/PhabricatorUnknownContentSource.php +++ b/src/infrastructure/contentsource/PhabricatorUnknownContentSource.php @@ -7,7 +7,7 @@ final class PhabricatorUnknownContentSource public function getSourceName() { $source = $this->getSource(); - if (strlen($source)) { + if ($source) { return pht('Unknown ("%s")', $source); } else { return pht('Unknown'); From f4d28119e8493f32bab68a92f693096fb2c8b6e7 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 5 May 2023 14:50:51 +0200 Subject: [PATCH 106/425] Fix PHP 8.1 "strlen(null)" exception when DarkConsole is enabled Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. This is not visible in the web user interface, only in the PHP error log. Closes T15344 Test Plan: Applied this change, on top of D25165 locally applied, and no stacktrace is printed anymore in the PHP error log. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15344 Differential Revision: https://we.phorge.it/D25188 --- src/applications/console/controller/DarkConsoleController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/applications/console/controller/DarkConsoleController.php b/src/applications/console/controller/DarkConsoleController.php index 3a677920ee..3c4f82feba 100644 --- a/src/applications/console/controller/DarkConsoleController.php +++ b/src/applications/console/controller/DarkConsoleController.php @@ -25,8 +25,9 @@ public function handleRequest(AphrontRequest $request) { return $response; } + // This should be '0' when closed and '1' when opened $visible = $request->getStr('visible'); - if (strlen($visible)) { + if (phutil_nonempty_string($visible)) { $this->writeDarkConsoleSetting( PhabricatorDarkConsoleVisibleSetting::SETTINGKEY, (int)$visible); From a3861edc194629af3a20a440dbdec875906b314d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 5 May 2023 14:53:26 +0200 Subject: [PATCH 107/425] Fix PHP 8.1 "strlen(null)" exception which blocks editing a Dashboard Tab Panel Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15345 Test Plan: After applying this change, the "Edit Panel" page at `/dashboard/panel/edit/2/` correctly rendered in the web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15345 Differential Revision: https://we.phorge.it/D25189 --- .../panel/PhabricatorDashboardPanelEditController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php index 4ab76d18b5..f40c0a05a1 100644 --- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php +++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php @@ -15,7 +15,7 @@ public function handleRequest(AphrontRequest $request) { // editing. $context_phid = $request->getStr('contextPHID'); - if (strlen($context_phid)) { + if (phutil_nonempty_string($context_phid)) { $context = id(new PhabricatorObjectQuery()) ->setViewer($viewer) ->withPHIDs(array($context_phid)) From ab69bcccd9829eb05015ebfff083192b6735df87 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 6 May 2023 04:03:37 +0200 Subject: [PATCH 108/425] Fix PHP 8.1 "strlen(null)" exceptions which block adding Dashboard to Project Menu Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15339 Test Plan: Applied these four changes and `/dashboard/install/1/project/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15339 Differential Revision: https://we.phorge.it/D25184 --- .../install/PhabricatorDashboardObjectInstallWorkflow.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php b/src/applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php index eb1be21954..27e1335f6e 100644 --- a/src/applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php +++ b/src/applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php @@ -17,16 +17,16 @@ public function handleRequest(AphrontRequest $request) { $target_identifier = head($target_tokens); } - if (!strlen($target_identifier)) { + if (!phutil_nonempty_string($target_identifier)) { $target_identifier = $request->getStr('target'); } - if (!strlen($target_identifier)) { + if (!phutil_nonempty_string($target_identifier)) { $target_identifier = $this->getMode(); } $target = null; - if (strlen($target_identifier)) { + if (phutil_nonempty_string($target_identifier)) { $targets = array(); if (ctype_digit($target_identifier)) { @@ -74,7 +74,7 @@ public function handleRequest(AphrontRequest $request) { } $errors = array(); - if (strlen($target_identifier)) { + if (phutil_nonempty_string($target_identifier)) { if (!$target) { $errors[] = pht('Choose a valid object.'); } else if (!$can_edit) { From 519acd9a104eedf9daee3bce2a9588668dbe5744 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 6 May 2023 04:05:16 +0200 Subject: [PATCH 109/425] Fix PHP 8.1 "strlen(null)" exception which blocks Space creation Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15340 Test Plan: Applied this change and page `/S3` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15340 Differential Revision: https://we.phorge.it/D25185 --- .../xaction/PhabricatorSpacesNamespaceNameTransaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/spaces/xaction/PhabricatorSpacesNamespaceNameTransaction.php b/src/applications/spaces/xaction/PhabricatorSpacesNamespaceNameTransaction.php index d7fcbc2c7a..d88425598f 100644 --- a/src/applications/spaces/xaction/PhabricatorSpacesNamespaceNameTransaction.php +++ b/src/applications/spaces/xaction/PhabricatorSpacesNamespaceNameTransaction.php @@ -15,7 +15,7 @@ public function applyInternalEffects($object, $value) { public function getTitle() { $old = $this->getOldValue(); - if (!strlen($old)) { + if (!phutil_nonempty_string($old)) { return pht( '%s created this space.', $this->renderAuthor()); From 6c692ddbbba858e790751dbb6e1a199dc303b398 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 6 May 2023 04:06:44 +0200 Subject: [PATCH 110/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering page of a File Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15341 Test Plan: Applied these four changes and file page at `/F91` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15341 Differential Revision: https://we.phorge.it/D25186 --- .../files/controller/PhabricatorFileViewController.php | 2 +- .../document/render/PhabricatorDocumentRenderingEngine.php | 4 ++-- src/applications/files/storage/PhabricatorFile.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/applications/files/controller/PhabricatorFileViewController.php b/src/applications/files/controller/PhabricatorFileViewController.php index 389fc66247..2dea0a51ce 100644 --- a/src/applications/files/controller/PhabricatorFileViewController.php +++ b/src/applications/files/controller/PhabricatorFileViewController.php @@ -311,7 +311,7 @@ private function buildPropertyViews( $file->getStorageHandle()); $custom_alt = $file->getCustomAltText(); - if (strlen($custom_alt)) { + if (phutil_nonempty_string($custom_alt)) { $finfo->addProperty(pht('Custom Alt Text'), $custom_alt); } diff --git a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php index 5a4cb77340..f43689bbba 100644 --- a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php +++ b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php @@ -60,12 +60,12 @@ final public function newDocumentView(PhabricatorDocumentRef $ref) { } $encode_setting = $request->getStr('encode'); - if (strlen($encode_setting)) { + if (phutil_nonempty_string($encode_setting)) { $engine->setEncodingConfiguration($encode_setting); } $highlight_setting = $request->getStr('highlight'); - if (strlen($highlight_setting)) { + if (phutil_nonempty_string($highlight_setting)) { $engine->setHighlightingConfiguration($highlight_setting); } diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index 355aa2d215..44e35c6b72 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -1278,7 +1278,7 @@ public function getImageWidth() { public function getAltText() { $alt = $this->getCustomAltText(); - if (strlen($alt)) { + if (phutil_nonempty_string($alt)) { return $alt; } From 651e3f7263b58e46cd65285cce7ba266af480aef Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 6 May 2023 04:08:07 +0200 Subject: [PATCH 111/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering a File page when Alt Text was altered Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15342 Test Plan: Applied these two changes on top of D25186. Added an Alt Text to a file; page of the File now rendered correctly in web browser. Removed Alt Text from a file; page of the File now rendered correctly in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15342 Differential Revision: https://we.phorge.it/D25187 --- .../files/xaction/PhabricatorFileAltTextTransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php b/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php index dcf3c6f76f..c3151910e4 100644 --- a/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php +++ b/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php @@ -27,12 +27,12 @@ public function getTitle() { $old_value = $this->getOldValue(); $new_value = $this->getNewValue(); - if (!strlen($old_value)) { + if (!phutil_nonempty_string($old_value)) { return pht( '%s set the alternate text for this file to %s.', $this->renderAuthor(), $this->renderNewValue()); - } else if (!strlen($new_value)) { + } else if (!phutil_nonempty_string($new_value)) { return pht( '%s removed the alternate text for this file (was %s).', $this->renderAuthor(), From e9e2c954132cf9d548e0ac9eb7f75c88f8308c8d Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 6 May 2023 15:55:44 +0200 Subject: [PATCH 112/425] Fix regression in PHUIObjectItemView.php:662: allow PhutilURI and other stringlike Summary: This fixes this specific exception that can happen with whatever PHP version in some pages: Call to phutil_nonempty_string() expected null or a string, got: PhutilURI from PHUIObjectItemView.php:662 The regression was introduced since: b56d86e48d0f63028cb5739c179d61ece2a24bb1 The problem is caused by the fact that we are trying to introduce very strict checks, and sometime we are too much strict. In this specific case we use `phutil_nonempty_stringlike()` since it also allows objects with a `__toString()` method. In order not to leave these cases to chance, we have added a log line, which can be removed in the future. If you see this log line, report it in the respective Task. Thank you! Closes T15306 Ref T15316 Test Plan: 1. {nav Dashboard > Add a Panel}: no crash 2. {nav Diffusion repo > README}: no crash 3. {nav Herald > Create}: no crash Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, aklapper, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15306, T15316 Differential Revision: https://we.phorge.it/D25157 --- src/view/phui/PHUIObjectItemView.php | 60 +++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/view/phui/PHUIObjectItemView.php b/src/view/phui/PHUIObjectItemView.php index 24f0d7743f..600db9f3b7 100644 --- a/src/view/phui/PHUIObjectItemView.php +++ b/src/view/phui/PHUIObjectItemView.php @@ -83,11 +83,29 @@ public function getObject() { return $this->object; } + /** + * Set the href attribute + * + * @param string|PhutilURI|null $href + * @return self + */ public function setHref($href) { + + // We have not a very clear idea about what this method should receive + // So, let's log alien stuff for some time + // https://we.phorge.it/T15316 + self::requireValidHref($href, 'href'); + $this->href = $href; return $this; } + /** + * Get the href attribute + * + * @see PHUIObjectItemView::setHref() + * @return string|PhutilURI|null + */ public function getHref() { return $this->href; } @@ -136,7 +154,19 @@ public function setImageURI($image_uri) { return $this; } + /** + * Set the image href attribute + * + * @param string|PhutilURI|null $image_href + * @return self + */ public function setImageHref($image_href) { + + // We have not a very clear idea about what this method should receive + // So, let's log alien stuff for some time + // https://we.phorge.it/T15316 + self::requireValidHref($image_href, 'image_href'); + $this->imageHref = $image_href; return $this; } @@ -659,8 +689,8 @@ protected function getTagContent() { $this->getImageIcon()); } - if ($image && (phutil_nonempty_string($this->href) || - phutil_nonempty_string($this->imageHref))) { + if ($image && (phutil_nonempty_stringlike($this->href) || + phutil_nonempty_stringlike($this->imageHref))) { $image_href = ($this->imageHref) ? $this->imageHref : $this->href; $image = phutil_tag( 'a', @@ -899,4 +929,30 @@ private function renderHandleIcon(PhabricatorObjectHandle $handle, $label) { return javelin_tag('span', $options, ''); } + + /** + * Receive a href attribute and check if it has expected values + * + * TODO: Feel free to remove after 2023, if no more new reports arrive. + * + * https://we.phorge.it/T15316 + * + * @param mixed $href Value to be checked + * @param string $variable_name Human reference + */ + private static function requireValidHref($href, $variable_name) { + + // We have not a very clear idea about what a "href" should be + if (is_object($href) && !($href instanceof PhutilURI)) { + + // We log stuff with a kind stack trace + phlog(new Exception(pht( + 'The variable %s received an unexpected type: %s. '. + 'Please share this stack trace as comment in Task %s', + $variable_name, + get_class($href), + 'https://we.phorge.it/T15316'))); + } + } + } From 1b4015d2c1ff9d79001a8ee516d98890b2064481 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 7 May 2023 12:35:30 +0200 Subject: [PATCH 113/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering Pholio mock preview Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15353 Test Plan: Applied this change and `/pholio/create/` displayed an image preview after uploading an image file. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15353 Differential Revision: https://we.phorge.it/D25193 --- .../pholio/controller/PholioImageUploadController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/pholio/controller/PholioImageUploadController.php b/src/applications/pholio/controller/PholioImageUploadController.php index 0ff5e061f5..44d7ea7eed 100644 --- a/src/applications/pholio/controller/PholioImageUploadController.php +++ b/src/applications/pholio/controller/PholioImageUploadController.php @@ -18,7 +18,7 @@ public function handleRequest(AphrontRequest $request) { return new Aphront404Response(); } - if (!strlen($title)) { + if (!phutil_nonempty_string($title)) { $title = $file->getName(); } From 8495060f82404f13826599f5f53355ab282c06a5 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 8 May 2023 13:17:58 +0200 Subject: [PATCH 114/425] Fix PHP 8.1 "strlen(null)" exception which blocks blog creation Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15355 Test Plan: Applied this change and `/phame/blog/manage/3/` correctly rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15355 Differential Revision: https://we.phorge.it/D25194 --- src/applications/phame/storage/PhameBlog.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index 95507d61dc..717658ec03 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -169,7 +169,7 @@ public function validateCustomDomain($domain_full_uri) { } public function getLiveURI() { - if (strlen($this->getDomain())) { + if (phutil_nonempty_string($this->getDomain())) { return $this->getExternalLiveURI(); } else { return $this->getInternalLiveURI(); From eba8012d3e805d76c8ab85dfdc657049bd135404 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 8 May 2023 13:19:10 +0200 Subject: [PATCH 115/425] Fix PHP 8.1 "strlen(null)" exception which blocks blog rendering Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15356 Test Plan: Applied this change change on top of D25194 and `/phame/blog/view/1/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15356 Differential Revision: https://we.phorge.it/D25195 --- src/applications/phame/controller/PhameLiveController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/phame/controller/PhameLiveController.php b/src/applications/phame/controller/PhameLiveController.php index 472f73c8c1..e87cfad138 100644 --- a/src/applications/phame/controller/PhameLiveController.php +++ b/src/applications/phame/controller/PhameLiveController.php @@ -87,7 +87,7 @@ protected function setupLiveEnvironment() { $this->isExternal = $is_external; $this->isLive = $is_live; - if (strlen($post_id)) { + if (phutil_nonempty_string($post_id)) { $post_query = id(new PhamePostQuery()) ->setViewer($viewer) ->needHeaderImage(true) From e5caf4333e5ebc8ebcb85b128193e56936bb81cf Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 8 May 2023 13:20:00 +0200 Subject: [PATCH 116/425] Fix PHP 8.1 "strlen(null)" exception which blocks creating a blog post Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15357 Test Plan: Applied this change and created blog post at `/phame/post/view/3/blogpost/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15357 Differential Revision: https://we.phorge.it/D25196 --- src/applications/phame/storage/PhamePost.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index 21149f07c2..5291bcd465 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -67,7 +67,8 @@ public function getLiveURI() { $blog = $this->getBlog(); $is_draft = $this->isDraft(); $is_archived = $this->isArchived(); - if (strlen($blog->getDomain()) && !$is_draft && !$is_archived) { + if (phutil_nonempty_string($blog->getDomain()) && + !$is_draft && !$is_archived) { return $this->getExternalLiveURI(); } else { return $this->getInternalLiveURI(); From d74e9d825a2cc42f9f04eeded768c22860023097 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 8 May 2023 13:21:14 +0200 Subject: [PATCH 117/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering most applications' Configure pages Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Added also a small inline documentation to better leave what I find. Closes T15358 Test Plan: Applied this change change and way more applications' Configure pages finally rendered in web browser. For example, `/applications/view/PhabricatorAuditApplication/` is now correctly rendered. (However, some pages expose followup exceptions to be handled in separate tasks.) Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15358 Differential Revision: https://we.phorge.it/D25197 --- src/applications/base/PhabricatorApplication.php | 5 +++++ .../PhabricatorApplicationDetailViewController.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index ba1edc26d3..9e1ea7d5ba 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -235,6 +235,11 @@ public function getHelpDocumentationArticles(PhabricatorUser $viewer) { return array(); } + /** + * Get the Application Overview in raw Remarkup + * + * @return string|null + */ public function getOverview() { return null; } diff --git a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php index 8be6d73a1d..8cceeb7396 100644 --- a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php +++ b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php @@ -114,7 +114,7 @@ private function buildPropertySectionView( } $overview = $application->getOverview(); - if (strlen($overview)) { + if (phutil_nonempty_string($overview)) { $overview = new PHUIRemarkupView($viewer, $overview); $properties->addSectionHeader( pht('Overview'), PHUIPropertyListView::ICON_SUMMARY); From 3819f7ff196e494950d938f509a67795a4f6e99a Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 8 May 2023 14:33:01 +0200 Subject: [PATCH 118/425] Fix PHP 8.1 "ltrim(null)" exception which blocks rendering five applications' Configure pages Summary: Since PHP 8.1, passing a null string to `ltrim(string $string, string $characters)` is deprecated. Thus do not check for `$path = null` but check for `$path = ''` before passing `$path` as the `$string` parameter to `ltrim()`, like src/applications/settings/panel/PhabricatorSettingsPanel.php already does. Closes T15359 Test Plan: Applied this change (on top of D25197) and five applications' Configure pages (Differential, Maniphest, Files, Paste, and Ponder) finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15359 Differential Revision: https://we.phorge.it/D25198 --- .../panel/PhabricatorApplicationConfigurationPanel.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php b/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php index 358a6f753b..d96132c9b0 100644 --- a/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php +++ b/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php @@ -24,7 +24,13 @@ public function getApplication() { return $this->application; } - public function getPanelURI($path = null) { + /** + * Get the URI for this application configuration panel. + * + * @param string? Optional path to append. + * @return string Relative URI for the panel. + */ + public function getPanelURI($path = '') { $app_key = get_class($this->getApplication()); $panel_key = $this->getPanelKey(); $base = "/applications/panel/{$app_key}/{$panel_key}/"; From aa8baf1c8b59c81b9d5549ee4ad8f53aa1415d1a Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 8 May 2023 14:34:01 +0200 Subject: [PATCH 119/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering a project's page when Reports prototype enabled Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. This is exactly the same case as D25140. Closes T15360 Test Plan: Applied this change and `/project/view/1/` rendered the workboard in the web browser as expected. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15360 Differential Revision: https://we.phorge.it/D25199 --- .../menuitem/PhabricatorProjectReportsProfileMenuItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/project/menuitem/PhabricatorProjectReportsProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectReportsProfileMenuItem.php index 8639b6d72c..3b0acdf375 100644 --- a/src/applications/project/menuitem/PhabricatorProjectReportsProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectReportsProfileMenuItem.php @@ -46,7 +46,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } From 25e93501c39e40d472a14f075a0ee31ce08231e7 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 8 May 2023 14:35:55 +0200 Subject: [PATCH 120/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering Phortune account page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15363 Test Plan: Applied this change and "Default Account" page `/phortune/account/1/details/` rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15363 Differential Revision: https://we.phorge.it/D25201 --- .../phortune/xaction/PhortuneAccountNameTransaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/phortune/xaction/PhortuneAccountNameTransaction.php b/src/applications/phortune/xaction/PhortuneAccountNameTransaction.php index 08b8b69422..20272fda27 100644 --- a/src/applications/phortune/xaction/PhortuneAccountNameTransaction.php +++ b/src/applications/phortune/xaction/PhortuneAccountNameTransaction.php @@ -17,7 +17,7 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (strlen($old) && strlen($new)) { + if (phutil_nonempty_string($old) && phutil_nonempty_string($new)) { return pht( '%s renamed this account from %s to %s.', $this->renderAuthor(), From 1b08be518ef6219f93dcdad1e34c21855bcdecd4 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 8 May 2023 21:42:34 +0200 Subject: [PATCH 121/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering error pages for Packages' Create Package and Fund's Create Initiative Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15365 Test Plan: Applied this change and `/packages/package/edit/form/default/` and `/fund/create/` finally rendered in web browser, showing the expected error messages about not having entered any data in the creation page. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15365 Differential Revision: https://we.phorge.it/D25202 --- .../transactions/storage/PhabricatorModularTransactionType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/transactions/storage/PhabricatorModularTransactionType.php b/src/applications/transactions/storage/PhabricatorModularTransactionType.php index 7d5e3c533e..3840bcdf9c 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransactionType.php +++ b/src/applications/transactions/storage/PhabricatorModularTransactionType.php @@ -339,7 +339,7 @@ final protected function isEmptyTextTransaction($value, array $xactions) { $value = $xaction->getNewValue(); } - return !strlen($value); + return !phutil_nonempty_string($value); } /** From 8d2b481bb5a83d1a4ce82014cabdc83fa3ca432d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 8 May 2023 23:12:22 +0200 Subject: [PATCH 122/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering page to import ICS calendar file Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15362 Test Plan: Applied these two changes and `/calendar/import/edit/` correctly rendered the "Create Import" page in the web browser, with the expected error about the missing ICS file (instead of an exception stacktrace). Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15362 Differential Revision: https://we.phorge.it/D25200 --- .../calendar/import/PhabricatorCalendarICSFileImportEngine.php | 2 +- .../xaction/PhabricatorCalendarImportICSFileTransaction.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php index c37ba6c944..4fde813223 100644 --- a/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php @@ -56,7 +56,7 @@ public function newEditEngineFields( public function getDisplayName(PhabricatorCalendarImport $import) { $filename_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_NAME; $filename = $import->getParameter($filename_key); - if (strlen($filename)) { + if (phutil_nonempty_string($filename)) { return pht('ICS File "%s"', $filename); } else { return pht('ICS File'); diff --git a/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php index 26a968d13d..51abdd8cad 100644 --- a/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php +++ b/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php @@ -53,7 +53,7 @@ public function validateTransactions($object, array $xactions) { $new_value = $object->getParameter(self::PARAMKEY_FILE); foreach ($xactions as $xaction) { $new_value = $xaction->getNewValue(); - if (!strlen($new_value)) { + if (!phutil_nonempty_string($new_value)) { continue; } From ac4c95169973e742ea768ebd103bb2142c3bdc7d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 9 May 2023 18:37:02 +0200 Subject: [PATCH 123/425] Fix PHP 8.1 "strlen(null)" exception which blocks dragging a task to a Workboard Column Summary: On a Workboard without any "Group by ", when moving a Task from a Column to another, in PHP 8.1 there was a crash caused by a null passed to `strlen()`. `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15373 Test Plan: Applied this change and afterwards dragging a Task card to another Column succeeded, with or without a "Group by " query. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15373 Differential Revision: https://we.phorge.it/D25207 --- .../project/controller/PhabricatorProjectMoveController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php index 1fd8b3c677..7f25905748 100644 --- a/src/applications/project/controller/PhabricatorProjectMoveController.php +++ b/src/applications/project/controller/PhabricatorProjectMoveController.php @@ -24,9 +24,11 @@ public function handleRequest(AphrontRequest $request) { $ordering = id(clone $ordering) ->setViewer($viewer); + // When the Workboard view is "Group By " the header provides + // that context in JSON form $edit_header = null; $raw_header = $request->getStr('header'); - if (strlen($raw_header)) { + if (phutil_nonempty_string($raw_header)) { $edit_header = phutil_json_decode($raw_header); } else { $edit_header = array(); From c86364c2aa6d8e81017028f625d4753bac64a8b8 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 10 May 2023 10:27:07 +0200 Subject: [PATCH 124/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering user page after creation with Welcome Email enabled Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15374 Test Plan: Applied these two changes, performed the user creation steps again, and afterwards the user page at `/p/user5/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15374 Differential Revision: https://we.phorge.it/D25208 --- .../people/mail/PhabricatorPeopleWelcomeMailEngine.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php index 868cc13da1..4bbbb2466e 100644 --- a/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php +++ b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php @@ -118,14 +118,14 @@ private function newBody() { $recipient = $this->getRecipient(); $custom_body = $this->getWelcomeMessage(); - if (strlen($custom_body)) { + if (phutil_nonempty_string($custom_body)) { return $this->newRemarkupText($custom_body); } $default_body = PhabricatorAuthMessage::loadMessageText( $recipient, PhabricatorAuthWelcomeMailMessageType::MESSAGEKEY); - if (strlen($default_body)) { + if (phutil_nonempty_string($default_body)) { return $this->newRemarkupText($default_body); } From 4d9719539773167058cf2b6bf6309945f1c5adba Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 10 May 2023 10:28:50 +0200 Subject: [PATCH 125/425] Fix PHP 8.1 "strlen(null)" exception when trying to add empty user email address Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15377 Test Plan: Applied this change, afterwards repeated the steps to add a new email address on `/settings/panel/email/` and left the "Email" field empty. This time, after selecting the "Save" button, the error message "Email is required." is displayed in the "New Address" overlay dialog. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15377 Differential Revision: https://we.phorge.it/D25211 --- .../settings/panel/PhabricatorEmailAddressesSettingsPanel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php index a888bb3175..29d0506105 100644 --- a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php @@ -184,7 +184,7 @@ private function returnNewAddressResponse( new PhabricatorSettingsAddEmailAction(), 1); - if (!strlen($email)) { + if (!phutil_nonempty_string($email)) { $e_email = pht('Required'); $errors[] = pht('Email is required.'); } else if (!PhabricatorUserEmail::isValidAddress($email)) { From c2856b479fbdbdec5a8b82bb87e421d3d0cb514f Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 12 May 2023 11:57:31 +0200 Subject: [PATCH 126/425] Fix PHP 8.1 "implicit conversion from float to int" exception on certain avatar colors which blocks rendering user pages Summary: Since PHP 8.1, conversion from float to integer should be explicit. https://wiki.php.net/rfc/implicit-float-int-deprecate According to `phlog`, the alpha channel value of the automatically created user avatar image for a new user account sometimes is a float. Thus always `round()` the alpha channel value to be an integer. Closes T15375 Test Plan: Applied this change, created five user accounts via http://phorge.localhost/people/new/standard/ , and each resulting alpha value in `PhabricatorFilesComposeAvatarBuiltinFile.php` was an integer according to `phlog` (see corresponding Maniphest task). Rendering of each new user page always succeeded. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15375 Differential Revision: https://we.phorge.it/D25209 --- .../PhabricatorFilesComposeAvatarBuiltinFile.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php b/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php index 4f827f7603..f1406d3efd 100644 --- a/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php +++ b/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php @@ -183,12 +183,21 @@ private function composeImage($color, $image, $border) { 'image/png'); } + /** + * Convert a color from RGBA to a value usable in PHP-GD. + * Each RGB color should be expressed as an integer from 0 to 255. + * The Alpha Channel should be expressed as a float from 0 to 1. + * @param array $rgba array( int Red, int Green, int Blue, float Alpha ) + * @return int + */ private static function rgba2gd($rgba) { + // When working with a truecolor image, we can use bitwise operations + // https://www.php.net/manual/en/function.imagecolorallocate.php#49168 $r = $rgba[0]; $g = $rgba[1]; $b = $rgba[2]; $a = $rgba[3]; - $a = (1 - $a) * 255; + $a = round(((1 - $a) * 255), 0); return ($a << 24) | ($r << 16) | ($g << 8) | $b; } From bac87ca2646b93a1db123b95bae5373e5d17b56b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 12 May 2023 11:59:38 +0200 Subject: [PATCH 127/425] Fix PHP 8.1 "strlen(null)" exception[s] which block rendering "Browse Projects" overlay dialog Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15380 Test Plan: Applied these two changes (on top of D25179) and on the task creation page, after clicking the magnifier icon in the "Tags" field, the "Browse Projects" overlay dialog got rendered. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15380 Differential Revision: https://we.phorge.it/D25213 --- .../project/typeahead/PhabricatorProjectDatasource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/project/typeahead/PhabricatorProjectDatasource.php b/src/applications/project/typeahead/PhabricatorProjectDatasource.php index 5b999a997f..5953f688d2 100644 --- a/src/applications/project/typeahead/PhabricatorProjectDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectDatasource.php @@ -83,7 +83,7 @@ public function loadResults() { } $slug = $proj->getPrimarySlug(); - if (!strlen($slug)) { + if (!phutil_nonempty_string($slug)) { foreach ($proj->getSlugs() as $slug_object) { $slug = $slug_object->getSlug(); if (strlen($slug)) { @@ -132,7 +132,7 @@ public function loadResults() { ->setPriorityType('proj') ->setClosed($closed); - if (strlen($slug)) { + if (phutil_nonempty_string($slug)) { $proj_result->setAutocomplete('#'.$slug); } From 3a3112f67b4eb46eb1eb8f9ee599a58a90299dc6 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 12 May 2023 12:01:55 +0200 Subject: [PATCH 128/425] Fix PHP 8.1 "strlen(null)" and preg_match() exceptions which block rendering "Browse Subscribers" overlay dialog Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Similarly, `preg_match()` does not accept passing `null` as `$subject` parameter since PHP 8.1. Closes T15381 Test Plan: Applied these two changes (on top of D25179 and D25213), and on the task creation page, after clicking the magnifier icon in the "Subscribers" field, the "Browse Subscribers" overlay dialog got rendered. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15381 Differential Revision: https://we.phorge.it/D25214 --- .../owners/typeahead/PhabricatorOwnersPackageDatasource.php | 2 +- .../query/policy/PhabricatorCursorPagedPolicyAwareQuery.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php index ea60acb54c..e93b3df7e9 100644 --- a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php +++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php @@ -26,7 +26,7 @@ public function loadResults() { // If the user is querying by monogram explicitly, like "O123", do an ID // search. Otherwise, do an ngram substring search. - if (preg_match('/^[oO]\d+\z/', $raw_query)) { + if ($raw_query && preg_match('/^[oO]\d+\z/', $raw_query)) { $id = trim($raw_query, 'oO'); $id = (int)$id; $query->withIDs(array($id)); diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 42ccad3316..6ad91b52ea 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -2449,7 +2449,7 @@ protected function withNgramsConstraint( PhabricatorSearchNgrams $index, $value) { - if (strlen($value)) { + if (phutil_nonempty_string($value)) { $this->ngrams[] = array( 'index' => $index, 'value' => $value, From 4d1adf69392a230075b8a4799058821d11cab16d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 12 May 2023 12:02:54 +0200 Subject: [PATCH 129/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering Log In page after user logout Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15384 Test Plan: Applied these four changes; logged in again; logged out again; finally saw "Log In" page with "Username or Email" and "Password" field on `/auth/loggedout/` rendered in web browser. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15384 Differential Revision: https://we.phorge.it/D25217 --- src/applications/auth/constants/PhabricatorCookies.php | 2 +- .../auth/controller/PhabricatorAuthController.php | 2 +- .../auth/controller/PhabricatorAuthStartController.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/applications/auth/constants/PhabricatorCookies.php b/src/applications/auth/constants/PhabricatorCookies.php index 2573bf3ec6..9675d21d42 100644 --- a/src/applications/auth/constants/PhabricatorCookies.php +++ b/src/applications/auth/constants/PhabricatorCookies.php @@ -89,7 +89,7 @@ public static function setClientIDCookie(AphrontRequest $request) { // temporary and clearing it when users log out. $value = $request->getCookie(self::COOKIE_CLIENTID); - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { $request->setTemporaryCookie( self::COOKIE_CLIENTID, Filesystem::readRandomCharacters(16)); diff --git a/src/applications/auth/controller/PhabricatorAuthController.php b/src/applications/auth/controller/PhabricatorAuthController.php index aee93b98d6..8bba90a1e6 100644 --- a/src/applications/auth/controller/PhabricatorAuthController.php +++ b/src/applications/auth/controller/PhabricatorAuthController.php @@ -282,7 +282,7 @@ final protected function newCustomStartMessage() { $viewer, PhabricatorAuthLoginMessageType::MESSAGEKEY); - if (!strlen($text)) { + if (!phutil_nonempty_string($text)) { return null; } diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php index 5789740e14..76c7eb9df5 100644 --- a/src/applications/auth/controller/PhabricatorAuthStartController.php +++ b/src/applications/auth/controller/PhabricatorAuthStartController.php @@ -98,7 +98,7 @@ public function handleRequest(AphrontRequest $request) { } $next_uri = $request->getStr('next'); - if (!strlen($next_uri)) { + if (!phutil_nonempty_string($next_uri)) { if ($this->getDelegatingController()) { // Only set a next URI from the request path if this controller was // delegated to, which happens when a user tries to view a page which @@ -112,7 +112,7 @@ public function handleRequest(AphrontRequest $request) { } if (!$request->isFormPost()) { - if (strlen($next_uri)) { + if (phutil_nonempty_string($next_uri)) { PhabricatorCookies::setNextURICookie($request, $next_uri); } PhabricatorCookies::setClientIDCookie($request); From 2df7ea13a3877250354556f08f40e26ccc727144 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 12 May 2023 21:36:35 +0200 Subject: [PATCH 130/425] PhabricatorModularTransactionType: fix regression Summary: Fix a regression in this specific point: phutil_nonempty_string(integer) called at [/src/applications/transactions/storage/PhabricatorModularTransactionType.php:342] This regression was causing a broken Almanac page and maybe others. Note: The function phutil_nonempty_string() is well-known to be very angry and throws for any alien value. This is by design, and in many cases this is appropriate. But not here. The business logic here handles very generic types like integers and really probably whatever scalar value coming from an user input and then normalized to something else, not necessarily a string, but definitely something that can be cast to string. If you have better ideas about how to handle these cases, please join T15190. Closes T15385 Test Plan: To test Almanac: 1. go to `/almanac/network/` and create at least one network (example: "foo") 2. go to `/almanac/device/` and create at least one device (example: "bar") 3. visit that Device 4. Add Interface - test the creation of an empty Interface - test the creation of a right Interface (example: Network "foo", Address 127.0.0.1, Port 80) - nothing esplodes anymore Reviewers: arnold, O1 Blessed Committers, avivey Reviewed By: arnold, O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15385 Differential Revision: https://we.phorge.it/D25220 --- .../PhabricatorModularTransactionType.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/applications/transactions/storage/PhabricatorModularTransactionType.php b/src/applications/transactions/storage/PhabricatorModularTransactionType.php index 3840bcdf9c..e2f0a02239 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransactionType.php +++ b/src/applications/transactions/storage/PhabricatorModularTransactionType.php @@ -334,12 +334,28 @@ final protected function isNewObject() { return $this->getEditor()->getIsNewObject(); } + /** + * Check whenever a new transaction's value is considered an "empty text" + * @param mixed $value A string, null, an integer... + * @param array $xactions Transactions + */ final protected function isEmptyTextTransaction($value, array $xactions) { foreach ($xactions as $xaction) { $value = $xaction->getNewValue(); } - return !phutil_nonempty_string($value); + // The $value can be a lot of stuff, null, string, integer and maybe more. + // We cast to string to answer the question "Is this string empty?". + // Note: Don't use phutil_nonempty_stringlike() since it was not designed + // for integers. + // Note: Don't use phutil_nonempty_scalar() since very probably we could + // receive a boolean, causing exceptions. + // https://we.phorge.it/T15239 + $value_clean = phutil_string_cast($value); + + // We made our lives easier and we don't need strlen(something) + // and we should not. + return $value_clean === ''; } /** From 402a63c5de96556b2d5a61566f7245464e2d98a6 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 19 May 2023 11:59:23 +0200 Subject: [PATCH 131/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering "Browse Dashboards" dialog Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. But, that function does not accept null anymore since PHP 8.1. Closes T15396 Test Plan: Applied this change (on top of D25179 and D25226) and the "Browse Dashboards" dialog got rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15396 Differential Revision: https://we.phorge.it/D25227 --- .../search/compiler/PhutilSearchQueryCompiler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/applications/search/compiler/PhutilSearchQueryCompiler.php b/src/applications/search/compiler/PhutilSearchQueryCompiler.php index c3f93e16c9..2356a06a8f 100644 --- a/src/applications/search/compiler/PhutilSearchQueryCompiler.php +++ b/src/applications/search/compiler/PhutilSearchQueryCompiler.php @@ -103,7 +103,9 @@ public function newTokens($query) { private function tokenizeQuery($query) { $maximum_bytes = 1024; - + if ($query === null) { + $query = ''; + } $query_bytes = strlen($query); if ($query_bytes > $maximum_bytes) { throw new PhutilSearchQueryCompilerSyntaxException( From 8a1cfff1e8ab866b7fe77e4d29b7a1a2cb179314 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 19 May 2023 16:59:07 +0200 Subject: [PATCH 132/425] Fix PHP 8.1 "strlen(null)" and "explode()" exceptions which block rendering Administrator Account Creation page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Similarly, `explode(string $separator, string $string, int $limit)` does not accept passing null instead of an actual string as input parameter either anymore. Closes T15284 Test Plan: Applied these two changes. Afterwards, admin user account was created and Phorge homepage rendered in web browser on a fresh installation. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15284 Differential Revision: https://we.phorge.it/D25175 --- src/applications/auth/constants/PhabricatorCookies.php | 2 +- .../people/cache/PhabricatorUserProfileImageCacheType.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/applications/auth/constants/PhabricatorCookies.php b/src/applications/auth/constants/PhabricatorCookies.php index 9675d21d42..9dc9d823b2 100644 --- a/src/applications/auth/constants/PhabricatorCookies.php +++ b/src/applications/auth/constants/PhabricatorCookies.php @@ -164,7 +164,7 @@ private static function parseNextURICookie($cookie) { // Old cookies look like: /uri // New cookies look like: timestamp,/uri - if (!strlen($cookie)) { + if (!phutil_nonempty_string($cookie)) { return null; } diff --git a/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php index 8babff859f..195d880fff 100644 --- a/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php +++ b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php @@ -91,6 +91,9 @@ public function shouldValidateRawCacheData() { } public function isRawCacheDataValid(PhabricatorUser $user, $key, $data) { + if ($data === null) { + return false; + } $parts = explode(',', $data, 2); $version = reset($parts); return ($version === $this->getCacheVersion($user)); From 26aed4935b55911b357b301611e050d56f7b53be Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 19 May 2023 17:00:49 +0200 Subject: [PATCH 133/425] Fix PHP 8.1 "strlen(null)" exception shown when sending SMS Test Message to Contact Number Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15382 Test Plan: Applied this change and after performing steps to send a test message again, `/mail/detail/107/` page got rendered correctly and I can see the "Status Details" with stuff like "//None//" or "No mailers are configured." Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15382 Differential Revision: https://we.phorge.it/D25215 --- .../metamta/controller/PhabricatorMetaMTAMailViewController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php index e38c7f9801..0d9b89c684 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php @@ -421,7 +421,7 @@ private function buildMetadataProperties(PhabricatorMetaMTAMail $mail) { $properties->addProperty(pht('Message PHID'), $mail->getPHID()); $details = $mail->getMessage(); - if (!strlen($details)) { + if (!phutil_nonempty_string($details)) { $details = phutil_tag('em', array(), pht('None')); } $properties->addProperty(pht('Status Details'), $details); From 912a933b0214f7b310b26b1c730ef9ed5331fe23 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 19 May 2023 17:01:35 +0200 Subject: [PATCH 134/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering Legalpad signatures Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15395 Test Plan: Applied these two changes; afterwards "All Signatures" page at `/legalpad/signatures/1/` gets rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15395 Differential Revision: https://we.phorge.it/D25226 --- .../legalpad/query/LegalpadDocumentSignatureSearchEngine.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php index 14ebca9d04..ac3e181889 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php +++ b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php @@ -127,12 +127,12 @@ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { } $name_contains = $saved->getParameter('nameContains'); - if (strlen($name_contains)) { + if (phutil_nonempty_string($name_contains)) { $query->withNameContains($name_contains); } $email_contains = $saved->getParameter('emailContains'); - if (strlen($email_contains)) { + if (phutil_nonempty_string($email_contains)) { $query->withEmailContains($email_contains); } From 9f1814f490bf5bd4423812cac0b1ebe9e3b10082 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 19 May 2023 17:18:01 +0200 Subject: [PATCH 135/425] Fix PHP 8.1 "strlen(null)" and "preg_match()" exceptions which block repository creation Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Similarly, `preg_match()` does not accept passing `null` as `$subject` parameter since PHP 8.1. Closes T15337 Test Plan: Applied these four changes and `/diffusion/4/manage/` finally rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15337 Differential Revision: https://we.phorge.it/D25182 --- .../controller/DiffusionRepositoryManagePanelsController.php | 2 +- .../diffusion/controller/DiffusionServeController.php | 4 ++-- .../management/DiffusionRepositoryBasicsManagementPanel.php | 2 +- .../repository/editor/PhabricatorRepositoryEditor.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php index dfb84cc3f0..e6d07d418f 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php @@ -40,7 +40,7 @@ public function handleRequest(AphrontRequest $request) { } $selected = $request->getURIData('panel'); - if (!strlen($selected)) { + if (!phutil_nonempty_string($selected)) { $selected = head_key($panels); } diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index 3040328fbf..89430eef37 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -43,8 +43,8 @@ public function isVCSRequest(AphrontRequest $request) { return null; } - $content_type = $request->getHTTPHeader('Content-Type'); - $user_agent = idx($_SERVER, 'HTTP_USER_AGENT'); + $content_type = $request->getHTTPHeader('Content-Type', ''); + $user_agent = idx($_SERVER, 'HTTP_USER_AGENT', ''); $request_type = $request->getHTTPHeader('X-Phabricator-Request-Type'); // This may have a "charset" suffix, so only match the prefix. diff --git a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php index fdd477f874..6ad26a399a 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php @@ -219,7 +219,7 @@ private function buildBasics() { $view->addProperty(pht('Type'), $type); $callsign = $repository->getCallsign(); - if (!strlen($callsign)) { + if (!phutil_nonempty_string($callsign)) { $callsign = phutil_tag('em', array(), pht('No Callsign')); } $view->addProperty(pht('Callsign'), $callsign); diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index 0955d54b98..c23fc41624 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -50,7 +50,7 @@ protected function applyFinalEffects( // If the repository does not have a local path yet, assign it one based // on its ID. We can't do this earlier because we won't have an ID yet. $local_path = $object->getLocalPath(); - if (!strlen($local_path)) { + if (!phutil_nonempty_string($local_path)) { $local_key = 'repository.default-local-path'; $local_root = PhabricatorEnv::getEnvConfig($local_key); From 678c7a78be43b939fdf5d86ebf3f8c57e1249885 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 19 May 2023 20:15:08 +0200 Subject: [PATCH 136/425] Fix PHP 8.1 "strlen(null)" exceptions setting up Username/Password Authentication Provider Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15383 Test Plan: Applied these two changes; afterwards "Username/Password" page at `/auth/config/view/1/` gets rendered in web browser. Additional tests (you need D25231 to complete this test plan): - Create WordPress Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Twitter Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Amazon Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Asana Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Bitbucket Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Disqus Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Disqus Auth; Edit; Disable Enable (and Disable again to cleanup) - Create Facebook Auth; Edit; Disable Enable (and Disable again to cleanup) - Create GitHub Auth; Edit; Disable Enable (and Disable again to cleanup) - Create JIRA Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Google Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create LDAP Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Phorge Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Slack Auth; Edit; Disable; Enable (and Disable again to cleanup) - Create Twitch.tv Auth; Edit; Disable; Enable (and Disable again to cleanup) Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15383 Differential Revision: https://we.phorge.it/D25216 --- .../auth/controller/config/PhabricatorAuthEditController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/auth/controller/config/PhabricatorAuthEditController.php b/src/applications/auth/controller/config/PhabricatorAuthEditController.php index 693fc5bffd..2dbdbc9a10 100644 --- a/src/applications/auth/controller/config/PhabricatorAuthEditController.php +++ b/src/applications/auth/controller/config/PhabricatorAuthEditController.php @@ -99,10 +99,10 @@ public function handleRequest(AphrontRequest $request) { if (!$errors) { if ($is_new) { - if (!strlen($config->getProviderType())) { + if (!phutil_nonempty_string($config->getProviderType())) { $config->setProviderType($provider->getProviderType()); } - if (!strlen($config->getProviderDomain())) { + if (!phutil_nonempty_string($config->getProviderDomain())) { $config->setProviderDomain($provider->getProviderDomain()); } } From 976a21f6584bda13289cee12599d29cb3126dbd4 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 19 May 2023 22:05:26 +0200 Subject: [PATCH 137/425] AphrontFormControl: fix regression for some specific Captions Summary: Fix a specific problem when visiting some specific pages like this one: /auth/config/edit/?provider=PhabricatorPhabricatorAuthProvider: Regression introduced in: 562d36ef5f9e83dba5e3ca0244f9c4af9e1f7b2b Stack trace: [Fri May 19 14:23:35.506028 2023] [php7:notice] [pid 20439] [client 127.0.0.1:39692] [2023-05-19 14:23:35] EXCEPTION: (InvalidArgumentException) Call to phutil_nonempty_string() expected null or a string, got: PhutilSafeHTML. at [/src/utils/utils.php:2127] [Fri May 19 14:23:35.506647 2023] [php7:notice] [pid 20439] [client 127.0.0.1:39692] arcanist(head=arcpatch-D25049, ref.master=c14785c3795c, ref.arcpatch-D25049=1b6412c24640), phorge(head=arcpatch-D25216_1, ref.master=2df7ea13a387, ref.arcpatch-D25216_1=02b40a9e25eb) [Fri May 19 14:23:35.506661 2023] [php7:notice] [pid 20439] [client 127.0.0.1:39692] #0 <#2> phutil_nonempty_string(PhutilSafeHTML) called at [/src/view/form/control/AphrontFormControl.php:206] [Fri May 19 14:23:35.506665 2023] [php7:notice] [pid 20439] [client 127.0.0.1:39692] #1 <#2> phutil_tag(string, array, array) called at [/src/view/form/PHUIFormLayoutView.php:54] [Fri May 19 14:23:35.506667 2023] [php7:notice] [pid 20439] [client 127.0.0.1:39692] #2 <#2> PHUIFormLayoutView::render() called at [/src/view/form/AphrontFormView.php:160] We keep 100% backward compatibility, reproducing the implicit cast happening automatically before PHP 8.1 It has also been simplified the non-empty check since that is possible after casting to string. Closes T15404 Test Plan: - Visit the page mentioned in the commit summary - no longer explodes - you still don't see empty Captions - you still see populated Captions - you still see HTML rendered for populated Captions Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15404 Differential Revision: https://we.phorge.it/D25231 --- src/view/form/control/AphrontFormControl.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/view/form/control/AphrontFormControl.php b/src/view/form/control/AphrontFormControl.php index d891636230..83e0207241 100644 --- a/src/view/form/control/AphrontFormControl.php +++ b/src/view/form/control/AphrontFormControl.php @@ -56,11 +56,22 @@ public function getLabel() { return $this->label; } + /** + * Set the Caption + * The Caption shows a tip usually nearby the related input field. + * @param string|PhutilSafeHTML|null + * @return self + */ public function setCaption($caption) { $this->caption = $caption; return $this; } + /** + * Get the Caption + * The Caption shows a tip usually nearby the related input field. + * @return string|PhutilSafeHTML|null + */ public function getCaption() { return $this->caption; } @@ -203,7 +214,11 @@ final public function render() { $custom_class .= ' aphront-form-control-nolabel'; } - if (phutil_nonempty_string($this->getCaption())) { + // The Caption can be stuff like PhutilSafeHTML and other objects that + // can be casted to a string. After this cast we have never null. + $has_caption = phutil_string_cast($this->getCaption()) !== ''; + + if ($has_caption) { $caption = phutil_tag( 'div', array('class' => 'aphront-form-caption'), From 44f2010a0b60ac1233d832ba7ea4a0ad9ef3eb2e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 20 May 2023 11:43:00 +0200 Subject: [PATCH 138/425] PhabricatorPhabricatorAuthProvider: Remove unnecessary Remarkup Summary: This is a small fix in a specific user message example. See other messages in the very same file which also use ``. Here the problematic phrase: {F292970} See the example in the phrase "Use lowercase letters, digits, and periods. For example: ...". Test Plan: After this change, the specific message "The URI where the OAuth server is installed. For example" from the page `/auth/config/edit/?provider=PhabricatorPhabricatorAuthProvider` does not have anymore backticks shown to the end user. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25232 --- .../auth/provider/PhabricatorPhabricatorAuthProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php index 4c1c31f146..0d729837b4 100644 --- a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php @@ -169,7 +169,7 @@ public function extendEditForm( phutil_tag( 'tt', array(), - '`example.oauthserver`')))); + 'example.oauthserver')))); } else { $form ->appendChild( From bff0f810a7d887c754201e1d7270d88c4802cf6b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 20 May 2023 11:46:21 +0200 Subject: [PATCH 139/425] Fix PHP 8.1 "strlen(null)" exception which blocks task Bulk Editor page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15391 Test Plan: Applied this change and afterwards, "Bulk Editor" page on `/maniphest/bulk/` rendered correctly. To test that: - visit a list of some Tasks (like `/maniphest/`) - filter something (this can be done in multiple ways) - select Tasks - click on Bulk Edit Selected Tasks Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15391 Differential Revision: https://we.phorge.it/D25223 --- src/applications/transactions/bulk/PhabricatorBulkEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/transactions/bulk/PhabricatorBulkEngine.php b/src/applications/transactions/bulk/PhabricatorBulkEngine.php index 0091321245..e2d2c02a29 100644 --- a/src/applications/transactions/bulk/PhabricatorBulkEngine.php +++ b/src/applications/transactions/bulk/PhabricatorBulkEngine.php @@ -153,7 +153,7 @@ private function loadObjectList() { ->setViewer($viewer); $query_key = $request->getURIData('queryKey'); - if (strlen($query_key)) { + if (phutil_nonempty_string($query_key)) { if ($search_engine->isBuiltinQuery($query_key)) { $saved = $search_engine->buildSavedQueryFromBuiltin($query_key); } else { From 1b905706b450e6142fba9a2570d814f024bd99e1 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 20 May 2023 11:47:42 +0200 Subject: [PATCH 140/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering MFA Provider page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15386 Test Plan: Applied this change, afterwards page "Mobile Phone App (TOTP)" at `/auth/mfa/4/` renders in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15386 Differential Revision: https://we.phorge.it/D25218 --- .../mfa/PhabricatorAuthFactorProviderViewController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderViewController.php b/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderViewController.php index 1dac49bcf9..44bb7e97f6 100644 --- a/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderViewController.php +++ b/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderViewController.php @@ -83,7 +83,7 @@ private function buildPropertiesView( $custom_enroll = $provider->getEnrollMessage(); - if (strlen($custom_enroll)) { + if (phutil_nonempty_string($custom_enroll)) { $view->addSectionHeader( pht('Custom Enroll Message'), PHUIPropertyListView::ICON_SUMMARY); From 94d45de2f7dd6472486c20110f542a6e3e6a6565 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 20 May 2023 14:27:06 +0200 Subject: [PATCH 141/425] Fix PHP 8.1 "ltrim(null)" exception which blocks adding additional user email address Summary: Since PHP 8.1, passing a null string to `ltrim(string $string)` is deprecated. Thus we make sure that `$request->getStr('email')` does not return null as default. Closes T15376 Test Plan: Applied this change, afterwards repeated the steps to add a new email address on `/settings/panel/email/`. This time, it's possible to close the "Verification Email Sent" and the page `/settings/panel/email/` renders and lists the new email address. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15376 Differential Revision: https://we.phorge.it/D25210 --- .../settings/panel/PhabricatorEmailAddressesSettingsPanel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php index 29d0506105..665bb42b2f 100644 --- a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php @@ -172,7 +172,7 @@ private function returnNewAddressResponse( $email = null; $errors = array(); if ($request->isDialogFormPost()) { - $email = trim($request->getStr('email')); + $email = trim($request->getStr('email', '')); if ($new == 'verify') { // The user clicked "Done" from the "an email has been sent" dialog. From f97c9d68d031b499045db031c91ad8e65788db95 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 20 May 2023 17:42:30 +0200 Subject: [PATCH 142/425] Fix PHP 8.1 Fatal error in Figlet: Use square brackets instead of curly braces Summary: Replace curly with square brackets when accessing an array value. See https://www.php.net/manual/en/migration74.deprecated.php#migration74.deprecated.core.array-string-access-curly-brace https://wiki.php.net/rfc/deprecate_curly_braces_array_access#wasn_t_the_curly_brace_syntax_deprecated_once_before Closes T15406 Test Plan: Fixed these two lines; afterwards Figlet rendered in task comment preview. Example figlet: ``` figlet {{{ stuff }}} ``` Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15406 Differential Revision: https://we.phorge.it/D25233 --- externals/pear-figlet/Text/Figlet.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/externals/pear-figlet/Text/Figlet.php b/externals/pear-figlet/Text/Figlet.php index cc121facb3..2ddefc94cb 100644 --- a/externals/pear-figlet/Text/Figlet.php +++ b/externals/pear-figlet/Text/Figlet.php @@ -231,7 +231,7 @@ function loadFont($filename, $loadgerman = true) $i = hexdec(substr($i, 2)); } else { // If octal - if ($i{0} === '0' && $i !== '0' || substr($i, 0, 2) == '-0') { + if ($i[0] === '0' && $i !== '0' || substr($i, 0, 2) == '-0') { $i = octdec($i); } } @@ -274,7 +274,7 @@ function lineEcho($str, $inhtml = false) $lt = hexdec(substr($str, $i+2, 4)); $i += 5; } else { - $lt = ord($str{$i}); + $lt = ord($str[$i]); } $hb = preg_quote($this->hardblank, '/'); From 17ebf3a11fb5e46f3a264ced96d65c90901dc134 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 20 May 2023 17:45:54 +0200 Subject: [PATCH 143/425] Remove PHP version suffixes on "Missing Required Extensions"; Mention dnf Summary: Explicitly mentioning PHP5 packages is outdated and confusing. Also, mentioning dnf together with yum (apparently yum is still in use!). Good for yum users. Closes T15352 Test Plan: Applied this change and strings on the "Missing Required Extensions" page rendered as expected. Reviewers: O1 Blessed Committers, valerio.bozzolan, jgleeson Reviewed By: O1 Blessed Committers, valerio.bozzolan, jgleeson Subscribers: jgleeson, bfs, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15352 Differential Revision: https://we.phorge.it/D25203 --- .../config/view/PhabricatorSetupIssueView.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/applications/config/view/PhabricatorSetupIssueView.php b/src/applications/config/view/PhabricatorSetupIssueView.php index 003725d5fc..bfeae20684 100644 --- a/src/applications/config/view/PhabricatorSetupIssueView.php +++ b/src/applications/config/view/PhabricatorSetupIssueView.php @@ -73,20 +73,22 @@ public function render() { 'Install these %d PHP extension(s):', count($extensions)); $install_info = pht( - 'You can usually install a PHP extension using %s or %s. Common '. - 'package names are %s or %s. Try commands like these:', + 'You can usually install a PHP extension using %s, %s, or %s. A '. + 'common package name is %s. Try commands like these:', phutil_tag('tt', array(), 'apt-get'), + phutil_tag('tt', array(), 'dnf'), phutil_tag('tt', array(), 'yum'), - hsprintf('php-%s', pht('extname')), - hsprintf('php5-%s', pht('extname'))); + hsprintf('php-%s', pht('extname'))); // TODO: We should do a better job of detecting how to install extensions // on the current system. $install_commands = hsprintf( - "\$ sudo apt-get install php5-extname ". + "\$ sudo apt-get install php-extname ". "# Debian / Ubuntu\n". - "\$ sudo yum install php-extname ". - "# Red Hat / Derivatives"); + "\$ sudo dnf install php-extname ". + "# Red Hat / Derivatives\n". + "\$ sudo yum install php-extname ". + "# Older Red Hat versions"); $fallback_info = pht( "If those commands don't work, try Google. The process of installing ". From 524dc83b4e8c9ed84954e91c17b68013994f9853 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 20 May 2023 17:48:31 +0200 Subject: [PATCH 144/425] Fix PHP 8.1 null parameter exceptions which block rendering the "Browse Projects" overlay dialog Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Similarly, passing `null` to the `$haystack` parameter of `strpos()` is deprecated in PHP 8.1. Similarly, passing `null` to the `$string` parameter of `ltrim()` is deprecated in PHP 8.1. Closes part of T15335 Test Plan: Applied these four changes in Phorge (plus the one change in D25180 in Arcanist) and the `Browse Projects` overlay dialog finally rendered in web browser and listed existing projects. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15335 Differential Revision: https://we.phorge.it/D25179 --- .../project/typeahead/PhabricatorProjectDatasource.php | 6 ++++-- .../datasource/PhabricatorTypeaheadCompositeDatasource.php | 2 +- .../typeahead/datasource/PhabricatorTypeaheadDatasource.php | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/applications/project/typeahead/PhabricatorProjectDatasource.php b/src/applications/project/typeahead/PhabricatorProjectDatasource.php index 5953f688d2..86478a5e1e 100644 --- a/src/applications/project/typeahead/PhabricatorProjectDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectDatasource.php @@ -21,7 +21,9 @@ public function loadResults() { $raw_query = $this->getRawQuery(); // Allow users to type "#qa" or "qa" to find "Quality Assurance". - $raw_query = ltrim($raw_query, '#'); + if ($raw_query !== null) { + $raw_query = ltrim($raw_query, '#'); + } $tokens = self::tokenizeString($raw_query); $query = id(new PhabricatorProjectQuery()) @@ -142,7 +144,7 @@ public function loadResults() { $proj_result->addAttribute($proj->getDisplayIconName()); $description = idx($descriptions, $phid); - if (strlen($description)) { + if (phutil_nonempty_string($description)) { $summary = PhabricatorMarkupEngine::summarizeSentence($description); $proj_result->addAttribute($summary); } diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php index d344425d30..8a396c1ed3 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -28,7 +28,7 @@ public function loadResults() { // We only need to do a prefix phase query if there's an actual query // string. If the user didn't type anything, nothing can possibly match it. - if (strlen($this->getRawQuery())) { + if (phutil_nonempty_string($this->getRawQuery())) { $phases[] = self::PHASE_PREFIX; } diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php index 29a8d4b7ba..a35a8e8f0f 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -464,7 +464,11 @@ public static function isFunctionToken($token) { // We're looking for a "(" so that a string like "members(q" is identified // and parsed as a function call. This allows us to start generating // results immediately, before the user fully types out "members(quack)". - return (strpos($token, '(') !== false); + if ($token) { + return (strpos($token, '(') !== false); + } else { + return false; + } } From c1c7699c989114f781fbbd2758d219521d1d5303 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 20 May 2023 19:17:43 +0200 Subject: [PATCH 145/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering the DarkConsole Summary: Since PHP 8.1 the function call `strlen(null)` is deprecated. Since Phabricator/Phorge elevates deprecation warnings to exceptions, the specific fix implies dealing with null, and dealing with real strings. In the specific case, a simple cast was made to deal with real strings. Closes T15315 Test Plan: Visit the Home Page with DarkConsole enabled in your configuration: no crashes Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: aklapper, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15315 Differential Revision: https://we.phorge.it/D25165 --- src/applications/console/core/DarkConsoleCore.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/applications/console/core/DarkConsoleCore.php b/src/applications/console/core/DarkConsoleCore.php index 437d6f0be7..73065b900e 100644 --- a/src/applications/console/core/DarkConsoleCore.php +++ b/src/applications/console/core/DarkConsoleCore.php @@ -124,6 +124,9 @@ private function sanitizeForJSON($data) { } else if (is_resource($data)) { return ''; } else { + // This is very probably not a string in strict sense + $data = phutil_string_cast($data); + // Truncate huge strings. Since the data doesn't really matter much, // just truncate bytes to avoid PhutilUTF8StringTruncator overhead. $length = strlen($data); From 10fae71a042f07d8901afd5dc2d737317f27a126 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 21 May 2023 07:56:57 +0200 Subject: [PATCH 146/425] Fix PHP 8.1 "explode(null)" exception which blocks rendering "Provide Multi-Factor Credentials" page Summary: Passing null to the `$string` in `explode(string $separator, string $string, int $limit)` is deprecated since PHP 8.1. Thus do not explode but create an empty array if the input is null. Closes T15397 Test Plan: Applied this change and afterwards the "Provide Multi-Factor Credentials" page at `/auth/finish/` showing the "App Code" field correctly rendered in the web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15397 Differential Revision: https://we.phorge.it/D25229 --- src/applications/auth/storage/PhabricatorAuthChallenge.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/storage/PhabricatorAuthChallenge.php b/src/applications/auth/storage/PhabricatorAuthChallenge.php index 0b740e5fa7..05d00c3e6e 100644 --- a/src/applications/auth/storage/PhabricatorAuthChallenge.php +++ b/src/applications/auth/storage/PhabricatorAuthChallenge.php @@ -56,7 +56,7 @@ public static function newChallengeResponsesFromRequest( AphrontRequest $request) { assert_instances_of($challenges, __CLASS__); - $token_list = $request->getStr(self::HTTPKEY); + $token_list = $request->getStr(self::HTTPKEY, ''); $token_list = explode(' ', $token_list); $token_map = array(); From d95200da918e24c1ac9ce8ddcc67004b5b7e2377 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 23 May 2023 11:53:14 +0200 Subject: [PATCH 147/425] Fix PHP 8.1 "strlen(null)" exceptions which block adding 2FA factor Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15388 Test Plan: Applied these two changes; afterwards managed to add a 2FA factor and `/settings/user/username/page/multifactor/?id=1` correctly listed mys Authentication Factors. Additional tests: - Unset any eventual personal MFA - Setup a personal MFA - Login/Logout using the MFA - Remove a personal MFA - Setup a personal enroll message from /auth/mfa/1/ - Setup a personal MFA - Login/Logout using the MFA - (then cleanup removing your test MFA) Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15388 Differential Revision: https://we.phorge.it/D25219 --- src/applications/auth/factor/PhabricatorAuthFactor.php | 4 ++-- .../settings/panel/PhabricatorMultiFactorSettingsPanel.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/applications/auth/factor/PhabricatorAuthFactor.php b/src/applications/auth/factor/PhabricatorAuthFactor.php index fefd9b5fd1..79ea17de7f 100644 --- a/src/applications/auth/factor/PhabricatorAuthFactor.php +++ b/src/applications/auth/factor/PhabricatorAuthFactor.php @@ -413,8 +413,8 @@ final protected function loadMFASyncToken( $sync_type = PhabricatorAuthMFASyncTemporaryTokenType::TOKENTYPE; $sync_token = null; - $sync_key = $request->getStr($this->getMFASyncTokenFormKey()); - if (strlen($sync_key)) { + $sync_key = $request->getStr($this->getMFASyncTokenFormKey(), ''); + if ($sync_key !== '') { $sync_key_digest = PhabricatorHash::digestWithNamedKey( $sync_key, PhabricatorAuthMFASyncTemporaryTokenType::DIGEST_KEY); diff --git a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php index 0054610c28..492544bef6 100644 --- a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php @@ -257,7 +257,7 @@ private function processNew(AphrontRequest $request) { // example, with SMS). if (!$request->isFormPost() || !$request->getBool('mfa.start')) { $enroll = $selected_provider->getEnrollMessage(); - if (!strlen($enroll)) { + if (!phutil_nonempty_string($enroll)) { $enroll = $selected_provider->getEnrollDescription($viewer); } From 3999a286741dc5d8b92f5c4735033c4cbbf0dbea Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 23 May 2023 11:54:39 +0200 Subject: [PATCH 148/425] Fix PHP 8.1 exceptions which block adding an embedded File preview as a Comment Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. Receiving null is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15389 Test Plan: Applied these three changes; afterwards writing `{F1234}` in the Task Comment text field finally rendered the image preview in the preview area, plus I could successfully add the Comment. Additional cute tests: ``` Test alt: {F1234,alt=0} {F1234,alt=null} {F1234,alt=123} {F1234,alt=0.1} {F1234,alt=[]} Test width: {F1234,width=0} {F1234,width=10} {F1234,width=10.1} {F1234,width=10%} {F1234,width=49.99%} {F1234,width=100%} Test height: {F1234,height=0} {F1234,height=10} {F1234,height=10.1} {F1234,height=10%} {F1234,height=49.99%} {F1234,height=100%} Test mix width/height: {F1234,height=0,width=0} {F1234,height=10,width=15} {F1234,height=10.1,width=15} {F1234,height=10%,width=15%} {F1234,height=49.99%,width=15%} {F1234,height=100%,width=15%} ``` If you have no nuclear implosion and if dimensions are somehow respected and the alt text works, well done! Test passed. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15389 Differential Revision: https://we.phorge.it/D25221 --- .../files/markup/PhabricatorEmbedFileRemarkupRule.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php index 9f37bdcaab..a81cb33726 100644 --- a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php +++ b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php @@ -197,7 +197,7 @@ private function renderImageFile( $alt = $options['alt']; } - if (!strlen($alt)) { + if (!phutil_nonempty_string($alt)) { $alt = $file->getAltText(); } @@ -346,10 +346,11 @@ private function renderFileLink( } private function parseDimension($string) { - $string = trim($string); - - if (preg_match('/^(?:\d*\\.)?\d+%?$/', $string)) { - return $string; + if ($string !== null) { + $string = trim($string); + if (preg_match('/^(?:\d*\\.)?\d+%?$/', $string)) { + return $string; + } } return null; From 538cccc63e0eeb99b2f62e5b1c2392e2b06cfe23 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 23 May 2023 11:56:26 +0200 Subject: [PATCH 149/425] Fix PHP 8.1 "strlen(null)" exception setting Story Points on a Task Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Here we adopt `phutil_string_cast()` to reply to the question "is this an empty string?". Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_string_cast() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15390 Test Plan: Enable `maniphest.points` in settings, for example with: ``` ./bin/config set maniphest.points --stdin <<< '{"enabled":true}' ``` Then try to create a Task: it does not explode anymore in PHP 8.1+. Also try to set various possible values from the Conduit API method "maniphest.edit". Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15390 Differential Revision: https://we.phorge.it/D25222 --- .../xaction/ManiphestTaskPointsTransaction.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php index 5a69199874..3b24dff590 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php @@ -97,8 +97,17 @@ public function getIcon() { return 'fa-calculator'; } + /** + * Normalize your Story Points from generic stuff to double or null. + * @param mixed $value Your raw Story Points + * @return double|null + */ private function getValueForPoints($value) { - if (!strlen($value)) { + // The Point can be various types also thanks to Conduit API + // like integers, floats, null, and strings of course. + // Everything meaningful must be printable as a string. + $is_empty = phutil_string_cast($value) === ''; + if ($is_empty) { $value = null; } if ($value !== null) { From 2ffbef8820380dd15628780d16001807e457799d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 23 May 2023 11:57:58 +0200 Subject: [PATCH 150/425] Fix PHP 8.1 "trim(null)" exception which blocks rendering Reports' Burnup Rate page Summary: Since PHP 8.1, passing a null string to `trim()` is deprecated. Thus first check that $row['oldValue'] is not null before trimming it. Closes T15392 Test Plan: Applied this change; afterwards "Burnup Rate" page at `/maniphest/report/burn/` got correctly rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15392 Differential Revision: https://we.phorge.it/D25224 --- .../maniphest/controller/ManiphestReportController.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/applications/maniphest/controller/ManiphestReportController.php b/src/applications/maniphest/controller/ManiphestReportController.php index 6fc19ece98..a7efe89194 100644 --- a/src/applications/maniphest/controller/ManiphestReportController.php +++ b/src/applications/maniphest/controller/ManiphestReportController.php @@ -186,7 +186,10 @@ public function renderBurn() { switch ($row['transactionType']) { case ManiphestTaskStatusTransaction::TRANSACTIONTYPE: // NOTE: Hack to avoid json_decode(). - $oldv = trim($row['oldValue'], '"'); + $oldv = $row['oldValue']; + if ($oldv !== null) { + $oldv = trim($oldv, '"'); + } $newv = trim($row['newValue'], '"'); break; case ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE: From 19d9da7feae98e747610a26535acbf7081f36c8d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 23 May 2023 11:58:57 +0200 Subject: [PATCH 151/425] Fix PHP 8.1 "strlen(null)" exception which blocks Working Copy blueprint creation in Drydock Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. So, we adopt an explicit cast to string in order to answer the question "Is this a non-empty string?" without relying on implicit cast and deprecated things. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_string_cast() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15409 Test Plan: Applied this change and afterwards, selecting "Working Copy" on `/drydock/blueprint/edit/form/default/` does not show a RuntimeException on strlen() anymore. Instead, it shows a PhutilAggregateException covered in T15408. Try also creating an integer "Custom Field" in some applications like Maniphest (maniphest.custom-field-definitions) or in the People component (user.custom-field-definitions) etc., populating the related forms with your usual fuzzy tests. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15409 Differential Revision: https://we.phorge.it/D25235 --- .../customfield/standard/PhabricatorStandardCustomFieldInt.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php index f06c30d482..e8a5b99e0d 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php @@ -24,7 +24,8 @@ public function buildOrderIndex() { public function getValueForStorage() { $value = $this->getFieldValue(); - if (strlen($value)) { + $is_nonempty = phutil_string_cast($value) !== ''; + if ($is_nonempty) { return $value; } else { return null; From b1edfea09bad48c267b547918e82d6de43298781 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 23 May 2023 11:59:25 +0200 Subject: [PATCH 152/425] Fix PHP 8.1 "strlen(null)" exception trying to create an empty Menu Item Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15398 Test Plan: Applied this change and "Edit Menu Item" overlay dialog correctly shows "You must choose a project" resp. "You must choose a room." error message, now without an exception. Go to /home/menu/configure/custom/ and also to /home/menu/configure/global/ and try: - Add Label with name `0` / `1` / `1.123` etc. - Add Label, Save again - Add Link, Save again - Add Application, Save again - Add Divider, Save again - Add Conpherence, Save again - Add Link, Save Again - Add Motivator (Cat Facts), Save again - Add Form, Save again - Order Items - Activate/Disable Built-in Home (unrelated) - Remove all the above Also, tried on a Project menu. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15398 Differential Revision: https://we.phorge.it/D25230 --- src/applications/search/menuitem/PhabricatorProfileMenuItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/search/menuitem/PhabricatorProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorProfileMenuItem.php index 773a0f09ac..2dc0270d24 100644 --- a/src/applications/search/menuitem/PhabricatorProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorProfileMenuItem.php @@ -126,7 +126,7 @@ final protected function isEmptyTransaction($value, array $xactions) { $result = $xaction['new']; } - return !strlen($result); + return !phutil_nonempty_string($result); } final protected function newError($title, $message, $xaction = null) { From 26d74ccc7c26b44872d63360322de72584586a5e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 24 May 2023 18:08:40 +0200 Subject: [PATCH 153/425] Fix PHP 8.1 "strlen(null)" exception which blocks deleting Phriction document Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15419 Test Plan: Applied this changes and page at `/w/` correctly rendered saying `Document Deleted - This document has been deleted. You can edit it to put new content here, or use history to revert to an earlier version.` Also test creating it again, and updating it again: it still works. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15419 Differential Revision: https://we.phorge.it/D25243 --- .../phriction/editor/PhrictionTransactionEditor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index 1476b24c46..d59541c401 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -556,7 +556,7 @@ private function newDocumentContent(PhrictionDocument $document) { ->setContent($this->getOldContent()->getContent()) ->setDescription(''); - if (strlen($this->getDescription())) { + if (phutil_nonempty_string($this->getDescription())) { $content->setDescription($this->getDescription()); } From 6d57904330bcd38a652ccdc9aa705fde5f658cec Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 24 May 2023 18:09:48 +0200 Subject: [PATCH 154/425] Fix PHP 8.1 "strlen(null)" exceptions which block rendering Browse Repositories overlay dialog Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15420 Test Plan: Applied these two changes; afterwards "Browse Repositories" overlay dialog gets rendered in web browser and lists existing repositories. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15420 Differential Revision: https://we.phorge.it/D25244 --- .../diffusion/typeahead/DiffusionRepositoryDatasource.php | 2 +- src/applications/repository/storage/PhabricatorRepository.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php b/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php index e0662b1e1a..cc9da44723 100644 --- a/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php @@ -41,7 +41,7 @@ public function loadResults() { $parts[] = $name; $slug = $repository->getRepositorySlug(); - if (strlen($slug)) { + if (phutil_nonempty_string($slug)) { $parts[] = $slug; } diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 5c7c9ff33d..b05be618c0 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -217,7 +217,7 @@ public function getAllMonograms() { $monograms[] = 'R'.$this->getID(); $callsign = $this->getCallsign(); - if (strlen($callsign)) { + if (phutil_nonempty_string($callsign)) { $monograms[] = 'r'.$callsign; } From 55596bfad523effdd8b1b63a25da8d86399804f7 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 25 May 2023 13:43:24 +0200 Subject: [PATCH 155/425] PHP Pitfalls: mention strlen() deprecation since PHP 8.1 Summary: - expand documentation about PHP Pitfalls to mention strlen() in PHP 8.1 - mention phutil_string_cast() - mention phutil_nonempty_string() - add a commodity link from PHP Contributors Manual Ref T15190 Test Plan: * check with your big eyes for typos Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Tags: #documentation Maniphest Tasks: T15190 Differential Revision: https://we.phorge.it/D25108 --- .../contributor/php_coding_standards.diviner | 4 + src/docs/flavor/php_pitfalls.diviner | 91 ++++++++++++++----- 2 files changed, 73 insertions(+), 22 deletions(-) diff --git a/src/docs/contributor/php_coding_standards.diviner b/src/docs/contributor/php_coding_standards.diviner index bb54478fa3..6c68d194ca 100644 --- a/src/docs/contributor/php_coding_standards.diviner +++ b/src/docs/contributor/php_coding_standards.diviner @@ -176,3 +176,7 @@ diffs which add elements to the array affect only one line. return $this->favoriteFood; } } + +# Extra Readings + +* @{article:PHP Pitfalls} diff --git a/src/docs/flavor/php_pitfalls.diviner b/src/docs/flavor/php_pitfalls.diviner index e15ca9e2bb..b280cb2394 100644 --- a/src/docs/flavor/php_pitfalls.diviner +++ b/src/docs/flavor/php_pitfalls.diviner @@ -49,18 +49,7 @@ If a value is not truthy, it is "falsey". These values are falsey in PHP: false // boolean array() // empty array -Disregarding some bizarre edge cases, all other values are truthy. Note that -because "0" is falsey, this sort of thing (intended to prevent users from making -empty comments) is wrong in PHP: - - COUNTEREXAMPLE - if ($comment_text) { - make_comment($comment_text); - } - -This is wrong because it prevents users from making the comment "0". //THIS -COMMENT IS TOTALLY AWESOME AND I MAKE IT ALL THE TIME SO YOU HAD BETTER NOT -BREAK IT!!!// A better test is probably `strlen()`. +Disregarding some bizarre edge cases, all other values are truthy. In addition to truth tests with `if`, PHP has two special truthiness operators which look like functions but aren't: `empty()` and `isset()`. These operators @@ -92,16 +81,16 @@ variables. `empty()` evaluates truthiness exactly opposite of `if()`. `isset()` returns `true` for everything except `null`. This is the truth table: -| Value | `if()` | `empty()` | `isset()` | -|-------|--------|-----------|-----------| -| `null` | `false` | `true` | `false` | -| `0` | `false` | `true` | `true` | -| `0.0` | `false` | `true` | `true` | -| `"0"` | `false` | `true` | `true` | -| `""` | `false` | `true` | `true` | -| `false` | `false` | `true` | `true` | -| `array()` | `false` | `true` | `true` | -| Everything else | `true` | `false` | `true` | +| Value | `if()` | `empty()` | `isset()` | +|---------------|--------|-----------|-----------| +| `null` | `false`| `true` | `false` | +| `0` | `false`| `true` | `true` | +| `0.0` | `false`| `true` | `true` | +| `"0"` | `false`| `true` | `true` | +| `""` | `false`| `true` | `true` | +|`false` | `false`| `true` | `true` | +|`array()` | `false`| `true` | `true` | +|Everything else| `true` | `false` | `true` | The value of these operators is that they accept undeclared variables and do not issue a warning. Specifically, if you try to do this you get a warning: @@ -138,6 +127,64 @@ Put another way, use `isset()` if you want to type `if ($value !== null)` but are testing something that may not be declared. Use `empty()` if you want to type `if (!$value)` but you are testing something that may not be declared. += Check for non-empty strings = + +As already mentioned, note that you cannot just use an `if` or `empty()` to +check for a non-empty string, mostly because "0" is falsey, so you cannot rely +on this sort of thing to prevent users from making empty comments: + + COUNTEREXAMPLE + if ($comment_text) { + make_comment($comment_text); + } + +This is wrong because it prevents users from making the comment "0". + +//THE COMMENT "0" IS TOTALLY AWESOME AND I MAKE IT ALL THE TIME SO YOU HAD +BETTER NOT BREAK IT!!!// + +Another way //was// also `strlen()`: + + COUNTEREXAMPLE + if (strlen($comment_text)) { + make_comment($comment_text); + } + +But using `strlen(null)` causes a deprecation warning since PHP 8.1. Also, +using `strlen()` uses too many CPU cycles to just check of a non-empty. + +In short, outside Phorge, this is a general way to check for non-empty strings +for most wild input types: + +```lang=php + $value_str = (string) $value; + if ($value_str !== '') { + // do something + } +``` + +To do the same thing in Phorge, use this better and safer approach: + +```lang=php + $value_str = phutil_string_cast($value); + if ($value_str !== '') { + // do something + } +``` + +And, if you are 100% sure that you are __only__ working with string and +null, evaluate this instead: + +```lang=php + if (phutil_nonempty_string($value)) { + // do something + } +``` + +WARNING: The function `phutil_nonempty_string()` is designed to throw a nice +exception if it receives `true`, `false`, an array, an object or anything +alien that is not a string and not null. Do your evaluations. + = usort(), uksort(), and uasort() are Slow = This family of functions is often extremely slow for large datasets. You should From 82e7f30a825d7043ca00cd6eed104050e2e9d577 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Thu, 25 May 2023 02:26:55 -0700 Subject: [PATCH 156/425] Update the supported PHP versions in the docs Summary: Refs T15064. Test Plan: Generated the doc locally, see that it says what I was planning for it to say. Reviewers: valerio.bozzolan, aklapper, O1 Blessed Committers Reviewed By: valerio.bozzolan, O1 Blessed Committers Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15064 Differential Revision: https://we.phorge.it/D25245 --- src/docs/user/installation_guide.diviner | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index 7306155917..edbc328336 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -79,8 +79,11 @@ You will also need: - **MySQL**: You need MySQL. We strongly recommend MySQL 5.5 or newer. You will need a server with multiple databases. - - **PHP**: You need PHP 5.5 or newer. Note that PHP 8.1 and above are not - fully supported. + - **PHP**: You need a PHP engine: + - PHP 5 - 5.5 or newer. + - PHP 7 - 7.1 or newer. + - PHP 8 - **Not yet supported**. We're working on it as fast as we can, + and expect to support PHP 8 Real Soon Now. - **git**: You need git 2.5.0 or newer on the server. No particular version is needed on your clients. From 5405134fa5db77bfdc0f5dbc55ed07134a60799a Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 25 May 2023 16:10:21 +0200 Subject: [PATCH 157/425] Fix PHP 8.1 "strlen(null)" exception which can block repository imports Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Interestingly, in upstream they started fixing this yesterday, with just this minimal change: https://secure.phabricator.com/rPf6214f060e780ecf7b565c5a0edbd28d85c03275#C11580NL1139 Premising counting the length of a string just to answer the question "is this empty?" may be overkill, but premising that adopting stuff like phutil_nonempty_string() could be too risky, we just do an explicit cast to string, to answer the question "Is this string empty?" just like it was done under the hood by the strlen() function. In short, this is probably nice and more readable than upstream (but not a competition). Closes T15370 Test Plan: - create a Mercurial repository - push some things - it should conclude the import (or at least unlock a different Exception) Reviewers: O1 Blessed Committers, speck, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, amit, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15370 Differential Revision: https://we.phorge.it/D25204 --- .../search/engine/PhabricatorApplicationSearchEngine.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index effb20eb34..076a9a1e52 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -1137,7 +1137,8 @@ public function buildConduitResponse( $viewer = $this->requireViewer(); $query_key = $request->getValue('queryKey'); - if (!strlen($query_key)) { + $is_empty_query_key = phutil_string_cast($query_key) === ''; + if ($is_empty_query_key) { $saved_query = new PhabricatorSavedQuery(); } else if ($this->isBuiltinQuery($query_key)) { $saved_query = $this->buildSavedQueryFromBuiltin($query_key); From c5a365e1b2869b14e43a52bb025bfe2d096f5ea9 Mon Sep 17 00:00:00 2001 From: Leon Eckardt Date: Sat, 27 May 2023 21:16:31 +0200 Subject: [PATCH 158/425] Unify user-select CSS directives Summary: Removes the -khtml, -moz and -ms prefix, since most Browsers are natively supporting the user select directive. The -webkit prefix is still kept or added for Safari, wich does not support user-select. Ref: see https://we.phorge.it/D25024#815 for context Test Plan: Removing the CSS should change nothing in modern browsers. Reviewers: O1 Blessed Committers, speck, valerio.bozzolan, Matthew Reviewed By: O1 Blessed Committers, speck, valerio.bozzolan Subscribers: Cigaryno, Matthew, speck, tobiaswiese, valerio.bozzolan Differential Revision: https://we.phorge.it/D25025 --- resources/celerity/map.php | 50 +++++++++---------- .../rule/PhabricatorKeyboardRemarkupRule.php | 1 + .../application/base/standard-page-view.css | 1 + .../differential/changeset-view.css | 10 ---- .../application/harbormaster/harbormaster.css | 3 -- webroot/rsrc/css/core/remarkup.css | 1 + .../layout/phabricator-source-code-view.css | 3 -- webroot/rsrc/css/phui/button/phui-button.css | 2 - .../phui/object-item/phui-oi-list-view.css | 2 +- .../css/phui/workboards/phui-workcard.css | 3 -- .../css/phui/workboards/phui-workpanel.css | 3 -- 11 files changed, 29 insertions(+), 50 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index a53735823b..ade1764cf3 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,10 +9,10 @@ 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '2ba11212', + 'core.pkg.css' => '0cb47e9d', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', - 'differential.pkg.css' => '609e63d4', + 'differential.pkg.css' => '525f9a1d', 'differential.pkg.js' => '46fcb3af', 'diffusion.pkg.css' => '42c75c37', 'diffusion.pkg.js' => '78c9885d', @@ -41,7 +41,7 @@ 'rsrc/css/application/base/main-menu-view.css' => '89fc16b6', 'rsrc/css/application/base/notification-menu.css' => '4df1ee30', 'rsrc/css/application/base/phui-theme.css' => '35883b37', - 'rsrc/css/application/base/standard-page-view.css' => 'a374f94c', + 'rsrc/css/application/base/standard-page-view.css' => 'e08c7462', 'rsrc/css/application/chatlog/chatlog.css' => 'abdc76ee', 'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41', 'rsrc/css/application/config/config-options.css' => '16c920ae', @@ -63,7 +63,7 @@ 'rsrc/css/application/diff/diff-tree-view.css' => 'e2d3e222', 'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d', 'rsrc/css/application/differential/add-comment.css' => '7e5900d9', - 'rsrc/css/application/differential/changeset-view.css' => 'bf159129', + 'rsrc/css/application/differential/changeset-view.css' => '8b9caefe', 'rsrc/css/application/differential/core.css' => '7300a73e', 'rsrc/css/application/differential/phui-inline-comment.css' => 'a864426f', 'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d', @@ -77,7 +77,7 @@ 'rsrc/css/application/feed/feed.css' => 'd8b6e3f8', 'rsrc/css/application/files/global-drag-and-drop.css' => '1d2713a4', 'rsrc/css/application/flag/flag.css' => '2b77be8d', - 'rsrc/css/application/harbormaster/harbormaster.css' => 'd98decda', + 'rsrc/css/application/harbormaster/harbormaster.css' => 'cd0ce059', 'rsrc/css/application/herald/herald-test.css' => '7e7bbdae', 'rsrc/css/application/herald/herald.css' => '648d39e2', 'rsrc/css/application/maniphest/report.css' => '3d53188b', @@ -110,7 +110,7 @@ 'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd', 'rsrc/css/application/uiexample/example.css' => 'b4795059', 'rsrc/css/core/core.css' => 'a708bd25', - 'rsrc/css/core/remarkup.css' => '5baa3bd9', + 'rsrc/css/core/remarkup.css' => '3480e1fe', 'rsrc/css/core/syntax.css' => '548567f6', 'rsrc/css/core/z-index.css' => 'ac3bfcd4', 'rsrc/css/diviner/diviner-shared.css' => '4bd263b0', @@ -121,10 +121,10 @@ 'rsrc/css/fuel/fuel-handle-list.css' => '2c4cbeca', 'rsrc/css/fuel/fuel-map.css' => 'd6e31510', 'rsrc/css/fuel/fuel-menu.css' => '21f5d199', - 'rsrc/css/layout/phabricator-source-code-view.css' => '6b31244f', + 'rsrc/css/layout/phabricator-source-code-view.css' => '49656486', 'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4', 'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa', - 'rsrc/css/phui/button/phui-button.css' => 'e434f171', + 'rsrc/css/phui/button/phui-button.css' => 'f9d0f9c8', 'rsrc/css/phui/calendar/phui-calendar-day.css' => '9597d706', 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2', 'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42', @@ -133,7 +133,7 @@ 'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0', 'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc', 'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e', - 'rsrc/css/phui/object-item/phui-oi-list-view.css' => 'af98a277', + 'rsrc/css/phui/object-item/phui-oi-list-view.css' => '9275ff55', 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '6a30fa46', 'rsrc/css/phui/phui-action-list.css' => '1b0085b2', 'rsrc/css/phui/phui-action-panel.css' => '6c386cbf', @@ -182,8 +182,8 @@ 'rsrc/css/phui/phui-two-column-view.css' => 'f96d319f', 'rsrc/css/phui/workboards/phui-workboard-color.css' => '3a1c21ff', 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', - 'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6', - 'rsrc/css/phui/workboards/phui-workpanel.css' => '3ae89b20', + 'rsrc/css/phui/workboards/phui-workcard.css' => '62056e3b', + 'rsrc/css/phui/workboards/phui-workpanel.css' => 'bc06f022', 'rsrc/css/sprite-login.css' => '35d1510c', 'rsrc/css/sprite-tokens.css' => 'f1896dc5', 'rsrc/css/syntax/syntax-default.css' => '055fc231', @@ -560,7 +560,7 @@ 'conpherence-transaction-css' => '3a3f5e7e', 'd3' => '9d068042', 'diff-tree-view-css' => 'e2d3e222', - 'differential-changeset-view-css' => 'bf159129', + 'differential-changeset-view-css' => '8b9caefe', 'differential-core-view-css' => '7300a73e', 'differential-revision-add-comment-css' => '7e5900d9', 'differential-revision-comment-css' => '7dbc8d1d', @@ -579,7 +579,7 @@ 'fuel-map-css' => 'd6e31510', 'fuel-menu-css' => '21f5d199', 'global-drag-and-drop-css' => '1d2713a4', - 'harbormaster-css' => 'd98decda', + 'harbormaster-css' => 'cd0ce059', 'herald-css' => '648d39e2', 'herald-rule-editor' => '2633bef7', 'herald-test-css' => '7e7bbdae', @@ -800,13 +800,13 @@ 'phabricator-object-selector-css' => 'ee77366f', 'phabricator-phtize' => '2f1db1ed', 'phabricator-prefab' => '5793d835', - 'phabricator-remarkup-css' => '5baa3bd9', + 'phabricator-remarkup-css' => '3480e1fe', 'phabricator-remarkup-metadata' => 'e40c4991', 'phabricator-search-results-css' => '9ea70ace', 'phabricator-shaped-request' => '995f5102', 'phabricator-slowvote-css' => '1694baed', - 'phabricator-source-code-view-css' => '6b31244f', - 'phabricator-standard-page-view' => 'a374f94c', + 'phabricator-source-code-view-css' => '49656486', + 'phabricator-standard-page-view' => 'e08c7462', 'phabricator-textareautils' => 'f340a484', 'phabricator-title' => '43bc9360', 'phabricator-tooltip' => '83754533', @@ -829,7 +829,7 @@ 'phui-box-css' => '5ed3b8cb', 'phui-bulk-editor-css' => '374d5e30', 'phui-button-bar-css' => 'a4aa75c4', - 'phui-button-css' => 'e434f171', + 'phui-button-css' => 'f9d0f9c8', 'phui-button-simple-css' => '1ff278aa', 'phui-calendar-css' => 'f11073aa', 'phui-calendar-day-css' => '9597d706', @@ -870,7 +870,7 @@ 'phui-oi-color-css' => 'b517bfa0', 'phui-oi-drag-ui-css' => 'da15d3dc', 'phui-oi-flush-ui-css' => '490e2e2e', - 'phui-oi-list-view-css' => 'af98a277', + 'phui-oi-list-view-css' => '9275ff55', 'phui-oi-simple-ui-css' => '6a30fa46', 'phui-pager-css' => 'd022c7ad', 'phui-pinboard-view-css' => '1f08f5d8', @@ -886,8 +886,8 @@ 'phui-two-column-view-css' => 'f96d319f', 'phui-workboard-color-css' => '3a1c21ff', 'phui-workboard-view-css' => '74fc9d98', - 'phui-workcard-view-css' => '913441b6', - 'phui-workpanel-view-css' => '3ae89b20', + 'phui-workcard-view-css' => '62056e3b', + 'phui-workpanel-view-css' => 'bc06f022', 'phuix-action-list-view' => 'c68f183f', 'phuix-action-view' => 'a8f573a9', 'phuix-autocomplete' => '2fbe234d', @@ -1247,9 +1247,6 @@ 'trigger-rule', 'trigger-rule-type', ), - '3ae89b20' => array( - 'phui-workcard-view-css', - ), '3b4899b0' => array( 'javelin-behavior', 'phabricator-prefab', @@ -1702,6 +1699,9 @@ 'javelin-dom', 'phabricator-busy', ), + '8b9caefe' => array( + 'phui-inline-comment-view-css', + ), '8badee71' => array( 'javelin-install', 'javelin-util', @@ -2002,8 +2002,8 @@ 'javelin-uri', 'phabricator-notification', ), - 'bf159129' => array( - 'phui-inline-comment-view-css', + 'bc06f022' => array( + 'phui-workcard-view-css', ), 'c03f2fb4' => array( 'javelin-install', diff --git a/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php b/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php index 56bab68e44..c5344a692c 100644 --- a/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php +++ b/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php @@ -212,6 +212,7 @@ public function markupKeystrokes(array $matches) { 'line-height: 0.6rem;', 'border-radius: 3px;', 'box-shadow: inset 0 -1px 0 rgba(71, 87, 120, 0.08);', + '-webkit-user-select: none;', 'user-select: none;', 'background: #f7f7f7;', 'border: 1px solid #C7CCD9;', diff --git a/webroot/rsrc/css/application/base/standard-page-view.css b/webroot/rsrc/css/application/base/standard-page-view.css index ee74139e69..eec00154cf 100644 --- a/webroot/rsrc/css/application/base/standard-page-view.css +++ b/webroot/rsrc/css/application/base/standard-page-view.css @@ -74,6 +74,7 @@ body.white-background { text-decoration: none; border-radius: 3px; box-shadow: inset 0 -1px 0 rgba({$alphablue}, 0.08); + -webkit-user-select: none; user-select: none; color: {$darkgreytext}; background: {$lightgreybackground}; diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 5788bd68bc..88bbcfa421 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -432,37 +432,27 @@ unselectable. */ .differential-diff.copy-l > tbody > tr > td, .differential-diff.copy-r > tbody > tr > td { - -moz-user-select: none; - -ms-user-select: none; -webkit-user-select: none; user-select: none; } .differential-diff.copy-l > tbody > tr > td:nth-child(2) { - -moz-user-select: auto; - -ms-user-select: auto; -webkit-user-select: auto; user-select: auto; } .differential-diff.copy-l > tbody > tr > td.show-more:nth-child(2) { - -moz-user-select: none; - -ms-user-select: none; -webkit-user-select: none; user-select: none; } .differential-diff.copy-r > tbody > tr > td:nth-child(5) { - -moz-user-select: auto; - -ms-user-select: auto; -webkit-user-select: auto; user-select: auto; } .differential-diff.copy-l > tbody > tr.inline > td, .differential-diff.copy-r > tbody > tr.inline > td { - -moz-user-select: none; - -ms-user-select: none; -webkit-user-select: none; user-select: none; } diff --git a/webroot/rsrc/css/application/harbormaster/harbormaster.css b/webroot/rsrc/css/application/harbormaster/harbormaster.css index 2fa5af7b22..0b9aed76f8 100644 --- a/webroot/rsrc/css/application/harbormaster/harbormaster.css +++ b/webroot/rsrc/css/application/harbormaster/harbormaster.css @@ -44,10 +44,7 @@ background-color: {$paste.highlight}; border-right: 1px solid {$paste.border}; - -moz-user-select: -moz-none; - -khtml-user-select: none; -webkit-user-select: none; - -ms-user-select: none; user-select: none; } diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css index 38e03290d7..7f1c51cac9 100644 --- a/webroot/rsrc/css/core/remarkup.css +++ b/webroot/rsrc/css/core/remarkup.css @@ -66,6 +66,7 @@ line-height: 0.6rem; border-radius: 3px; box-shadow: inset 0 -1px 0 rgba({$alphablue},0.08); + -webkit-user-select: none; user-select: none; background: {$lightgreybackground}; border: 1px solid {$lightgreyborder}; diff --git a/webroot/rsrc/css/layout/phabricator-source-code-view.css b/webroot/rsrc/css/layout/phabricator-source-code-view.css index 71e3516003..b16b158004 100644 --- a/webroot/rsrc/css/layout/phabricator-source-code-view.css +++ b/webroot/rsrc/css/layout/phabricator-source-code-view.css @@ -76,10 +76,7 @@ th.phabricator-source-line a:hover { .phabricator-source-blame-skip, .phabricator-source-blame-info { - -moz-user-select: -moz-none; - -khtml-user-select: none; -webkit-user-select: none; - -ms-user-select: none; user-select: none; } diff --git a/webroot/rsrc/css/phui/button/phui-button.css b/webroot/rsrc/css/phui/button/phui-button.css index 22ad96779d..b84b4895f7 100644 --- a/webroot/rsrc/css/phui/button/phui-button.css +++ b/webroot/rsrc/css/phui/button/phui-button.css @@ -9,8 +9,6 @@ input[type="submit"] { font: {$basefont}; -webkit-font-smoothing: antialiased; -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; } diff --git a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css index e64989f8cd..0ae87aafcf 100644 --- a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css +++ b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css @@ -675,8 +675,8 @@ ul.phui-oi-list-view .phui-oi-selected .phui-oi-selectable { cursor: pointer; - user-select: none; -webkit-user-select: none; + user-select: none; } /* When the list selection state can be toggled on the client (as in the bulk diff --git a/webroot/rsrc/css/phui/workboards/phui-workcard.css b/webroot/rsrc/css/phui/workboards/phui-workcard.css index d9acef12ef..eea7d7b043 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workcard.css +++ b/webroot/rsrc/css/phui/workboards/phui-workcard.css @@ -28,9 +28,6 @@ .phui-workcard.phui-oi .phui-oi-objname { -webkit-touch-callout: text; -webkit-user-select: text; - -khtml-user-select: text; - -moz-user-select: text; - -ms-user-select: text; user-select: text; } diff --git a/webroot/rsrc/css/phui/workboards/phui-workpanel.css b/webroot/rsrc/css/phui/workboards/phui-workpanel.css index 5ee54f2deb..e1f6d3296b 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workpanel.css +++ b/webroot/rsrc/css/phui/workboards/phui-workpanel.css @@ -29,9 +29,6 @@ .phui-workboard-view { -webkit-touch-callout: none; -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; } From 4862eada5cd05236b81487b261668f2a2d72fad7 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Sat, 27 May 2023 22:18:34 +0100 Subject: [PATCH 159/425] Correct spelling mistakes Test Plan: Only changes to spelling. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25238 --- src/aphront/response/AphrontResponse.php | 2 +- .../conduit/controller/PhabricatorConduitAPIController.php | 2 +- src/applications/config/check/PhabricatorDatabaseSetupCheck.php | 2 +- .../config/option/PhabricatorMetaMTAConfigOptions.php | 2 +- .../xaction/build/HarbormasterBuildMessageResumeTransaction.php | 2 +- .../PhabricatorRepositoryManagementMaintenanceWorkflow.php | 2 +- .../worker/__tests__/PhabricatorChangeParserTestCase.php | 2 +- src/applications/search/compiler/PhutilSearchQueryCompiler.php | 2 +- .../transactions/conduit/TransactionSearchConduitAPIMethod.php | 2 +- .../editor/PhabricatorApplicationTransactionEditor.php | 2 +- src/docs/contributor/database.diviner | 2 +- src/docs/contributor/general_coding_standards.diviner | 2 +- src/docs/user/configuration/configuring_inbound_email.diviner | 2 +- src/docs/user/userguide/multi_factor_auth.diviner | 2 +- src/docs/user/userguide/webhooks.diviner | 2 +- src/infrastructure/customfield/field/PhabricatorCustomField.php | 2 +- .../markup/blockrule/PhutilRemarkupLiteralBlockRule.php | 2 +- .../highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php | 2 +- src/infrastructure/parser/PhutilPygmentizeParser.php | 2 +- src/view/control/AphrontTableView.php | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php index 5dae168c73..355177e316 100644 --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -327,7 +327,7 @@ public static function processValueForJSONEncoding(&$value, $key) { } if ($value instanceof PhutilSafeHTML) { - // TODO: Javelin supports implicity conversion of '__html' objects to + // TODO: Javelin supports implicit conversion of '__html' objects to // JX.HTML, but only for Ajax responses, not behaviors. Just leave things // as they are for now (where behaviors treat responses as HTML or plain // text at their discretion). diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index a4d5fdd568..3ae402cf56 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -37,7 +37,7 @@ public function handleRequest(AphrontRequest $request) { // TODO: The relationship between ConduitAPIRequest and ConduitCall is a // little odd here and could probably be improved. Specifically, the // APIRequest is a sub-object of the Call, which does not parallel the - // role of AphrontRequest (which is an indepenent object). + // role of AphrontRequest (which is an independent object). // In particular, the setUser() and getUser() existing independently on // the Call and APIRequest is very awkward. diff --git a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php index a5989f37b3..d3f6d52f04 100644 --- a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php +++ b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php @@ -106,7 +106,7 @@ private function executeRefChecks(PhabricatorDatabaseRef $ref) { 'The "InnoDB" engine is not available in MySQL (on host "%s"). '. 'Enable InnoDB in your MySQL configuration.'. "\n\n". - '(If you aleady created tables, MySQL incorrectly used some other '. + '(If you already created tables, MySQL incorrectly used some other '. 'engine to create them. You need to convert them or drop and '. 'reinitialize them.)', $ref_key); diff --git a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php index cca8794055..36cbd9bd51 100644 --- a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php +++ b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php @@ -74,7 +74,7 @@ public function getOptions() { - If you use mailing lists, recipients may sometimes get duplicate mail. - Getting threading to work properly is easier, and threading settings - can be customzied by each user. + can be customized by each user. - "Reply All" will never send extra mail to other users involved in the thread. - Required if private reply-to addresses are configured. diff --git a/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php index 1bd7094799..65f27ae374 100644 --- a/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php +++ b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php @@ -99,7 +99,7 @@ protected function newCanSendMessageAssertion( throw new HarbormasterMessageException( pht('Unable to Resume Build'), pht( - 'You can not resume this build beacuse it is already resuming.')); + 'You can not resume this build because it is already resuming.')); } if ($build->isRestarting()) { diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php index 65fe0adaad..6c2f64b31c 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php @@ -23,7 +23,7 @@ protected function didConstruct() { 'name' => 'stop', 'help' => pht( 'Take repositories out of maintenance mode, returning them '. - 'to normal serice.'), + 'to normal service.'), ), array( 'name' => 'repositories', diff --git a/src/applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php b/src/applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php index c3dd6f3d60..0ffb963b71 100644 --- a/src/applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php +++ b/src/applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php @@ -517,7 +517,7 @@ public function testSubversionParser() { '/dir', null, null, - // TODO: This might reasonbly be considered a bug in the parser; it + // TODO: This might reasonably be considered a bug in the parser; it // should probably be COPY_AWAY. DifferentialChangeType::TYPE_CHILD, DifferentialChangeType::FILE_DIRECTORY, diff --git a/src/applications/search/compiler/PhutilSearchQueryCompiler.php b/src/applications/search/compiler/PhutilSearchQueryCompiler.php index 2356a06a8f..951939b52c 100644 --- a/src/applications/search/compiler/PhutilSearchQueryCompiler.php +++ b/src/applications/search/compiler/PhutilSearchQueryCompiler.php @@ -297,7 +297,7 @@ private function tokenizeQuery($query) { $use_substring = true; } else if (phutil_preg_match('/^_/', $value)) { // See T13632. Assume users searching for any term that begins - // with an undescore intend to perform substring search if they + // with an underscore intend to perform substring search if they // don't provide an explicit search function. $use_substring = true; } diff --git a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php index 82eaf08e83..10709bb79f 100644 --- a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php +++ b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php @@ -21,7 +21,7 @@ protected function newDocumentationPages(PhabricatorUser $viewer) { record allows you to see who edited an object, when, and how their edit changed things. -One common reason to call this method is that you're implmenting a webhook and +One common reason to call this method is that you're implementing a webhook and just received a notification that an object has changed. See the Webhooks documentation for more detailed discussion of this use case. diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 908c76a106..0e7333b342 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -391,7 +391,7 @@ private function adjustTransactionValues( $new = $this->getTransactionNewValue($object, $xaction); $xaction->setNewValue($new); - // Apply an optional transformation to convert "external" tranaction + // Apply an optional transformation to convert "external" transaction // values (provided by APIs) into "internal" values. $old = $xaction->getOldValue(); diff --git a/src/docs/contributor/database.diviner b/src/docs/contributor/database.diviner index fc39c1ff1c..dc553678a8 100644 --- a/src/docs/contributor/database.diviner +++ b/src/docs/contributor/database.diviner @@ -134,7 +134,7 @@ eventually, but there isn't a strong case for them at the present time. PHIDs ===== -Each globally referencable object in Phorge has an associated PHID +Each globally referenceable object in Phorge has an associated PHID ("Phorge ID") which serves as a global identifier, similar to a GUID. We use PHIDs for referencing data in different databases. diff --git a/src/docs/contributor/general_coding_standards.diviner b/src/docs/contributor/general_coding_standards.diviner index 5127aebbfc..532b922f9f 100644 --- a/src/docs/contributor/general_coding_standards.diviner +++ b/src/docs/contributor/general_coding_standards.diviner @@ -142,7 +142,7 @@ example. = Documentation, Comments and Formatting = - Prefer to remove code by deleting it over removing it by commenting it out. - It shall live forever in source control, and can be retrieved therefrom if + It shall live forever in source control, and can be retrieved there from if it is ever again called upon. - In source code, use only ASCII printable characters plus space and linefeed. Do not use UTF-8 or other multibyte encodings. diff --git a/src/docs/user/configuration/configuring_inbound_email.diviner b/src/docs/user/configuration/configuring_inbound_email.diviner index dfebfde7aa..c7c9818057 100644 --- a/src/docs/user/configuration/configuring_inbound_email.diviner +++ b/src/docs/user/configuration/configuring_inbound_email.diviner @@ -60,7 +60,7 @@ as the "From" address when it sends mail. The exact address it uses can be configured with `metamta.default-address`. When a user takes an action that generates mail, Phorge sets the -"Reply-To" addresss for the mail to that user's name and address. This means +"Reply-To" address for the mail to that user's name and address. This means that users can reply to email to discuss changes, but: the conversation won't be recorded in Phorge; and users will not be able to use email commands to take actions or make edits. diff --git a/src/docs/user/userguide/multi_factor_auth.diviner b/src/docs/user/userguide/multi_factor_auth.diviner index cccf3e12b9..d445727e6c 100644 --- a/src/docs/user/userguide/multi_factor_auth.diviner +++ b/src/docs/user/userguide/multi_factor_auth.diviner @@ -134,7 +134,7 @@ Providers may be in these states: - **Active**: Users may add new factors. Users will be prompted to respond to challenges from these providers when they take a sensitive action. - **Deprecated**: Users may not add new factors, but they will still be - asked to respond to challenges from exising factors. + asked to respond to challenges from existing factors. - **Disabled**: Users may not add new factors, and existing factors will not be used. If MFA is required and a user only has disabled factors, they will be forced to add a new factor. diff --git a/src/docs/user/userguide/webhooks.diviner b/src/docs/user/userguide/webhooks.diviner index c2d0678b26..47b02cc261 100644 --- a/src/docs/user/userguide/webhooks.diviner +++ b/src/docs/user/userguide/webhooks.diviner @@ -178,7 +178,7 @@ Retries and Rate Limiting Test requests are never retried: they execute exactly once. Live requests are automatically retried. If your endpoint does not return a -HTTP 2XX response, the request will be retried regularly until it suceeds. +HTTP 2XX response, the request will be retried regularly until it succeeds. Retries will continue until the request succeeds or is garbage collected. By default, this is after 7 days. diff --git a/src/infrastructure/customfield/field/PhabricatorCustomField.php b/src/infrastructure/customfield/field/PhabricatorCustomField.php index 9e2bf6895f..4e55348c36 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomField.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomField.php @@ -1679,7 +1679,7 @@ private static function adjustCustomFieldsForObjectSubtype( foreach ($map as $field_key => $field) { // For now, only support overriding standard custom fields. In the // future there's no technical or product reason we couldn't let you - // override (some properites of) other fields like "Title", but they + // override (some properties of) other fields like "Title", but they // don't usually support appropriate "setX()" methods today. if (!($field instanceof PhabricatorStandardCustomField)) { // For fields that are proxies on top of StandardCustomField, which diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php index 4f30ad089e..505c7ecfd1 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php @@ -7,7 +7,7 @@ public function getPriority() { } public function getMatchingLineCount(array $lines, $cursor) { - // NOTE: We're consuming all continguous blocks of %%% literals, so this: + // NOTE: We're consuming all contiguous blocks of %%% literals, so this: // // %%%a%%% // %%%b%%% diff --git a/src/infrastructure/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php b/src/infrastructure/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php index c34153ba42..f617122ec8 100644 --- a/src/infrastructure/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php +++ b/src/infrastructure/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php @@ -32,7 +32,7 @@ private function applyXHPHighlight($result) { // We perform two passes here: one using the AST to find symbols we care // about -- particularly, class names and function names. These are used - // in the crossreference stuff to link into Diffusion. After we've done our + // in the cross-reference stuff to link into Diffusion. After we've done our // AST pass, we do a followup pass on the token stream to catch all the // simple stuff like strings and comments. diff --git a/src/infrastructure/parser/PhutilPygmentizeParser.php b/src/infrastructure/parser/PhutilPygmentizeParser.php index 35c1d5739c..39b0e441c5 100644 --- a/src/infrastructure/parser/PhutilPygmentizeParser.php +++ b/src/infrastructure/parser/PhutilPygmentizeParser.php @@ -32,7 +32,7 @@ public function parse($block) { $c = $block[$ii]; switch ($mode) { case 'text': - // We're in general text between tags, and just passing characers + // We're in general text between tags, and just passing characters // through unmodified. if ($c == '<') { $mode = 'tag'; diff --git a/src/view/control/AphrontTableView.php b/src/view/control/AphrontTableView.php index 0e06b97b4a..ab1f6be0ed 100644 --- a/src/view/control/AphrontTableView.php +++ b/src/view/control/AphrontTableView.php @@ -392,7 +392,7 @@ public static function renderSingleDisplayLine($line) { // white-space: pre to prevent wrapping. We need to append a character // (  -- nonbreaking space) afterward to give the bounds div height // (alternatively, we could hard-code the line height). This is gross but - // it's not clear that there's a better appraoch. + // it's not clear that there's a better approach. return phutil_tag( 'div', From 827f63a065432e4223cef357e47992291f34f15e Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 29 May 2023 09:12:44 +0200 Subject: [PATCH 160/425] Cleanup unused cowsay files (related to legacy Perl binary version) Summary: Premising that Remarkup supports a fully-featured cowsay generator (!)... since 2015 this awesome thing no longer requires a binary cowsay command, since everything was rewritten in the core: c705c8011e964945a6f73ae78087e188de0bd71f So, some files from the legacy cowsay version in Perl can be safely removed. We still thank the original author Tony Monroe for the original version in Perl (dated 2000), and Evan Priestley for the re-implementation in PHP. Probably nobody knows about this feature. This may change in the future. See T15401. Closes T15418 Test Plan: Try some awesome cows to verify that everything still works. For instance: ``` cowsay {{{ hello }}} ``` ``` cowsay (think=yes,eyes=XX,tongue=^^) {{{ hello }}} ``` ``` cowsay (cow=stegosaurus) {{{ hello }}} ``` If nothing explodes, yuppie! Cowsay still works. Credits to the original version in Perl remained in place. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15418 Differential Revision: https://we.phorge.it/D25242 --- externals/cowsay/INSTALL | 15 --- externals/cowsay/MANIFEST | 11 -- externals/cowsay/README | 8 ++ externals/cowsay/Wrap.pm.diff | 47 ------- externals/cowsay/cowsay | 187 ---------------------------- externals/cowsay/cowsay.1 | 168 ------------------------- externals/cowsay/install.sh | 92 -------------- externals/cowsay/pgp_public_key.txt | 14 --- 8 files changed, 8 insertions(+), 534 deletions(-) delete mode 100644 externals/cowsay/INSTALL delete mode 100644 externals/cowsay/MANIFEST delete mode 100644 externals/cowsay/Wrap.pm.diff delete mode 100755 externals/cowsay/cowsay delete mode 100644 externals/cowsay/cowsay.1 delete mode 100755 externals/cowsay/install.sh delete mode 100644 externals/cowsay/pgp_public_key.txt diff --git a/externals/cowsay/INSTALL b/externals/cowsay/INSTALL deleted file mode 100644 index d3844ca565..0000000000 --- a/externals/cowsay/INSTALL +++ /dev/null @@ -1,15 +0,0 @@ -================= -Installing cowsay -================= - -If you really want to get things installed a nice and pretty way, - - sh install.sh - -It will ask approximately one question. If you can't answer it, -you need serious help. - -If the install goes well, you can start cowing immediately! Just -be sure to read the manual page first... - -$Id: INSTALL,v 1.1 1999/08/14 08:03:17 tony Exp $ diff --git a/externals/cowsay/MANIFEST b/externals/cowsay/MANIFEST deleted file mode 100644 index a3ff805fed..0000000000 --- a/externals/cowsay/MANIFEST +++ /dev/null @@ -1,11 +0,0 @@ -ChangeLog Changes to recent versions. -INSTALL Instructions for installing cowsay. -LICENSE The license for use and redistribution of cowsay. -MANIFEST This file. -README Read this first. Really. -Wrap.pm.diff Diff for Text/Wrap.pm. -cows/* Support files used by cowsay. -cowsay Main cowsay executable. -cowsay.1 Main cowsay manual page. -install.sh cowsay installation script. -pgp_public_key.txt Verify the signature file with this key. diff --git a/externals/cowsay/README b/externals/cowsay/README index b5de08c405..50b746c1ec 100644 --- a/externals/cowsay/README +++ b/externals/cowsay/README @@ -36,3 +36,11 @@ this directory. -- Tony Monroe (tony@nog.net) $Id: README,v 1.3 2000/05/28 06:24:46 tony Exp $ + +---- + +In September 2015, Phabricator imported the Perl version of cowsay +in the core, to use its nice template files. Anyway, the logic now +is not in Perl. It was re-implemented in PHP, here: + +src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php diff --git a/externals/cowsay/Wrap.pm.diff b/externals/cowsay/Wrap.pm.diff deleted file mode 100644 index 1da064079e..0000000000 --- a/externals/cowsay/Wrap.pm.diff +++ /dev/null @@ -1,47 +0,0 @@ -*** Wrap.pm.in Thu May 22 00:21:42 1997 ---- Wrap.pm Fri Nov 12 10:00:15 1999 -*************** -*** 3,9 **** - require Exporter; - - @ISA = (Exporter); -! @EXPORT = qw(wrap); - @EXPORT_OK = qw($columns); - - $VERSION = 97.011701; ---- 3,9 ---- - require Exporter; - - @ISA = (Exporter); -! @EXPORT = qw(wrap fill); - @EXPORT_OK = qw($columns); - - $VERSION = 97.011701; -*************** -*** 66,71 **** ---- 66,90 ---- - - print "-----------$r---------\n" if $debug;; - return $r; -+ } -+ -+ ## Copied up from below. -+ sub fill -+ { -+ my ($ip, $xp, @raw) = @_; -+ my @para; -+ my $pp; -+ -+ for $pp (split(/\n\s+/, join("\n",@raw))) { -+ $pp =~ s/\s+/ /g; -+ my $x = wrap($ip, $xp, $pp); -+ push(@para, $x); -+ } -+ -+ # if paragraph_indent is the same as line_indent, -+ # separate paragraphs with blank lines -+ -+ return join ($ip eq $xp ? "\n\n" : "\n", @para); - } - - 1; diff --git a/externals/cowsay/cowsay b/externals/cowsay/cowsay deleted file mode 100755 index 900ca46014..0000000000 --- a/externals/cowsay/cowsay +++ /dev/null @@ -1,187 +0,0 @@ -#%BANGPERL% - -## -## Cowsay 3.03 -## -## This file is part of cowsay. (c) 1999-2000 Tony Monroe. -## - -use Text::Tabs qw(expand); -use Text::Wrap qw(wrap fill $columns); -use File::Basename; -use Getopt::Std; -use Cwd; - -$version = "3.03"; -$progname = basename($0); -$eyes = "oo"; -$tongue = " "; -$cowpath = $ENV{'COWPATH'} || '%PREFIX%/share/cows'; -@message = (); -$thoughts = ""; - -## Yeah, this is rude, I know. But hopefully it gets around a nasty -## little version dependency. - -$Text::Wrap::initial_tab = 8; -$Text::Wrap::subsequent_tab = 8; -$Text::Wrap::tabstop = 8; - -## One of these days, we'll get it ported to Windows. Yeah, right. - -if (($^O eq "MSWin32") or ($^O eq "Windows_NT")) { ## Many perls, eek! - $pathsep = ';'; -} else { - $pathsep = ':'; -} - -%opts = ( - 'e' => 'oo', - 'f' => 'default.cow', - 'n' => 0, - 'T' => ' ', - 'W' => 40, -); - -getopts('bde:f:ghlLnNpstT:wW:y', \%opts); - -&display_usage if $opts{'h'}; -&list_cowfiles if $opts{'l'}; - -$borg = $opts{'b'}; -$dead = $opts{'d'}; -$greedy = $opts{'g'}; -$paranoid = $opts{'p'}; -$stoned = $opts{'s'}; -$tired = $opts{'t'}; -$wired = $opts{'w'}; -$young = $opts{'y'}; -$eyes = substr($opts{'e'}, 0, 2); -$tongue = substr($opts{'T'}, 0, 2); -$the_cow = ""; - -&slurp_input; -$Text::Wrap::columns = $opts{'W'}; -@message = ($opts{'n'} ? expand(@message) : - split("\n", fill("", "", @message))); -&construct_balloon; -&construct_face; -&get_cow; -print @balloon_lines; -print $the_cow; - -sub list_cowfiles { - my $basedir; - my @dirfiles; - chop($basedir = cwd); - for my $d (split(/$pathsep/, $cowpath)) { - print "Cow files in $d:\n"; - opendir(COWDIR, $d) || die "$0: Cannot open $d\n"; - for my $file (readdir COWDIR) { - if ($file =~ s/\.cow$//) { - push(@dirfiles, $file); - } - } - closedir(COWDIR); - print wrap("", "", sort @dirfiles), "\n"; - @dirfiles = (); - chdir($basedir); - } - exit(0); -} - -sub slurp_input { - unless ($ARGV[0]) { - chomp(@message = ); - } else { - &display_usage if $opts{'n'}; - @message = join(' ', @ARGV); - } -} - -sub maxlength { - my ($l, $m); - $m = -1; - for my $i (@_) { - $l = length $i; - $m = $l if ($l > $m); - } - return $m; -} - -sub construct_balloon { - my $max = &maxlength(@message); - my $max2 = $max + 2; ## border space fudge. - my $format = "%s %-${max}s %s\n"; - my @border; ## up-left, up-right, down-left, down-right, left, right - if ($0 =~ /think/i) { - $thoughts = 'o'; - @border = qw[ ( ) ( ) ( ) ]; - } elsif (@message < 2) { - $thoughts = '\\'; - @border = qw[ < > ]; - } else { - $thoughts = '\\'; - if ($V and $V gt v5.6.0) { # Thanks, perldelta. - @border = qw[ / \\ \\ / | | ]; - } else { - @border = qw[ / \ \ / | | ]; - } - } - push(@balloon_lines, - " " . ("_" x $max2) . " \n" , - sprintf($format, $border[0], $message[0], $border[1]), - (@message < 2 ? "" : - map { sprintf($format, $border[4], $_, $border[5]) } - @message[1 .. $#message - 1]), - (@message < 2 ? "" : - sprintf($format, $border[2], $message[$#message], $border[3])), - " " . ("-" x $max2) . " \n" - ); -} - -sub construct_face { - if ($borg) { $eyes = "=="; } - if ($dead) { $eyes = "xx"; $tongue = "U "; } - if ($greedy) { $eyes = "\$\$"; } - if ($paranoid) { $eyes = "@@"; } - if ($stoned) { $eyes = "**"; $tongue = "U "; } - if ($tired) { $eyes = "--"; } - if ($wired) { $eyes = "OO"; } - if ($young) { $eyes = ".."; } -} - -sub get_cow { -## -## Get a cow from the specified cowfile; otherwise use the default cow -## which was defined above in $the_cow. -## - my $f = $opts{'f'}; - my $full = ""; - if ($opts{'f'} =~ m,/,) { - $full = $opts{'f'}; - } else { - for my $d (split(/:/, $cowpath)) { - if (-f "$d/$f") { - $full = "$d/$f"; - last; - } elsif (-f "$d/$f.cow") { - $full = "$d/$f.cow"; - last; - } - } - if ($full eq "") { - die "$progname: Could not find $f cowfile!\n"; - } - } - do $full; - die "$progname: $@\n" if $@; -} - -sub display_usage { - die </dev/null 2>&1; then - echo Found a good perl in $perl - goodperls="$goodperls $perl" - fi -done -echo The following perl executables will run cowsay: -echo $goodperls -echo I recommend the latest stable perl you can find. -set $goodperls -if [ -z "$1" ]; then - echo Ack! You do not have Perl 5 installed correctly! - echo Get thee to CPAN! - exit 1 -fi -usethisperl=$1 -echo I will be using $1 because I know it will work. - -echo Now I need an installation prefix. I will use /usr/local unless -printf "you give me a better idea here: " -if [ -n "$backdoor" ]; then - prefix=$backdoor - printf "%s (specified on command line)\n" $prefix -else - read prefix -fi - -PREFIX=${prefix:-/usr/local} - -echo Okay, time to install this puppy. - -echo s,%BANGPERL%,!$usethisperl,\; > install.pl -echo s,%PREFIX%,$PREFIX,\; >> install.pl -set -x -mkdir -p $PREFIX/bin || (mkdir $PREFIX; mkdir $PREFIX/bin) -$usethisperl -p install.pl cowsay > $PREFIX/bin/cowsay -chmod a+x $PREFIX/bin/cowsay -ln -s cowsay $PREFIX/bin/cowthink -mkdir -p $PREFIX/man/man1 || ($mkdir $PREFIX; mkdir $PREFIX/man; mkdir $PREFIX/man/man1) -$usethisperl -p install.pl cowsay.1 > $PREFIX/man/man1/cowsay.1 -chmod a+r $PREFIX/man/man1/cowsay.1 -ln -s cowsay.1 $PREFIX/man/man1/cowthink.1 -mkdir -p $PREFIX/share/cows || (mkdir $PREFIX; mkdir $PREFIX/share; mkdir $PREFIX/share/cows) -tar -cf - $filelist | (cd $PREFIX/share && tar -xvf -) -set +x - -echo Okay, let us see if the install actually worked. - -if [ ! -f $PREFIX/share/cows/default.cow ]; then - echo The default cow file did not make it across! - echo Ooops, it failed...sorry! - exit 1 -fi - -echo Installation complete! Enjoy the cows! diff --git a/externals/cowsay/pgp_public_key.txt b/externals/cowsay/pgp_public_key.txt deleted file mode 100644 index 135735e6f9..0000000000 --- a/externals/cowsay/pgp_public_key.txt +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: 2.6.2 - -mQCNAzNS+H4AAAEEAMilCcxLMIqMNXhZoeHjbeJGnHoKP0JpKDejz66qBlDwP+du -FvVBHkyNIuU3orKe7l/aXIR8TcpjrxdcmrjjzwuB1aV48V4swT7v9UXvv9YP41Pd -1pXYXNelXHmE0eKSfcnkkFmYTJBXPaPNTJ3rpZpZOEW3SfgrrheqQm6w/6IJAAUR -tBpUb255IE1vbnJvZSA8dG9ueUBub2cubmV0PrQdVG9ueSBNb25yb2UgPHRtb25y -b2VAbm9nLm5ldD6JAJUDBRA2bDA5F6pCbrD/ogkBASOZA/9XtYSpcPjpi62MTEZl -l+wV1svtlUlPvKkDE7FB9bwnMbF1FgGhTe/H4/8qDu20m5eGxMX58Ri7HPXWjjJ/ -CrUWMzGVbg1JBkgh+d9QvEywmR0j4WezaifW1nCbxk0GrW1PjJyGb3kx7vrIF1Km -xt6VwxTgfJzjIioBJFb4EiDyaLQnVG9ueSBNb25yb2UgPHRtb25yb2VAQ1NVQS5C -ZXJrZWxleS5FRFU+ -=WpM4 ------END PGP PUBLIC KEY BLOCK----- From 0d81da5909239d404b92f69848b858257b564b4e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 29 May 2023 12:35:37 +0200 Subject: [PATCH 161/425] Fix PHP 8.1 "explode(null)" exception which blocks rendering the Multimeter page Summary: Passing null as an input string to explode() is deprecated in PHP 8. Thus first check via `phutil_nonempty_string()` that the input string is not null. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15361 Test Plan: Applied this change; afterwards `/multimeter/` correctly rendered in web browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15361 Differential Revision: https://we.phorge.it/D25254 --- .../multimeter/controller/MultimeterSampleController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/multimeter/controller/MultimeterSampleController.php b/src/applications/multimeter/controller/MultimeterSampleController.php index 190a839f63..0aca60f0e1 100644 --- a/src/applications/multimeter/controller/MultimeterSampleController.php +++ b/src/applications/multimeter/controller/MultimeterSampleController.php @@ -10,7 +10,7 @@ public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $group_map = $this->getColumnMap(); - $group = explode('.', $request->getStr('group')); + $group = explode('.', $request->getStr('group', '')); $group = array_intersect($group, array_keys($group_map)); $group = array_fuse($group); From ea43519c6bb10147f4db7d1398133e46631c20a0 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 30 May 2023 10:43:18 +0200 Subject: [PATCH 162/425] Correct spelling mistake in PhabricatorAuditEditor Summary: Closes T15267 Test Plan: Only changes to spelling. Reviewers: O1 Blessed Committers, chris Reviewed By: O1 Blessed Committers, chris Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15267 Differential Revision: https://we.phorge.it/D25251 --- src/applications/audit/editor/PhabricatorAuditEditor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php index 7b97ea515a..9c2a5d7e96 100644 --- a/src/applications/audit/editor/PhabricatorAuditEditor.php +++ b/src/applications/audit/editor/PhabricatorAuditEditor.php @@ -700,7 +700,7 @@ public function getMailTagsMap() { PhabricatorAuditTransaction::MAILTAG_COMMIT => pht('A commit is created.'), PhabricatorAuditTransaction::MAILTAG_ACTION_CONCERN => - pht('A commit has a concerned raised against it.'), + pht('A commit has a concern raised against it.'), PhabricatorAuditTransaction::MAILTAG_ACTION_ACCEPT => pht('A commit is accepted.'), PhabricatorAuditTransaction::MAILTAG_ACTION_RESIGN => From f2329f81650481403ed24330b521a5671030ffd0 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 30 May 2023 10:45:34 +0200 Subject: [PATCH 163/425] Correct two spelling mistakes in Differential Summary: Closes T15269 Test Plan: Only changes to spelling. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15269 Differential Revision: https://we.phorge.it/D25252 --- .../differential/editor/DifferentialRevisionEditEngine.php | 2 +- .../xaction/DifferentialRevisionPlanChangesTransaction.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/differential/editor/DifferentialRevisionEditEngine.php b/src/applications/differential/editor/DifferentialRevisionEditEngine.php index 74fee82219..995179a864 100644 --- a/src/applications/differential/editor/DifferentialRevisionEditEngine.php +++ b/src/applications/differential/editor/DifferentialRevisionEditEngine.php @@ -307,7 +307,7 @@ protected function buildCustomEditFields($object) { pht('Hold as Draft')) ->setTransactionType( DifferentialRevisionHoldDraftTransaction::TRANSACTIONTYPE) - ->setDescription(pht('Hold revision as as draft.')) + ->setDescription(pht('Hold revision as draft.')) ->setConduitDescription( pht( 'Change autosubmission from draft state after builds finish.')) diff --git a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php index 144152526d..381a61a09d 100644 --- a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php @@ -86,7 +86,7 @@ protected function validateAction($object, PhabricatorUser $viewer) { if ($object->isClosed()) { throw new Exception( pht( - 'You can not plan changes to this this revision because it has '. + 'You can not plan changes to this revision because it has '. 'already been closed.')); } From 2a9eca696f09c32932c58ba76d9b9c5bccdfc8ee Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 30 May 2023 10:53:27 +0200 Subject: [PATCH 164/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering a Diffusion repository page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15421 Test Plan: Applied this change, create a repository, and afterwards the repository page at `/diffusion/9/` is shown with its name and "Empty Repository - This repository does not have any commits yet." - have a Diffusion repository with Staging Area and Automation already setup - create a Differential patch from arc - press the Land button on the UI and write random things inside Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15421 Differential Revision: https://we.phorge.it/D25247 --- .../repository/query/PhabricatorRepositoryRefCursorQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php index 5e894333f6..a6177c20b5 100644 --- a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php @@ -132,7 +132,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $name_hashes); } - if (strlen($this->datasourceQuery)) { + if (phutil_nonempty_string($this->datasourceQuery)) { $where[] = qsprintf( $conn, 'refNameRaw LIKE %>', From 72fdf232b4abf20d44267443e62b0d119fc72de3 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 30 May 2023 10:56:51 +0200 Subject: [PATCH 165/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering Conduit's dashboard.panel.edit page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15425 Test Plan: Applied this change; afterwards `/conduit/method/dashboard.panel.edit/` correctly renders in browser. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15425 Differential Revision: https://we.phorge.it/D25257 --- .../editengine/PhabricatorEditEngineAPIMethod.php | 2 +- .../transactions/editfield/PhabricatorEditField.php | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index 2028f10d90..26811d7534 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -95,7 +95,7 @@ private function buildEditTypesDocumentationPages( $section[] = $type->getConduitDescription(); $type_documentation = $type->getConduitDocumentation(); - if (strlen($type_documentation)) { + if (phutil_nonempty_string($type_documentation)) { $section[] = $type_documentation; } diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php index 4457e03bae..4c72d63214 100644 --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -169,11 +169,20 @@ public function getConduitDescription() { return $this->conduitDescription; } + /** + * Set the Conduit documentation in raw Remarkup. + * @param string|null $conduit_documentation + * @return self + */ public function setConduitDocumentation($conduit_documentation) { $this->conduitDocumentation = $conduit_documentation; return $this; } + /** + * Get the Conduit documentation in raw Remarkup. + * @return string|null + */ public function getConduitDocumentation() { return $this->conduitDocumentation; } From 7cf210b763857f80cfce7fc9c8078d42da533756 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 30 May 2023 10:58:55 +0200 Subject: [PATCH 166/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering Config's Database Status page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15428 Test Plan: Applied this change; afterwards displays "Database Status" page at `/config/database/` correctly rendered in web browser. Also visit every single database name, to see that also there nothing explode. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15428 Differential Revision: https://we.phorge.it/D25260 --- .../services/PhabricatorConfigDatabaseStatusController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php b/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php index 09f4f344b5..40c4cfdf8c 100644 --- a/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php +++ b/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php @@ -837,7 +837,7 @@ private function getURI(array $properties) { $parts = array(); foreach ($properties as $key => $property) { - if (!strlen($property)) { + if (!phutil_nonempty_string($property)) { continue; } From e11c5486c92bd75895eda4e0d905d8e49c0f68b1 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 30 May 2023 11:49:26 +0200 Subject: [PATCH 167/425] Fix typo in PhortuneMerchant.php Summary: Closes T15429 Test Plan: Look at the string. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15429 Differential Revision: https://we.phorge.it/D25261 --- src/applications/phortune/storage/PhortuneMerchant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/phortune/storage/PhortuneMerchant.php b/src/applications/phortune/storage/PhortuneMerchant.php index 6e0bf81e22..25426d3b28 100644 --- a/src/applications/phortune/storage/PhortuneMerchant.php +++ b/src/applications/phortune/storage/PhortuneMerchant.php @@ -157,7 +157,7 @@ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { } public function describeAutomaticCapability($capability) { - return pht("A merchant's members an always view and edit it."); + return pht("A merchant's members can always view and edit it."); } } From 1dd9609beddbda1634e7527b6182a689755a1cbb Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 31 May 2023 09:38:22 +0200 Subject: [PATCH 168/425] Fix PHP 8.1 "preg_match(null)" exception which blocks rendering the "Browse Herald Rules" dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: `preg_match()` does not accept passing null as the `$subject` string parameter in PHP 8.1. Thus first check that `$subject !== null`. ``` EXCEPTION: (RuntimeException) preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=0e32dbc1ac8f), phorge(head=diffusionRepoPage, ref.master=5405134fa5db, ref.diffusionRepoPage=dbe5e3a68c41) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> preg_match(string, NULL) called at [/src/applications/herald/typeahead/HeraldRuleDatasource.php:25] ``` Closes T15422 Test Plan: Applied this change; afterwards on the "Diffusion 🡒 Push Logs 🡒 Advanced Search" page at `/diffusion/pushlog/?repositories=PHID-REPO-someRepositoryString`, clicking the search icon for the "Blocked By" field correctly renders.the "Browse Herald Rules" overlay dialog, listing available Herald rules. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15422 Differential Revision: https://we.phorge.it/D25248 --- src/applications/herald/typeahead/HeraldRuleDatasource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/herald/typeahead/HeraldRuleDatasource.php b/src/applications/herald/typeahead/HeraldRuleDatasource.php index 88343bd433..6b73bc0ab6 100644 --- a/src/applications/herald/typeahead/HeraldRuleDatasource.php +++ b/src/applications/herald/typeahead/HeraldRuleDatasource.php @@ -22,7 +22,7 @@ public function loadResults() { $query = id(new HeraldRuleQuery()) ->setViewer($viewer); - if (preg_match('/^[hH]\d+\z/', $raw_query)) { + if (($raw_query !== null) && preg_match('/^[hH]\d+\z/', $raw_query)) { $id = trim($raw_query, 'hH'); $id = (int)$id; $query->withIDs(array($id)); From 6b8ec50148909938850b5acfd11725ae23a8e31b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 31 May 2023 09:40:06 +0200 Subject: [PATCH 169/425] Fix PHP 8.1 "preg_match(null)" exception when cloning a repository with no URI set Summary: `preg_match()` does not accept passing null as the `$subject` string parameter in PHP 8.1. Thus add a `phutil_nonempty_string()` check if the `$subject` parameter is a non-empty string. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=arcpatch-D25250, ref.master=18554ea76ceb, ref.arcpatch-D25250=fade4603a799), phorge(head=diffusionEmptyBranchHistory, ref.master=e11c5486c92b, ref.diffusionEmptyBranchHistory=76f042e4b969) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> preg_match(string, NULL) called at [/src/applications/diffusion/request/DiffusionGitRequest.php:6] ``` Closes T15435 Test Plan: After applying this change, try to "Clone" a Repository with no URI set shows the overlay dialog `Clone Repository - Repository has no URIs set.` as expected. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15435 Differential Revision: https://we.phorge.it/D25266 --- src/applications/diffusion/request/DiffusionGitRequest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/applications/diffusion/request/DiffusionGitRequest.php b/src/applications/diffusion/request/DiffusionGitRequest.php index a283fff206..16953f5996 100644 --- a/src/applications/diffusion/request/DiffusionGitRequest.php +++ b/src/applications/diffusion/request/DiffusionGitRequest.php @@ -3,6 +3,9 @@ final class DiffusionGitRequest extends DiffusionRequest { protected function isStableCommit($symbol) { + if ($symbol === null) { + return false; + } return preg_match('/^[a-f0-9]{40}\z/', $symbol); } From cbc0e661544a31290099ab3d88481bb8d4a3003c Mon Sep 17 00:00:00 2001 From: dylsss Date: Fri, 2 Jun 2023 15:16:33 +0200 Subject: [PATCH 170/425] Change erroneous loose comparison to strict comparison in tab panels Summary: Fix a loose comparison causing a bug when comparing the selected tab (0) with a tab which has an alphanumeric ID which doesn't begin with an integer. E.g., `(0 == 'kq3p37awi2n5')` is true in PHP 7.4 and below, this can sometimes cause multiple tabs to be displayed when a tab panel is first loaded onto a page: {F278735} Test Plan: Create a tab panel with at least 3 tabs, add a couple more and ensure that multiple tabs aren't loaded. Reviewers: O1 Blessed Committers, valerio.bozzolan, Cigaryno, avivey Reviewed By: O1 Blessed Committers, valerio.bozzolan, Cigaryno, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15146 Differential Revision: https://we.phorge.it/D25067 --- .../paneltype/PhabricatorDashboardTabsPanelType.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php index 91c6d17833..54c26dd9d3 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php @@ -111,9 +111,11 @@ public function renderPanelContent( $name = pht('Unnamed Tab'); } + $is_selected = (string)$idx === (string)$selected; + $tab_view = id(new PHUIListItemView()) ->setHref('#') - ->setSelected((string)$idx === (string)$selected) + ->setSelected($is_selected) ->addSigil('dashboard-tab-panel-tab') ->setMetadata(array('panelKey' => $idx)) ->setName($name); @@ -286,7 +288,7 @@ public function renderPanelContent( 'div', array( 'id' => $content_id, - 'style' => ($idx == $selected) ? null : 'display: none', + 'style' => $is_selected ? null : 'display: none', ), $panel_content); From 0cbcb110b03bc31a772c5971845235fcac7d0fc8 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 2 Jun 2023 20:04:53 +0200 Subject: [PATCH 171/425] Fix PHP 8.1 "strlen(null)" exceptions trying to View Branches of a Diffusion repository Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=e4fd31ec024e), phorge(head=D25241, ref.master=b1edfea09bad, ref.D25241=b1edfea09bad) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/controller/DiffusionBranchTableController.php:29] ``` ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=e4fd31ec024e), phorge(head=D25241, ref.master=b1edfea09bad, ref.D25241=b1edfea09bad) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php:33] ``` Closes T15414 Test Plan: Applied these two changes and at least got to `(FilesystemException) Filesystem path "/var/repo/1/" does not exist.` as an error message, instead of being stuck at `(RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated`. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15414 Differential Revision: https://we.phorge.it/D25241 --- .../diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php | 2 +- .../diffusion/controller/DiffusionBranchTableController.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php index 345e63fc3b..0286b49ee4 100644 --- a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php @@ -30,7 +30,7 @@ protected function getGitResult(ConduitAPIRequest $request) { $repository = $drequest->getRepository(); $contains = $request->getValue('contains'); - if (strlen($contains)) { + if (phutil_nonempty_string($contains)) { // See PHI958 (and, earlier, PHI720). If "patterns" are provided, pass // them to "git branch ..." to let callers test for reachability from diff --git a/src/applications/diffusion/controller/DiffusionBranchTableController.php b/src/applications/diffusion/controller/DiffusionBranchTableController.php index 13f566a57b..63b1dbc592 100644 --- a/src/applications/diffusion/controller/DiffusionBranchTableController.php +++ b/src/applications/diffusion/controller/DiffusionBranchTableController.php @@ -26,7 +26,7 @@ public function handleRequest(AphrontRequest $request) { ); $contains = $drequest->getSymbolicCommit(); - if (strlen($contains)) { + if (phutil_nonempty_string($contains)) { $params['contains'] = $contains; } From dd24c94b0741ae2c3fdf73d6e70d0a10e18ba010 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 2 Jun 2023 20:39:54 +0200 Subject: [PATCH 172/425] Fix PHP 8.1 "strlen(null)" exceptions block creating a diff in Differential web interface Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=18554ea76ceb), phorge(head=diffDiff, ref.master=e11c5486c92b, ref.diffDiff=e11c5486c92b) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php:160] ``` Closes T15430 Test Plan: After applying these two changes, going to `/differential/diff/create/`, pasting the content of a diff file into the "Raw Diff" field, and selecting the "Create Diff" button, `/differential/diff/1/` rendered correctly in web browser. Also, install xdebug and try again with coverage mode enabled in your php.ini of your PHP cli. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15430 Differential Revision: https://we.phorge.it/D25262 --- .../diff/view/PHUIDiffTableOfContentsItemView.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php b/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php index f705fa4377..752d895952 100644 --- a/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php +++ b/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php @@ -42,6 +42,11 @@ public function setCoverage($coverage) { return $this; } + /** + * Get the Coverage, expressed as a string, each letter with this meaning: + * N: Not Executable, C: Covered, U: Uncovered. + * @return string|null + */ public function getCoverage() { return $this->coverage; } @@ -139,7 +144,7 @@ public function renderCoverage() { $not_applicable = '-'; $coverage = $this->getCoverage(); - if (!strlen($coverage)) { + if (!phutil_nonempty_string($coverage)) { return $not_applicable; } @@ -157,7 +162,7 @@ public function renderModifiedCoverage() { $not_applicable = '-'; $coverage = $this->getCoverage(); - if (!strlen($coverage)) { + if (!phutil_nonempty_string($coverage)) { return $not_applicable; } From d8d65f3f87ed74a3c500278cbb3f0b7490fff9a7 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 3 Jun 2023 11:50:16 +0200 Subject: [PATCH 173/425] Fix PHP 8.1 "strlen(null)" exception on UIExamples' Bars page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=uiExamples, ref.master=dd24c94b0741, ref.uiExamples=dd24c94b0741) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/view/phui/PHUISegmentBarSegmentView.php:58] ``` Closes T15441 Test Plan: After applying this change, going to `/uiexample/view/PhabricatorAphrontBarUIExample/`,"Bars (PhabricatorAphrontBarUIExample)" renders with beautiful colors. To see that page, enable prototypes with: ./bin/config set phabricator.show-prototypes true Also try to create a Milestone on a Project, with the Config `maniphest.points` enabled, and try to create some Tasks with different points. Visit its Workboard to see the bar that still works with a useful tooltip. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15441 Differential Revision: https://we.phorge.it/D25272 --- src/view/phui/PHUISegmentBarSegmentView.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/view/phui/PHUISegmentBarSegmentView.php b/src/view/phui/PHUISegmentBarSegmentView.php index 6bd3c530ef..d786e89eaa 100644 --- a/src/view/phui/PHUISegmentBarSegmentView.php +++ b/src/view/phui/PHUISegmentBarSegmentView.php @@ -26,6 +26,11 @@ public function setPosition($position) { return $this; } + /** + * Set a Tooltip. + * @param string|null $tooltip + * @return self + */ public function setTooltip($tooltip) { $this->tooltip = $tooltip; return $this; @@ -55,7 +60,7 @@ protected function getTagAttributes() { $left = sprintf('%.2f%%', $left); $tooltip = $this->tooltip; - if (strlen($tooltip)) { + if (phutil_nonempty_string($tooltip)) { Javelin::initBehavior('phabricator-tooltips'); $sigil = 'has-tooltip'; From cb938d869c3f7324f9d703e7e531a7d31daaa57a Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Sat, 3 Jun 2023 09:41:30 -0700 Subject: [PATCH 174/425] Teach `./bin/celerity` about sprite maps Summary: Add `./bin/celerity sprites`, to replace script `./scripts/celerity/generate_sprites.php`. Also make new workflow run `./bin/celerity map` at the same time. Fixes T15437. Test Plan: Changes a file that goes in the sprites, run new command Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15437 Differential Revision: https://we.phorge.it/D25274 --- scripts/celerity/generate_sprites.php | 80 +------------ src/__phutil_library_map__.php | 2 + ...erityManagementGenerateSpritesWorkflow.php | 108 ++++++++++++++++++ 3 files changed, 113 insertions(+), 77 deletions(-) create mode 100644 src/applications/celerity/management/CelerityManagementGenerateSpritesWorkflow.php diff --git a/scripts/celerity/generate_sprites.php b/scripts/celerity/generate_sprites.php index ccdd194b36..a85f9266ae 100755 --- a/scripts/celerity/generate_sprites.php +++ b/scripts/celerity/generate_sprites.php @@ -3,80 +3,6 @@ require_once dirname(dirname(__FILE__)).'/__init_script__.php'; -$args = new PhutilArgumentParser($argv); -$args->setTagline(pht('regenerate CSS sprite sheets')); -$args->setSynopsis(<<parseStandardArguments(); -$args->parse( - array( - array( - 'name' => 'force', - 'help' => pht('Force regeneration even if sources have not changed.'), - ), - )); - -$root = dirname(phutil_get_library_root('phabricator')); -$webroot = $root.'/webroot/rsrc'; -$webroot = Filesystem::readablePath($webroot); - -$generator = new CeleritySpriteGenerator(); - -$sheets = array( - 'tokens' => $generator->buildTokenSheet(), - 'login' => $generator->buildLoginSheet(), -); - -list($err) = exec_manual('optipng'); -if ($err) { - $have_optipng = false; - echo phutil_console_format( - " %s %s\n%s\n", - pht('WARNING'), - pht('`%s` not found in PATH.', 'optipng'), - pht('Sprites will not be optimized! Install `%s`!', 'optipng')); -} else { - $have_optipng = true; -} - -foreach ($sheets as $name => $sheet) { - - $sheet->setBasePath($root); - - $manifest_path = $root.'/resources/sprite/manifest/'.$name.'.json'; - if (!$args->getArg('force')) { - if (Filesystem::pathExists($manifest_path)) { - $data = Filesystem::readFile($manifest_path); - $data = phutil_json_decode($data); - if (!$sheet->needsRegeneration($data)) { - continue; - } - } - } - - $sheet - ->generateCSS($webroot."/css/sprite-{$name}.css") - ->generateManifest($root."/resources/sprite/manifest/{$name}.json"); - - foreach ($sheet->getScales() as $scale) { - if ($scale == 1) { - $sheet_name = "sprite-{$name}.png"; - } else { - $sheet_name = "sprite-{$name}-X{$scale}.png"; - } - - $full_path = "{$webroot}/image/{$sheet_name}"; - $sheet->generateImage($full_path, $scale); - - if ($have_optipng) { - echo pht('Optimizing...')."\n"; - phutil_passthru('optipng -o7 -clobber %s', $full_path); - } - } -} - -echo pht('Done.')."\n"; +echo pht('This script was replaced with `%s`!.', './bin/celerity sprites'); +echo "\n"; +exit(13); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 3d8570b4f7..17560e11d1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -318,6 +318,7 @@ 'CelerityDefaultPostprocessor' => 'applications/celerity/postprocessor/CelerityDefaultPostprocessor.php', 'CelerityHighContrastPostprocessor' => 'applications/celerity/postprocessor/CelerityHighContrastPostprocessor.php', 'CelerityLargeFontPostprocessor' => 'applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php', + 'CelerityManagementGenerateSpritesWorkflow' => 'applications/celerity/management/CelerityManagementGenerateSpritesWorkflow.php', 'CelerityManagementMapWorkflow' => 'applications/celerity/management/CelerityManagementMapWorkflow.php', 'CelerityManagementSyntaxWorkflow' => 'applications/celerity/management/CelerityManagementSyntaxWorkflow.php', 'CelerityManagementWorkflow' => 'applications/celerity/management/CelerityManagementWorkflow.php', @@ -6311,6 +6312,7 @@ 'CelerityDefaultPostprocessor' => 'CelerityPostprocessor', 'CelerityHighContrastPostprocessor' => 'CelerityPostprocessor', 'CelerityLargeFontPostprocessor' => 'CelerityPostprocessor', + 'CelerityManagementGenerateSpritesWorkflow' => 'CelerityManagementWorkflow', 'CelerityManagementMapWorkflow' => 'CelerityManagementWorkflow', 'CelerityManagementSyntaxWorkflow' => 'CelerityManagementWorkflow', 'CelerityManagementWorkflow' => 'PhabricatorManagementWorkflow', diff --git a/src/applications/celerity/management/CelerityManagementGenerateSpritesWorkflow.php b/src/applications/celerity/management/CelerityManagementGenerateSpritesWorkflow.php new file mode 100644 index 0000000000..52735b00f7 --- /dev/null +++ b/src/applications/celerity/management/CelerityManagementGenerateSpritesWorkflow.php @@ -0,0 +1,108 @@ +setName('sprites') + ->setExamples('**sprites** [options]') + ->setSynopsis(pht('Rebuild CSS sprite sheets.')) + ->setArguments( + array( + array( + 'name' => 'force', + 'help' => pht('Force regeneration even no sources have changed.'), + ), + array( + 'name' => 'no-map', + 'help' => + pht( + 'Do not invoke `%s` after updating sprites', + 'celerity map'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $resources_map = CelerityPhysicalResources::getAll(); + + $console = PhutilConsole::getConsole(); + + $root = dirname(phutil_get_library_root('phorge')); + $webroot = $root.'/webroot/rsrc'; + $webroot = Filesystem::readablePath($webroot); + + $generator = new CeleritySpriteGenerator(); + + $sheets = array( + 'tokens' => $generator->buildTokenSheet(), + 'login' => $generator->buildLoginSheet(), + ); + + list($err) = exec_manual('optipng'); + if ($err) { + $have_optipng = false; + $console->writeErr( + " %s %s\n%s\n", + pht('WARNING'), + pht('`%s` not found in PATH.', 'optipng'), + pht('Sprites will not be optimized! Install `%s`!', 'optipng')); + } else { + $have_optipng = true; + } + + foreach ($sheets as $name => $sheet) { + + $sheet->setBasePath($root); + + $manifest_path = $root.'/resources/sprite/manifest/'.$name.'.json'; + if (!$args->getArg('force')) { + if (Filesystem::pathExists($manifest_path)) { + $data = Filesystem::readFile($manifest_path); + $data = phutil_json_decode($data); + if (!$sheet->needsRegeneration($data)) { + continue; + } + } + } + + $sheet + ->generateCSS($webroot."/css/sprite-{$name}.css") + ->generateManifest($root."/resources/sprite/manifest/{$name}.json"); + + foreach ($sheet->getScales() as $scale) { + if ($scale == 1) { + $sheet_name = "sprite-{$name}.png"; + } else { + $sheet_name = "sprite-{$name}-X{$scale}.png"; + } + + $full_path = "{$webroot}/image/{$sheet_name}"; + $sheet->generateImage($full_path, $scale); + + if ($have_optipng) { + $console->writeOut("%s\n", pht('Optimizing...')); + phutil_passthru('optipng -o7 -clobber %s', $full_path); + } + } + } + + $run_map = !$args->getArg('no-map'); + + if ($run_map) { + $console->writeOut( + "%s\n", + pht('Done generating sprites - updating map...')); + $map_flow = id($args->getWorkflows())['map']; + // this is very hacky, but it works because `map` has no arguments. + $map_flow->execute($args); + } else { + $console->writeOut("%s\n", pht('Done.')); + return 0; + } + + } + + +} From 71e4eee275f598a26c99d47789df9aed0ed609ae Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 5 Jun 2023 09:16:41 -0400 Subject: [PATCH 175/425] Update Figlet implementation to be PHP8 compatible Summary: As of PHP ~v8 the zip_open and associated functions have been deprecated and removed. The replacement is the ZipArchive API. This updates the figlet implementation to use this API which has been present in PHP since 5.2. Additionally in PHP 8 the use of squiggly brackets for indexing into arrays is also deprecated. This updates to remove two uses of squiggly brackets and replace with square brackets. These two deprecations would result in being unable to load differential revisions in which someone had commented using figlet remarkup. Imported from: https://secure.phabricator.com/rPd5c63c86e7e4e87d5f72b35b1bdb1e888aea49bc https://secure.phabricator.com/rPbc6f4786a2e36441d17b765fde8e8e047840bc58 Closes T15289 Test Plan: Applied these changes to an install and loaded a revision that had comments where someone utilized figlet remarkup. The revision loaded properly and the figlet comment rendered properly. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15064, T15289 Differential Revision: https://we.phorge.it/D25142 --- externals/pear-figlet/Text/Figlet.php | 23 +++++++++++++---------- src/docs/user/installation_guide.diviner | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/externals/pear-figlet/Text/Figlet.php b/externals/pear-figlet/Text/Figlet.php index 2ddefc94cb..167d3e1aa3 100644 --- a/externals/pear-figlet/Text/Figlet.php +++ b/externals/pear-figlet/Text/Figlet.php @@ -140,20 +140,23 @@ function loadFont($filename, $loadgerman = true) if (!$compressed) { /* ZIPed font */ if (fread($fp, 2) == 'PK') { - if (!function_exists('zip_open')) { - return self::raiseError('Cannot load ZIP compressed fonts since' - . ' ZIP PHP extension is not available.', - 5); - } - fclose($fp); - if (!($fp = zip_open($filename))) { - return self::raiseError('Cannot open figlet font file ' . $filename, 2); + $zip = new ZipArchive(); + + $zip_flags = 0; + if(defined('ZipArchive::RDONLY')) { + $zip_flags = ZipArchive::RDONLY; // Flag available since PHP 7.4, unnecessary before + } + + $open_result = $zip->open($filename, $zip_flags); + if ($open_result !== true) { + return self::raiseError('Cannot open figlet font file ' . + $filename . ', got error: ' . $open_result, 2); } - $name = zip_entry_name(zip_read($fp)); - zip_close($fp); + $name = $zip->getNameIndex(0); + $zip->close(); if (!($fp = fopen('zip://' . realpath($filename) . '#' . $name, 'rb'))) { return self::raiseError('Cannot open figlet font file ' . $filename, 2); diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index edbc328336..1a09a2a3d6 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -124,7 +124,7 @@ Here's a general description of what you need to install: - PHP (usually "php") - Required PHP extensions: mbstring, iconv, mysql (or mysqli), curl, pcntl (these might be something like "php-mysql" or "php5-mysqlnd") - - Optional PHP extensions: gd + - Optional PHP extensions: gd, zip If you already have LAMP setup, you've probably already got everything you need. It may also be helpful to refer to the install scripts above, even if they don't From 1028eb3ee966a9662f847301e4db45b5377321c8 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 8 Jun 2023 10:29:01 +0200 Subject: [PATCH 176/425] Fix PHP 8.1 "strlen(null)" exception setting a Diffusion repo URI when diffusion.ssh-host is null Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=diffusionCreateRepoURI, ref.master=d8d65f3f87ed, ref.diffusionCreateRepoURI=d8d65f3f87ed) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/repository/storage/PhabricatorRepositoryURI.php:755] ``` Closes T15444 Test Plan: After applying this change and setting a repo URI following the steps in T15444, the page "R10: URI 58" on `/diffusion/10/uri/view/58/` is correctly rendered without an exception. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15444 Differential Revision: https://we.phorge.it/D25273 --- .../repository/storage/PhabricatorRepositoryURI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/repository/storage/PhabricatorRepositoryURI.php b/src/applications/repository/storage/PhabricatorRepositoryURI.php index 26db694a66..774df1fd95 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryURI.php +++ b/src/applications/repository/storage/PhabricatorRepositoryURI.php @@ -752,7 +752,7 @@ public static function getURINormalizerDomainMap() { // without requiring an index rebuild. $ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host'); - if (strlen($ssh_host)) { + if (phutil_nonempty_string($ssh_host)) { $domain_map[''] = $ssh_host; } From 66dd1c35d8204df073ffe3e5644f25f76e3d8ac3 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 8 Jun 2023 10:31:08 +0200 Subject: [PATCH 177/425] Fix PHP 8.1 "strlen(null)" exception on SVN repo page with no branches Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=SvnRepoMain, ref.master=cb938d869c3f, ref.SvnRepoMain=cb938d869c3f) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/controller/DiffusionController.php:156] ``` Closes T15446 Test Plan: After applying this change, going to a newly created SVN repository at `/diffusion/1/`, the page renders, showing the repository as Inactive and also "Empty Repository - This repository does not have any commits yet." as expected. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15446 Differential Revision: https://we.phorge.it/D25278 --- src/applications/diffusion/controller/DiffusionController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index aa4f7e3f1e..618ee1a631 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -153,7 +153,7 @@ private function buildCrumbList(array $spec = array()) { if (!$spec['commit'] && !$spec['tags'] && !$spec['branches']) { $branch_name = $drequest->getBranch(); - if (strlen($branch_name)) { + if (phutil_nonempty_string($branch_name)) { $repository_name .= ' ('.$branch_name.')'; } } From b3894bc2c62f829974dddcc85b424768cef78ebb Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 8 Jun 2023 10:32:04 +0200 Subject: [PATCH 178/425] Fix PHP 8.1 "strlen(null)" exception viewing branches of non-existing Hg repo Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=HgViewBranches, ref.master=cb938d869c3f, ref.HgViewBranches=cb938d869c3f) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php:83] ``` Closes T15447 Test Plan: After applying this change, going to `/diffusion/1/branches/default/` shows an expected FilesystemException instead of the previous RuntimeException. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15447 Differential Revision: https://we.phorge.it/D25279 --- .../diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php index 0286b49ee4..e37e3ccaf6 100644 --- a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php @@ -80,7 +80,7 @@ protected function getMercurialResult(ConduitAPIRequest $request) { ->setRepository($repository); $contains = $request->getValue('contains'); - if (strlen($contains)) { + if (phutil_nonempty_string($contains)) { $query->withContainsCommit($contains); } From 44a8a1c408ed9467a74435a9c0a8f6708b935cdd Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 8 Jun 2023 14:48:32 +0200 Subject: [PATCH 179/425] Fix PHP 8.1 "preg_match(null)" exception on "Create Revision" page in Differential when not entering data Summary: `preg_match()` does not accept passing null as the `$subject` string parameter in PHP 8.1. Thus add a `phutil_nonempty_string()` check if the `$subject` parameter is a non-empty string. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=18554ea76ceb), phorge(head=diffAttach, ref.master=e11c5486c92b, ref.diffAttach=e11c5486c92b) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> preg_match(string, NULL, NULL) called at [/src/applications/differential/editor/DifferentialTransactionEditor.php:221] ``` Closes T15431 Test Plan: After applying this change on top of D25262, going to `/differential/diff/create/`, creating a diff, and entering nothing on the "Create Revision" page on `/differential/revision/attach/1/to/` correctly shows the errors "You must provide a test plan. Describe the actions you performed to verify the behavior of this change. Revisions must have a title." in web browser instead of an exception. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15431 Differential Revision: https://we.phorge.it/D25263 --- .../differential/editor/DifferentialTransactionEditor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index 345fdff72a..b935b09b2d 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -218,7 +218,7 @@ protected function expandTransaction( // No "$", to allow for branches like T123_demo. $match = null; - if (preg_match('/^T(\d+)/i', $branch, $match)) { + if ($branch !== null && preg_match('/^T(\d+)/i', $branch, $match)) { $task_id = $match[1]; $tasks = id(new ManiphestTaskQuery()) ->setViewer($this->getActor()) From 25c4f6224d63c2016e1f71fa7ecf4f640ba436cb Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 8 Jun 2023 14:49:07 +0200 Subject: [PATCH 180/425] Fix some PHP 8.1 "strlen(null)" exceptions on Differential Revision page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=18554ea76ceb), phorge(head=diff3, ref.master=e11c5486c92b, ref.diff3=e11c5486c92b) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/differential/customfield/DifferentialBranchField.php:42] ``` Closes T15432 Test Plan: After applying these three changes (on top of D25262 and D25263), there is no `strlen()` related exception displayed on `/D1/` anymore, however there are further exceptions to be sorted out in other tasks. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15432 Differential Revision: https://we.phorge.it/D25264 --- .../differential/customfield/DifferentialBranchField.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/differential/customfield/DifferentialBranchField.php b/src/applications/differential/customfield/DifferentialBranchField.php index 2387e3cd3f..60291b6be3 100644 --- a/src/applications/differential/customfield/DifferentialBranchField.php +++ b/src/applications/differential/customfield/DifferentialBranchField.php @@ -36,8 +36,8 @@ public function renderDiffPropertyViewValue(DifferentialDiff $diff) { } private function getBranchDescription(DifferentialDiff $diff) { - $branch = $diff->getBranch(); - $bookmark = $diff->getBookmark(); + $branch = coalesce($diff->getBranch(), ''); + $bookmark = coalesce($diff->getBookmark(), ''); if (strlen($branch) && strlen($bookmark)) { return pht('%s (bookmark) on %s (branch)', $bookmark, $branch); From 9aa88ae0d7d4acb2131d028988309ff6d826529d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 8 Jun 2023 14:50:24 +0200 Subject: [PATCH 181/425] Fix PHP 8.1 "strlen(null)" exception on History page of Diffusion repo after renaming default branch Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=arcpatch-D25250, ref.master=18554ea76ceb, ref.arcpatch-D25250=fade4603a799), phorge(head=master, ref.master=e11c5486c92b) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php:25] ``` Closes T15434 Test Plan: After applying this change, after changing the name of the (previously empty) default branch of a Diffusion repository, and then going to the repository History at `/diffusion//manage/history/`, the page renders correctly in the web browser instead of showing an exception. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15434 Differential Revision: https://we.phorge.it/D25265 --- .../xaction/PhabricatorRepositoryDefaultBranchTransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php index 23eebf60c8..949e394b91 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php @@ -17,12 +17,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (!strlen($new)) { + if (!phutil_nonempty_string($new)) { return pht( '%s removed %s as the default branch.', $this->renderAuthor(), $this->renderOldValue()); - } else if (!strlen($old)) { + } else if (!phutil_nonempty_string($old)) { return pht( '%s set the default branch to %s.', $this->renderAuthor(), From b09471d55748cca246555f93f1703d6b93bfd294 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 8 Jun 2023 14:52:07 +0200 Subject: [PATCH 182/425] Fix PHP 8.1 "strlen(null)" exceptions creating a Diffusion Identity without entering assignee Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=diffusionCreateIdentity, ref.master=cbc0e661544a, ref.diffusionCreateIdentity=cbc0e661544a) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php:55] ``` ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=diffusionCreateIdentity, ref.master=cbc0e661544a, ref.diffusionCreateIdentity=cbc0e661544a) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php:298] ``` ``` EXCEPTION: (RuntimeException) mb_detect_encoding(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=diffusionCreateIdentity, ref.master=cbc0e661544a, ref.diffusionCreateIdentity=cbc0e661544a) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> mb_detect_encoding(NULL, array) called at [/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php:313] ``` Closes T15440 Test Plan: After applying these three changes, going to `/diffusion/identity/edit/form/default/` and selecting the "Create Identity" button will only show a AphrontQueryException unrelated to PHP 8 and covered in T15439 instead. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15440 Differential Revision: https://we.phorge.it/D25271 --- .../PhabricatorRepositoryIdentityAssignTransaction.php | 2 +- src/infrastructure/storage/lisk/PhabricatorLiskDAO.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php index e81ecbe80b..f7a127005a 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php @@ -52,7 +52,7 @@ public function validateTransactions($object, array $xactions) { foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); - if (!strlen($new)) { + if ($new === null || !strlen($new)) { continue; } diff --git a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php index 93f623338f..88ec92daba 100644 --- a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php +++ b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php @@ -290,12 +290,12 @@ protected function detectEncodingForStorage($string) { } protected function getUTF8StringFromStorage($string, $encoding) { - if ($encoding == 'utf8') { + if ($encoding == 'utf8' || !phutil_nonempty_string($string)) { return $string; } if (function_exists('mb_detect_encoding')) { - if (strlen($encoding)) { + if (phutil_nonempty_string($encoding)) { $try_encodings = array( $encoding, ); From 7af82abcb9082a8620001e72b3149b3c1030791f Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 8 Jun 2023 14:53:12 +0200 Subject: [PATCH 183/425] PHP 8.1 "strlen(null)" exception on SVN History page after changing "Import Only" Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=master, ref.master=cb938d869c3f) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php:25] ``` Closes T15445 Test Plan: After applying this change, and after setting some "Import Only" value for a new Subversion repository, the page `/diffusion/1/manage/history/` renders showing `R1` being Inactive and `user set the repository "Import Only" path to main`. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15445 Differential Revision: https://we.phorge.it/D25277 --- .../xaction/PhabricatorRepositorySVNSubpathTransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php b/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php index 10de2d9980..43ebefa015 100644 --- a/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php @@ -17,12 +17,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (!strlen($new)) { + if ($new === null || !strlen($new)) { return pht( '%s removed %s as the "Import Only" path.', $this->renderAuthor(), $this->renderOldValue()); - } else if (!strlen($old)) { + } else if ($old === null || !$old) { return pht( '%s set the repository "Import Only" path to %s.', $this->renderAuthor(), From 980293b707a0180757f7708fbea8042a5cb1dd1d Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 9 Jun 2023 14:46:54 +0200 Subject: [PATCH 184/425] Mobile: hide unuseful "Persistent Chat" checkbox Summary: On mobile devices like tablets or toasters the "Persistent Chat" floating widget is already hidden. So, the related checkbox available from the top navigation bar is just confusing on tablet and mobile devices / toasters, since that nice checkbox does nothing there. On mobile and tablet, this is the graphical change: | Before | After | |----------|-----------| |{F281239} | {F281235} | This change do not change anything for desktop devices. So, on desktop, that checkbox is obviously still visible. Closes T15240 Test Plan: - test on tablet and below: now the checkbox should be not visible - test on desktop: the checkbox should still be visible Reviewers: O1 Blessed Committers, Cigaryno, bfs, speck Reviewed By: O1 Blessed Committers, Cigaryno, bfs, speck Subscribers: avivey, bfs, dcog, chris, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15240 Differential Revision: https://we.phorge.it/D25120 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/application/conpherence/notification.css | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index ade1764cf3..84d27ab4aa 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'conpherence.pkg.css' => '0e3cf785', + 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', 'core.pkg.css' => '0cb47e9d', 'core.pkg.js' => '2eeda9e0', @@ -53,7 +53,7 @@ 'rsrc/css/application/conpherence/header-pane.css' => 'c9a3db8e', 'rsrc/css/application/conpherence/menu.css' => '67f4680d', 'rsrc/css/application/conpherence/message-pane.css' => 'd244db1e', - 'rsrc/css/application/conpherence/notification.css' => '6a3d4e58', + 'rsrc/css/application/conpherence/notification.css' => '85c48def', 'rsrc/css/application/conpherence/participant-pane.css' => '69e0058a', 'rsrc/css/application/conpherence/transaction.css' => '3a3f5e7e', 'rsrc/css/application/contentsource/content-source-view.css' => 'cdf0d579', @@ -554,7 +554,7 @@ 'conpherence-header-pane-css' => 'c9a3db8e', 'conpherence-menu-css' => '67f4680d', 'conpherence-message-pane-css' => 'd244db1e', - 'conpherence-notification-css' => '6a3d4e58', + 'conpherence-notification-css' => '85c48def', 'conpherence-participant-pane-css' => '69e0058a', 'conpherence-thread-manager' => 'aec8e38c', 'conpherence-transaction-css' => '3a3f5e7e', diff --git a/webroot/rsrc/css/application/conpherence/notification.css b/webroot/rsrc/css/application/conpherence/notification.css index 21e73047a6..fd24e775f1 100644 --- a/webroot/rsrc/css/application/conpherence/notification.css +++ b/webroot/rsrc/css/application/conpherence/notification.css @@ -84,3 +84,8 @@ font-weight: normal; color: {$greytext}; } + +/* On small devices the Persistent Chat is already hidden, and so its option */ +.device .phabricator-notification-header .persistent-option { + display: none; +} From 83edbffd521d315399892f2c665b5f174d68dc6a Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 10 Jun 2023 11:54:16 +0200 Subject: [PATCH 185/425] Config page: add lovely git-related error messages in standard error log Summary: Premise: the Config page runs git commands. Spoiler: they can fail. Before this change errors were just suppressed and ignored. After this change you get at least a log line. Also, you get a tip for a very specific well-known error affecting recent git. Probably suppressing stuff was fine in the moment git worked here. But nowadays git doesn't work so easily here, since it introduced very weird additional configurations in order for a repository to be indicated as "safe" or not. Error suppression was a problem there, because understanding the error with "future objects" is not trivial for most users. Really. After this change, these errors are beautifully mentioned in the standard log of your webserver, to the best of our communication ability. This is a cute example of a new log line: Cannot identify the version of the phorge repository because the webserver does not trust it (more info on Task https://we.phorge.it/T15282). Try this system resolution: sudo git config --system --add safe.directory /var/www/phorge Another: Cannot identify the version of the phorge repository because the webserver does not trust it (more info on Task https://we.phorge.it/T15282). Try this system resolution: sudo git config --system --add safe.directory /var/www/arcanist Incidentally, these specific errors probably afflict your Phorge/Phabricator, and now you have some useful resolution tips. You are welcome! You can also join T15282 to discuss your specific case. Closes T15243 Test Plan: - visit the `/config/` page: does it worked before? it still works now - visit the `/config/` page without `/etc/gitconfig`: you may still see "Unknown" as version - but, you finally get something in the log (instead of nothing) - visit the `/config/` page after following your log messages: now you should see the library versions! yeeh Additional tests: - manually sabotage the command "git log" replacing with "gitfooolog" and visit /config page: see the unexpected 'gitfooolog command not found' log line - manually sabotage the command "git remove" replacing with "gitremotelog" and visit /config/ page: see the unexpected 'gitremotelog command not found' log line Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, deadalnix, aklapper, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15243 Differential Revision: https://we.phorge.it/D25148 --- .../PhabricatorConfigConsoleController.php | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/applications/config/controller/PhabricatorConfigConsoleController.php b/src/applications/config/controller/PhabricatorConfigConsoleController.php index f70398bc5f..e545019666 100644 --- a/src/applications/config/controller/PhabricatorConfigConsoleController.php +++ b/src/applications/config/controller/PhabricatorConfigConsoleController.php @@ -189,9 +189,10 @@ private function loadVersions(PhabricatorUser $viewer) { foreach ($specs as $lib) { $remote_future = $remote_futures[$lib]; - list($err, $stdout) = $remote_future->resolve(); - if ($err) { - // If this fails for whatever reason, just move on. + try { + list($stdout, $err) = $remote_future->resolvex(); + } catch (CommandException $e) { + $this->logGitErrorWithPotentialTips($e, $lib); continue; } @@ -258,13 +259,14 @@ private function loadVersions(PhabricatorUser $viewer) { $results = array(); foreach ($log_futures as $lib => $future) { - list($err, $stdout) = $future->resolve(); - if (!$err) { + try { + list($stdout, $err) = $future->resolvex(); list($hash, $epoch) = explode(' ', $stdout); - } else { + } catch (CommandException $e) { $hash = null; $epoch = null; - } + $this->logGitErrorWithPotentialTips($e, $lib); + } $result = array( 'hash' => $hash, @@ -275,7 +277,7 @@ private function loadVersions(PhabricatorUser $viewer) { $upstream_future = idx($upstream_futures, $lib); if ($upstream_future) { - list($err, $stdout) = $upstream_future->resolve(); + list($stdout, $err) = $upstream_future->resolvex(); if (!$err) { $branchpoint = trim($stdout); if (strlen($branchpoint)) { @@ -340,5 +342,36 @@ private function newBinaryVersionTable() { ->appendChild($table_view); } + /** + * Help in better troubleshooting git errors. + * @param CommandException $e Exception + * @param string $lib Library name involved + */ + private function logGitErrorWithPotentialTips($e, $lib) { + + // First, detect this specific error message related to [safe] stuff. + $expected_error_msg_part = 'detected dubious ownership in repository'; + $stderr = $e->getStderr(); + if (strpos($stderr, $expected_error_msg_part) !== false) { + + // Found! Let's show a nice resolution tip. + + // Complete path of the problematic repository. + $lib_root = dirname(phutil_get_library_root($lib)); + + phlog(pht( + "Cannot identify the version of the %s repository because ". + "the webserver does not trust it (more info on Task %s).\n". + "Try this system resolution:\n". + "sudo git config --system --add safe.directory %s", + $lib, + 'https://we.phorge.it/T15282', + $lib_root)); + } else { + + // Otherwise show a generic error message + phlog($e); + } + } } From bab99707405585911ca0e07d52d7117acd08fbcc Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 10 Jun 2023 16:25:56 +0200 Subject: [PATCH 186/425] Fix PHP 8.1 "trim(null)" exception which blocks rendering Conduit's harbormaster.sendmessage page Summary: Since PHP 8.1, passing a null string to `trim()` is deprecated. Thus first check that `$content` is not null before trimming it. Also since trim() returns a string and never null, we can also simplify the non-empty check, in a more readable and efficient way, avoiding strlen() that was usually used for other "more wild" cases. ``` EXCEPTION: (RuntimeException) trim(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=18554ea76ceb), phorge(head=master, ref.master=0d81da590923) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> trim(NULL) called at [/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php:117] ``` Closes T15427 Test Plan: Applied this change, afterwards `/conduit/method/harbormaster.sendmessage/` correctly rendered in web browser. Reviewers: O1 Blessed Committers, speck, valerio.bozzolan Reviewed By: O1 Blessed Committers, speck, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15427 Differential Revision: https://we.phorge.it/D25259 --- .../markup/blockrule/PhutilRemarkupTableBlockRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php index 72e61881ce..f02b9b7422 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php @@ -114,7 +114,7 @@ private function newTable(PhutilDOMNode $table) { if ($cell->isContentNode()) { $content = $node->getContent(); - if (!strlen(trim($content))) { + if ($content === null || trim($content) === '') { continue; } From 76ef9db8a5ed652b5d74f6a85031e1292a897837 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 10 Jun 2023 18:33:58 +0200 Subject: [PATCH 187/425] Fix PHP 8.1 "strlen(null)" exception about Staging URI on Diffusion repo History page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. Passing null to strlen() is deprecated since PHP 8.1. Thus first check that the string is not null. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=master, ref.master=83edbffd521d) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php:20] ``` This is quite similar to D25277. Closes T15458 Test Plan: After applying this change, editing the Staging URI by saving its already empty value, and going to the repo History page, the page `/diffusion/1/manage/history/` renders without an exception, showing `R1` being Inactive and `user set as the staging area for this repository.` [sic!] Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15458 Differential Revision: https://we.phorge.it/D25282 --- .../xaction/PhabricatorRepositoryStagingURITransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php index 4297d5e244..ed50f65e4e 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php @@ -17,12 +17,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (!strlen($old)) { + if ($old === null || !strlen($old)) { return pht( '%s set %s as the staging area for this repository.', $this->renderAuthor(), $this->renderNewValue()); - } else if (!strlen($new)) { + } else if ($new === null || !strlen($new)) { return pht( '%s removed %s as the staging area for this repository.', $this->renderAuthor(), From b4cfe56f03b44615ac9251aed8d74bf13b085051 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 10 Jun 2023 18:40:00 +0200 Subject: [PATCH 188/425] Fix PHP 8.1 "ltrim(null)" exception which blocks rendering first Meme comment Summary: Since PHP 8.1, passing a null string to `ltrim(string $string)` is deprecated. Thus first check if After and Below text are not null before trimming. Closes T15379 Test Plan: Applied this change; afterwards several times created a new Pholio mock and added a Meme comment. I could not reproduce the problem anymore (first meme comment in a mock always rendered correctly instead of showing an exception as comment). However, I could not reliably reproduce the problem anyway. - Create a meme without any above/below text - Create a meme with just spaces as above/below text - Create a meme with just above text as "asd", or "0" or a lizard - Create a meme with just below text as "asd", or "0" or a lizard - Create a meme with both above and below texts with "asd", "0" and a lizard and more stuff (doing all 64 combinations) Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15379 Differential Revision: https://we.phorge.it/D25212 --- src/applications/macro/engine/PhabricatorMemeEngine.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/applications/macro/engine/PhabricatorMemeEngine.php b/src/applications/macro/engine/PhabricatorMemeEngine.php index afee0f9b18..e1befc20a2 100644 --- a/src/applications/macro/engine/PhabricatorMemeEngine.php +++ b/src/applications/macro/engine/PhabricatorMemeEngine.php @@ -182,7 +182,8 @@ private function newAssetData(PhabricatorFile $template) { // changes to the image. $above_text = $this->getAboveText(); $below_text = $this->getBelowText(); - if (!strlen(trim($above_text)) && !strlen(trim($below_text))) { + if (($above_text === null || !phutil_nonempty_string(trim($above_text))) && + ($below_text === null || !phutil_nonempty_string(trim($below_text)))) { return $template_data; } From c553692d452b764d2877469c5639812384634828 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 10 Jun 2023 22:58:10 +0200 Subject: [PATCH 189/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering errors on Create Blueprint page Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=e4fd31ec024e), phorge(head=D25240, ref.master=b1edfea09bad, ref.D25240=b1edfea09bad) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php:392] ``` Closes T15413 Test Plan: Applied this change on top of D25239. Afterwards, the "Create Blueprint" page on `/drydock/blueprint/edit/form/default/` after pressing the "Create Blueprint" button the page correctly renders the expected error that some fields cannot be empty. # Enter a commit message. # # Changes: # # src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php Also, try to create extra numeric fields (like in the User application) and try to send zero or 1 using Conduit `user.edit`. Reviewers: O1 Blessed Committers, valerio.bozzolan, speck Reviewed By: O1 Blessed Committers, valerio.bozzolan, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15413 Differential Revision: https://we.phorge.it/D25240 --- .../customfield/standard/PhabricatorStandardCustomField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php index 4c0bce861b..c41148a4aa 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -389,7 +389,7 @@ protected function isValueEmpty($value) { if (is_array($value)) { return empty($value); } - return !strlen($value); + return $value === null || !strlen($value); } public function getApplicationTransactionTitle( From 7b57ba2b982f9d4376a876717d7ae76b8d880820 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 10 Jun 2023 22:59:07 +0200 Subject: [PATCH 190/425] Fix PHP 8.1 "strlen(null)" exception initializing Diffusion repository without path access permissions Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Closes T15459 Test Plan: After applying this change, going to `/diffusion/17/manage/`, the actual error (`cannot mkdir /var/repo/17: Permission denied`) is propagated and displayed in the "Working Copy Status" section, instead of being hidden by a `strlen()` exception. See T15459 for details. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15459 Differential Revision: https://we.phorge.it/D25283 --- src/applications/repository/storage/PhabricatorRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index b05be618c0..eb57b31b57 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -1160,7 +1160,7 @@ public function getRemoteProtocol() { */ public function getRemoteURIObject() { $raw_uri = $this->getDetail('remote-uri'); - if (!strlen($raw_uri)) { + if (!phutil_nonempty_string($raw_uri)) { return new PhutilURI(''); } From 108cbcd09bd39fda0ad24a2ee25afe5aac230e08 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 11 Jun 2023 16:33:20 +0200 Subject: [PATCH 191/425] Fix PHP 8.1 "base64_decode(null)" exception calling Conduit's file.upload with no data passed Summary: Since PHP 8.1, `base64_decode()` does not accept passing null as a parameter. Thus first check that `data !== null` before calling `decodeBase64($data)` (which then calls PHP's `base64_decode()`), and throw an exception if it is. ``` EXCEPTION: (RuntimeException) base64_decode(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=18554ea76ceb), phorge(head=conduitDashboardPanelEdit, ref.master=0d81da590923, ref.conduitDashboardPanelEdit=ab4391b30465) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> base64_decode(NULL, boolean) called at [/src/applications/files/conduit/FileConduitAPIMethod.php:84] ``` Closes T15426 Test Plan: Applied this change; afterwards `/api/file.upload` under "Method Result", "error_info" shows "Unable to decode base64 data!" instead of "base64_decode(): Passing null to parameter #1 ($string) of type string is deprecated" which is more descriptive. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15426 Differential Revision: https://we.phorge.it/D25258 --- src/applications/files/conduit/FileUploadConduitAPIMethod.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/applications/files/conduit/FileUploadConduitAPIMethod.php b/src/applications/files/conduit/FileUploadConduitAPIMethod.php index 8b3b9faaaa..cafe842596 100644 --- a/src/applications/files/conduit/FileUploadConduitAPIMethod.php +++ b/src/applications/files/conduit/FileUploadConduitAPIMethod.php @@ -31,8 +31,10 @@ protected function execute(ConduitAPIRequest $request) { $view_policy = $request->getValue('viewPolicy'); $data = $request->getValue('data_base64'); + if (!phutil_nonempty_string($data)) { + throw new Exception(pht('Field "data_base64" must be non-empty.')); + } $data = $this->decodeBase64($data); - $params = array( 'authorPHID' => $viewer->getPHID(), 'canCDN' => $can_cdn, From 9b99123ff93120f78d2eaafe6f830fcec5a4e7ef Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 12 Jun 2023 02:33:37 +0200 Subject: [PATCH 192/425] Fix PHP 8.1 "strlen(null)" exceptions trying to browse Diffusion repository history Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=diffusionBrowseCode, ref.master=108cbcd09bd3, ref.diffusionBrowseCode=015ffef14b0c) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php:50] ``` ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=diffusionBrowseCode, ref.master=108cbcd09bd3, ref.diffusionBrowseCode=015ffef14b0c) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php:50] ``` Closes T15463 Test Plan: After applying these two changes, get next expected exceptions about `RuntimeException: file_exists(): Passing null to parameter #1 ($filename) of type string is deprecated` and `CommandException: Command failed with error #128! fatal: detected dubious ownership in repository at '/var/repo/1'`, but no RuntimeException about `strlen()` anymore. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15463 Differential Revision: https://we.phorge.it/D25287 --- .../conduit/DiffusionHistoryQueryConduitAPIMethod.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php index 5eb20d2f7e..ba0ee3bd0b 100644 --- a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php @@ -47,14 +47,14 @@ protected function getGitResult(ConduitAPIRequest $request) { $against_hash = $request->getValue('against'); $path = $request->getValue('path'); - if (!strlen($path)) { + if (!phutil_nonempty_string($path)) { $path = null; } $offset = $request->getValue('offset'); $limit = $request->getValue('limit'); - if (strlen($against_hash)) { + if (phutil_nonempty_string($against_hash)) { $commit_range = "{$against_hash}..{$commit_hash}"; } else { $commit_range = $commit_hash; From 331b9cca42600e6e758e6c3c6d9c8456ab606913 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 12 Jun 2023 03:36:56 +0200 Subject: [PATCH 193/425] Fix PHP 8.1 "strlen(null)" exception rendering a commit in Diffusion when bugtraq.url = null Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=diffusionBrowseCommit, ref.master=9b99123ff931, ref.diffusionBrowseCommit=9b99123ff931) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/controller/DiffusionCommitController.php:925] ``` Closes T15464 Test Plan: After applying this change, selecting the last commit on http://phorge.localhost/diffusion/query/all/ correctly renders the commit page (e.g. `/R1:1234567890abcdef1234567890abcdef12345678`) Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15464 Differential Revision: https://we.phorge.it/D25288 --- .../diffusion/controller/DiffusionCommitController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 7b893b1e52..ef469281ef 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -922,7 +922,7 @@ private function renderAuditStatusView( private function linkBugtraq($corpus) { $url = PhabricatorEnv::getEnvConfig('bugtraq.url'); - if (!strlen($url)) { + if (!phutil_nonempty_string($url)) { return $corpus; } From db19b23dce9a12d8ba68f9c179a6e4ce1a336f34 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 12 Jun 2023 03:49:19 +0200 Subject: [PATCH 194/425] Fix PHP 8.1 "strlen(null)" exceptions on History page of Diffusion repo after changing text encoding Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=master, ref.master=108cbcd09bd3) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php:20] ``` Closes T15460 Test Plan: After applying these two changes, changing the text encoding of a Diffusion repository to a valid encoding and going to `/diffusion/1/manage/history/`, page renders correctly and shows no exception. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15460 Differential Revision: https://we.phorge.it/D25284 --- .../xaction/PhabricatorRepositoryEncodingTransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php index ef129da832..203b98abec 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php @@ -17,12 +17,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (strlen($old) && !strlen($new)) { + if ($old !== null && $new === null) { return pht( '%s removed the %s encoding configured for this repository.', $this->renderAuthor(), $this->renderOldValue()); - } else if (strlen($new) && !strlen($old)) { + } else if ($new !== null && $old === null) { return pht( '%s set the encoding for this repository to %s.', $this->renderAuthor(), From 32b2d3871f722123d6c6eac604786048e7a315bb Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 12 Jun 2023 03:49:51 +0200 Subject: [PATCH 195/425] Fix PHP 8.1 "strlen(null)" exceptions trying to browse Diffusion repository code Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=diffusionBrowseCode, ref.master=108cbcd09bd3, ref.diffusionBrowseCode=108cbcd09bd3) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/controller/DiffusionBrowseController.php:25] ``` ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=diffusionBrowseCode, ref.master=108cbcd09bd3, ref.diffusionBrowseCode=108cbcd09bd3) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php:40] ``` Closes T15462 Test Plan: After applying these two changes, get next expected exceptions about `RuntimeException: file_exists(): Passing null to parameter #1 ($filename) of type string is deprecated` and `CommandException: Command failed with error #128! fatal: detected dubious ownership in repository at '/var/repo/1'`, but no RuntimeException about `strlen()` anymore. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15462 Differential Revision: https://we.phorge.it/D25286 --- .../diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php | 2 +- .../diffusion/controller/DiffusionBrowseController.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php index 86ec7b7466..80c3f7b27d 100644 --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -37,7 +37,7 @@ protected function getGitResult(ConduitAPIRequest $request) { $repository = $drequest->getRepository(); $path = $request->getValue('path'); - if (!strlen($path) || $path === '/') { + if (!phutil_nonempty_string($path) || $path === '/') { $path = null; } diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index 2b81ab89cc..b81c42db8c 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -22,7 +22,7 @@ public function handleRequest(AphrontRequest $request) { // list. $grep = $request->getStr('grep'); - if (strlen($grep)) { + if (phutil_nonempty_string($grep)) { return $this->browseSearch(); } From efbb809d420f9328c3fd451404d5b9881fa73178 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 12 Jun 2023 11:20:52 +0200 Subject: [PATCH 196/425] Fix PHP 8.1 "strlen(null)" exception which blocks rendering commit page in Diffusion Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=diffusionCallsignHistory, ref.master=9b99123ff931, ref.diffusionCallsignHistory=d5fb6702a49a) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/controller/DiffusionCommitController.php:31] ``` Closes T15466 Test Plan: After applying this change, going to an existing commit URL like `/diffusion/XYZ/commit/999a2c7d1ff8` shows the exception covered by T15464 instead, as expected. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15466 Differential Revision: https://we.phorge.it/D25290 --- .../diffusion/controller/DiffusionCommitController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index ef469281ef..ce3ef41c9e 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -27,8 +27,9 @@ public function handleRequest(AphrontRequest $request) { // If this page is being accessed via "/source/xyz/commit/...", redirect // to the canonical URI. - $has_callsign = strlen($request->getURIData('repositoryCallsign')); - $has_id = strlen($request->getURIData('repositoryID')); + $has_callsign = + phutil_nonempty_string($request->getURIData('repositoryCallsign')); + $has_id = phutil_nonempty_string($request->getURIData('repositoryID')); if (!$has_callsign && !$has_id) { $canonical_uri = $repository->getCommitURI($commit_identifier); return id(new AphrontRedirectResponse()) From 1060736e895b7761fbcc1653635f82bbfd3ca94e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 12 Jun 2023 17:28:16 +0200 Subject: [PATCH 197/425] Fix some icons not displayed on UIExamples' "Icons and Images" (PHUIIconExample) page Summary: Fix three typos in icon names Closes T15454 Test Plan: Go to `/uiexample/view/PHUIIconExample/` before and after applying the change; compare icons displayed for `fa-codiepie`, `fa-user-o`, `fa-thermometer-0` Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15454 Differential Revision: https://we.phorge.it/D25280 --- src/view/phui/PHUIIconView.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/view/phui/PHUIIconView.php b/src/view/phui/PHUIIconView.php index a5897e02d6..5a6bba71b1 100644 --- a/src/view/phui/PHUIIconView.php +++ b/src/view/phui/PHUIIconView.php @@ -780,7 +780,7 @@ public static function getIcons() { 'fa-reddit-alien', 'fa-edge', 'fa-credit-card-alt', - 'fa-codiepie:before', + 'fa-codiepie', 'fa-modx', 'fa-fort-awesome', 'fa-usb', @@ -843,7 +843,7 @@ public static function getIcons() { 'fa-address-card-o', 'fa-user-circle', 'fa-user-circle-o', - 'fa-user-o:before', + 'fa-user-o', 'fa-id-badge', 'fa-drivers-license', 'fa-id-card', @@ -861,7 +861,7 @@ public static function getIcons() { 'fa-thermometer-half', 'fa-thermometer-1', 'fa-thermometer-quarter', - 'fa-thermometer-0:', + 'fa-thermometer-0', 'fa-thermometer-empty', 'fa-shower', 'fa-bathtub', From 3f8846d80c53f678c764381378153f216be61a37 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 12 Jun 2023 21:47:30 +0200 Subject: [PATCH 198/425] Fix PHP 8.1 "strlen(null)" exception on Diffusion repository History page after setting Callsign Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=diffusionCallsignHistory, ref.master=9b99123ff931, ref.diffusionCallsignHistory=9b99123ff931) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php:28] ``` Closes T15465 Test Plan: After applying this change, `/diffusion/XYZ/manage/history/` renders correctly and shows `user set the callsign for this repository to XYZ.` Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15465 Differential Revision: https://we.phorge.it/D25289 --- .../xaction/PhabricatorRepositoryCallsignTransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php index ab13c6a571..418acf476e 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php @@ -25,12 +25,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (!strlen($old)) { + if ($old === null) { return pht( '%s set the callsign for this repository to %s.', $this->renderAuthor(), $this->renderNewValue()); - } else if (!strlen($new)) { + } else if ($new === null) { return pht( '%s removed the callsign (%s) for this repository.', $this->renderAuthor(), From 54ee51d7abd0c527a2e2d5d071f38eff71c367ea Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 12 Jun 2023 21:48:20 +0200 Subject: [PATCH 199/425] Work around exception in Herald transcript of old tasks: Call to a member function getAppliedTransactionPHIDs() on bool Summary: In some cases, older Herald transcripts which got garbage collected display an exception. For unknown reasons, calling `getObjectTranscript()` on the HeraldTranscript object `$xscript` can return a bool with the value `false`. Afterwards, calling `getAppliedTransactionPHIDs()` on that bool throws an exception. Thus as a workaround, return an empty array instead in this case, like the code already does when $xscript->getObjectTranscript()->getAppliedTransactionPHIDs() === null. Note that it remains unclear whether/when the bool may have the value `true` hence this situation is not covered by this code change (the code would still throw an exception in this situation). Closes T15343 Test Plan: 1. Change system time to one year back (or optionally, use `./phorge/bin/garbage set-policy --collector herald.transcripts --days 1`, drop steps 9 and 10, and wait a day before performing step 11) 2. As an admin, go to http://phorge.localhost/herald/create/ 3. Select "Maniphest Tasks" 4. On http://phorge.localhost/herald/create/?adapter=HeraldManiphestTaskAdapter , select "Global Rule" 5. Under "Conditions", select `any of` and set the three conditions `Description | contains | Internet Archive`, `Description | contains | archive.org`, `Description | contains | Wayback Machine`. 6. Under "Action", select `Add projects | someProject` 7. Select "Save Rule" 8. Trigger the rule by creating a new task. 9. Change system time back to today 10. On the shell, run `./phorge/bin/phd restart` 11. Visit created task and select "View Herald Transcript" 12. Transcript page displays "Old Transcript - Details of this transcript have been garbage collected." and shows no exception anymore Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15343 Differential Revision: https://we.phorge.it/D25246 --- .../herald/controller/HeraldTranscriptController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/herald/controller/HeraldTranscriptController.php b/src/applications/herald/controller/HeraldTranscriptController.php index dfc0dfc0b1..4bc569538f 100644 --- a/src/applications/herald/controller/HeraldTranscriptController.php +++ b/src/applications/herald/controller/HeraldTranscriptController.php @@ -717,8 +717,8 @@ private function newSideNavView( ->setName(pht('Field Values')) ->setIcon('fa-file-text-o'); - $xaction_phids = $this->getTranscriptTransactionPHIDs($xscript); - $has_xactions = (bool)$xaction_phids; + $has_xactions = $xscript->getObjectTranscript() + && $this->getTranscriptTransactionPHIDs($xscript); $nav->newLink('xactions') ->setName(pht('Transactions')) From 7bbd657c19c49f8d1ff1bd6382ff5f1df2d7bdcd Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 12 Jun 2023 21:50:14 +0200 Subject: [PATCH 200/425] Fix PHP 8.1 "strlen(null)" exception when Diffusion repository has no tags Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=diffusionEmptyRepoPatternSearch, ref.master=97e163187418, ref.diffusionEmptyRepoPatternSearch=97e163187418), phorge(head=master, ref.master=108cbcd09bd3) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/diffusion/controller/DiffusionTagListController.php:28] ``` Closes T15461 Test Plan: After applying this line, get the expected `CommandException: Command failed with error #128!` due to ` fatal: detected dubious ownership in repository at '/var/repo/1'` (plus a `RuntimeException: file_exists(): Passing null to parameter #1 ($filename) of type string is deprecated` as a bonus), instead of the previous RuntimeException about `strlen()`. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15461 Differential Revision: https://we.phorge.it/D25285 --- .../diffusion/controller/DiffusionTagListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/controller/DiffusionTagListController.php b/src/applications/diffusion/controller/DiffusionTagListController.php index 15b8dc93ae..ffc6a1a9ef 100644 --- a/src/applications/diffusion/controller/DiffusionTagListController.php +++ b/src/applications/diffusion/controller/DiffusionTagListController.php @@ -25,7 +25,7 @@ public function handleRequest(AphrontRequest $request) { 'offset' => $pager->getOffset(), ); - if (strlen($drequest->getSymbolicCommit())) { + if (phutil_nonempty_string($drequest->getSymbolicCommit())) { $is_commit = true; $params['commit'] = $drequest->getSymbolicCommit(); } else { From 1c098c273d06ea3cf8215245ea109f41c59593d9 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 13 Jun 2023 07:30:53 +0200 Subject: [PATCH 201/425] Fix PHP 8.1 "strlen(null)" exception calling Conduit's user.whoami Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=master, ref.master=7bbd657c19c4) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/conduit/controller/PhabricatorConduitAPIController.php:686] ``` Closes T15469 Test Plan: After applying this change, `/api/user.whoami` correctly renders the "Method Call Result" Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15469 Differential Revision: https://we.phorge.it/D25292 --- .../conduit/controller/PhabricatorConduitAPIController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index 3ae402cf56..208ee81d53 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -683,7 +683,7 @@ private function decodeConduitParams( // Otherwise, look for a single parameter called 'params' which has the // entire param dictionary JSON encoded. $params_json = $request->getStr('params'); - if (strlen($params_json)) { + if (phutil_nonempty_string($params_json)) { $params = null; try { $params = phutil_json_decode($params_json); From 7fe622e5d36619a2932c0945fa6d10fe33d5fe2a Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 15 Jun 2023 11:49:18 +0200 Subject: [PATCH 202/425] Auto-subscription: little less verbose Summary: When the user "Mrs. Kitten" adds or remove "Mrs. Kitten" as Subscriber, usually these messages were generated: Mrs. Kitten added a subscriber: Mrs. Kitten. Mrs. Kitten removed a subscriber: Mrs. Kitten. This was a bit like the Spiderman meme self-pointing Spiderman. After this change, these beautiful messages are generated instead: Mrs. Kitten subscribed. Mrs. Kitten unsubscribed. | Before | After | | {F286215} | {F286216} | Closes T15347 Test Plan: - subscribe on something - unsubscribe from something - all other cases remain as-is Reviewers: O1 Blessed Committers, bfs, speck Reviewed By: O1 Blessed Committers, bfs, speck Subscribers: bfs, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15347 Differential Revision: https://we.phorge.it/D25191 --- .../storage/PhabricatorApplicationTransaction.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 56079efaf2..c03cefeed0 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -1033,12 +1033,22 @@ public function getTitle() { count($rem), $this->renderSubscriberList($rem, 'rem')); } else if ($add) { + if ($this->isSelfSubscription()) { + return pht( + '%s subscribed.', + $this->renderHandleLink($author_phid)); + } return pht( '%s added %d subscriber(s): %s.', $this->renderHandleLink($author_phid), count($add), $this->renderSubscriberList($add, 'add')); } else if ($rem) { + if ($this->isSelfSubscription()) { + return pht( + '%s unsubscribed.', + $this->renderHandleLink($author_phid)); + } return pht( '%s removed %d subscriber(s): %s.', $this->renderHandleLink($author_phid), From 8e6bb9b6903c41751277776a0c4b2a3fa628eedb Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 28 May 2023 16:51:11 +0200 Subject: [PATCH 203/425] Correct Phorge icon on Auth Provider page Summary: Add Phorge icon to auth/login PNG files. Closes T15403 Test Plan: Go to `/auth/config/new/` in browser and look at icons. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15403 Differential Revision: https://we.phorge.it/D25255 --- resources/celerity/map.php | 10 +++++----- resources/sprite/login_1x/Phorge.png | Bin 0 -> 664 bytes resources/sprite/login_2x/Phorge.png | Bin 0 -> 996 bytes resources/sprite/manifest/login.json | 5 +++++ webroot/rsrc/css/sprite-login.css | 12 ++++++++---- webroot/rsrc/image/sprite-login-X2.png | Bin 37744 -> 31127 bytes webroot/rsrc/image/sprite-login.png | Bin 16010 -> 13862 bytes 7 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 resources/sprite/login_1x/Phorge.png create mode 100644 resources/sprite/login_2x/Phorge.png diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 84d27ab4aa..aa1d6b8014 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '0cb47e9d', + 'core.pkg.css' => '051ab1ff', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '525f9a1d', @@ -184,7 +184,7 @@ 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', 'rsrc/css/phui/workboards/phui-workcard.css' => '62056e3b', 'rsrc/css/phui/workboards/phui-workpanel.css' => 'bc06f022', - 'rsrc/css/sprite-login.css' => '35d1510c', + 'rsrc/css/sprite-login.css' => '8d16d643', 'rsrc/css/sprite-tokens.css' => 'f1896dc5', 'rsrc/css/syntax/syntax-default.css' => '055fc231', 'rsrc/externals/d3/d3.min.js' => '9d068042', @@ -342,8 +342,8 @@ 'rsrc/image/phrequent_active.png' => 'de66dc50', 'rsrc/image/phrequent_inactive.png' => '79c61baf', 'rsrc/image/resize.png' => '9cc83373', - 'rsrc/image/sprite-login-X2.png' => '269800ec', - 'rsrc/image/sprite-login.png' => 'a843f146', + 'rsrc/image/sprite-login-X2.png' => '2f61c306', + 'rsrc/image/sprite-login.png' => '7031ab39', 'rsrc/image/sprite-tokens-X2.png' => '21621dd9', 'rsrc/image/sprite-tokens.png' => 'bede2580', 'rsrc/image/texture/card-gradient.png' => 'e6892cb4', @@ -906,7 +906,7 @@ 'project-triggers-css' => 'cd9c8bb9', 'project-view-css' => '2f7caa20', 'setup-issue-css' => '5eed85b2', - 'sprite-login-css' => '35d1510c', + 'sprite-login-css' => '8d16d643', 'sprite-tokens-css' => 'f1896dc5', 'syntax-default-css' => '055fc231', 'syntax-highlighting-css' => '548567f6', diff --git a/resources/sprite/login_1x/Phorge.png b/resources/sprite/login_1x/Phorge.png new file mode 100644 index 0000000000000000000000000000000000000000..20b6c1b2159b631b8f6a45ffdc58a4f069c98bf4 GIT binary patch literal 664 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

KT^Ih^!~ZeSeNteK5;0Z zq0c&%yP?kYT`299Epb!Ohxv3Sy0PazItwSPKALtG$?T}nYKAnXLZeLoV!h4j%4MNk?HCS~Sa+^Wg)B2Xdym)|n|8qfY>mmT?6uR|dgg8`q3EkI3i5h?r zbF)A4gocLp%#KOMrK32O_rN+`)eH@{Eg`5+po?f5-inbHf*bcrz_8IzmLVmb7ncvn~ZL8ZM zf4+%|Hvc<(-c|C5v1(>89@Le-D{C>_Ch|$$5~hVoLN`~gw8vtrSIQXY#e;6`kY%e+ zI+!hxOk-eR(CCZq$Y%poFcSY_7ZC6v{5dghDB^WCBl_^pVB-Yq^2>H(c%RJ6iKXj( z2JOve?YK+_bB}GxH`;Bgk1>lYydgQj@6#T?J4F$a0tvz!eF|%Z7o!=!8Qwjm@0B;n zOpR%Iw(EW%GkV!3*Y?^!_L3lv6-@7{p#x45aj3QpV9>X-CB#C zN_twFe)b;WKlHAy^FQYd)pm!|LFIeOJ{8m2Yb_+%O}17lHe`S3YIB&qJI;>>qNpZM zKHRyQ|LjEg_|KW-aNFce?tUK`8M(rR90)}d(dF}hzTPX4j2yp*$(AAYDgKy~A!G45 zDTuTUUm6L)f4loED=E3c$vbvr0QSnp9!DyPxAY!&U*(^11WfUFD`QY~PNEzdeQhYd z84=!9wsa3Pm8^=Y&eLc8B8ck#6Jwe${)g$*_T}~{Ua!_u1hMc^4t(^^70Kuz^c(jDLGX$EyOu1m373?R z0KUDv3?&fqRQe8rFov+`lv9P?&YQ-#tfn`FeQpn&3!5yb)-H~Y(*7-IsFVzNN+$?E z>OO$A0<+a|Q!pa1IzjYf#dN1H7)9bZ!pw;JYD#x`H=z;J3bJrbK%-sJMrW9@^L?&E z>}$f!`ip(GAX~zEi(!NZOP^xvR^4uyqze@T2pP2ym}KHvC zHr$2&YTwjdF1Sd~hN|m;4QKm>_s?NWEmoDh z8s_bOi~&-%$a|R*8pNFRY>J?D2|)lY7WKR1O;$6HP%E-L-I4EaPjEzMz8n6+ zG&jj_QcE>^wj-k_t2G*RBWKuDuP?rzokLs)gEdt-JP6AynVZy~PhJ|n_@Iqu`=(Jv z@ddJGX(aa2>sGRAlVAN^7aJ|aDL?0vL&}Nv&d&|25g@5`ziMh87?W~Xgh#w z3HeGz>B$u91chaN1G?M6ll+h4A$>ANjrY0}<_fJ8jH4^dh@#XHz3>pv#Q3-?^&w6c zIXWSsZ+T(iZ({-B?e9-H4>4>#8To&O0p)9)TwHOu=kFKqO$9B0q(@y0s2_6AYfBzO zy~jE8E0BAyJ$P=TJuO_cdwG~>G(tlDIUIFUQ-NKs z&&li1rIVp9bEEKteI+I{NO+E2zN#qEfdM*>cW0Sxg&D8h&WG?}0iV=ud6`>z;St&? z0ew++Meae7QWklN5bzeK&ct`Dwh?Sp1~Tv#*{8zO*?d-VfWCi72YbQ{0&@KbuZ0ZREcxyU$fs>0duz76&)~93lx@*!kmf_b};c zkabjKfU`)z3*dNgbLkAyYt~J-jhQviOC+^}V6!B(#K1>|;JH!sUJ%{Ph zBq}7H!HCPA$2B{k!f(I*IKH3E%*;||($q#{*vlu!%ir{`=M`L5J-)f=gGwaDH1E!# z4euuTkrRJ=d?Qt?LN{ee`V;>;#We9r&v5Q^^mG(hXSW2ATp$<}cH$ZQz3r?%uZwQ+ zr&fkrPG(%3Qq2lluhVul>T@1K7WhA0;i5W8l!|%dkFe{?u@y zt#Ixzi_V!n0|;`mY(u;qswffGrE;RRKo~^=M)2N=gC^F|rXJ*YvE93E?O=#T%x^qj zXVjZbxF^*%`S-0`XxU@q@VJzN$`&yO1l>x+r5yg`jJ|o$SlaS8jue6t7x-5Y5{RcH zE-WXQq+s4Cl=7|HH%ecszV`4i-N=)B$q#JjjgfBSJY8f>ji6H@;o$|AtYABw8WV)X zqxL-}1zk z!!ME&E4wS;l0Xr1O&DDYWerhZyh_|c7*&cnwz3mY!0i^15EqfV@H&fl`3xIFF_kO) z7M4c#IdpeCLnOq-&&D=`nXg;&w|g>!V}SW1X)T~z;MsGlqk`|7@Q&O5b$@-8-FJ(0 zOU2U+^9B;2M#j%VBV}Sa+m0>cxlpnDZJcUtxFj5F3p(;t`^%d=pqYQch7U5P3?dqu zQD`$ar{CcZ9c_YF-~ZG*cx_8YS63I*(0gp8T%yk_CB4Gi^QN3(-yP_<8De!-%j|dg z2<8c|k`JVY1*?xuCIz82RyKDlGpSgMdqtT^&{(7AFkeEi=ji06QD(tn^;djcAdX=0 zNiR8kjnm?nSWZq(V6q^q5&)mmeDtBN_hZf2fv@-dah*dl64hb$o>aEb%-8o7-|YLy zZD63)jos2ZSD+w%3PPd{g>X3QOkR8)ENh1bSx=8)>k?g+i0OMlUb1Q1r$4pLyb9Y$ z_=JS^!GbVfToeX_*dXqt$kal``{T!|5-3`re^kAGqOcP;g9(JF2u&GkmJ~t*!u$wVhq&k^BGV5C zM8r3NheaE&?8A@?QbJQN-A(`flS`&tuETU}cJ)aoSV#Sw<@3_gz28Zeu4vo5%~>2l z5!bmXa`+SOL31W<&`AqYS$-$Q9@ zYvv$;(?_tB_KZA(#{yoL8)5(nKS~Op9=qjQn~#qVswJ>FzxBLbYRGd!BbB-yO=ikK zK|$%nk%?{%EHCSv;WgW@a;DSe`Mbs^B+O*}oi8IJ5pXr9`O~o41I9EnW##yO1*I&+ z+fw;=yv=f#JixWHMYN$4X<&^4L37~8+m|vhVOHU!srg-<@`z|n>DaQ+gTSR!0V-$y zTdwNqOT{o!JlImOAFln3wt2vEow{!hDx&Z1sA3?9$tWy3|G{WAGXLv-gPWTheXgjd z%S|FwTrH~8-_>Ns;sipwcQaSxZ#-Zlm!g$4H>|Lt+c`r})AQwEp5XV)*rJ zUaXUwQ|XWFVnCvf0EUaSBcOUrve=>}9Vg zL;%1zLM^%^*^KGfq@<+CMCVqw^_RQj7E7b!qa(qMz|Z#_Sd8|_MzDJ{yJJDyCh9*^ z_1#m)a{Q#sMPrWcuG4R0TVx!;GV8?6!}?E2UAvp=^;Oads>KHv3_4vPF*x8-Y77GdABYaH|H5-nNoenvf%;d@) zem)lkO8>Mv>eRU>(r-D3ZMWD|W^`bDnS34pBf!Ttlk*$-Pyq^e>vccox62VKr79ka zhoz%1vQ&T0iCK+9gr&ilS0qNmhd7DiTxT9b0}iBR9$Z~Un*_C)wPPW zQCiSR>i2s87a=Ej_VHppE=?A;zP%QnIing z+Td}SjqeRSQCUidv^0n5;Iv0_bQ=X@;<0tl;xq?01_os29f^m#r>B`CD3jZRv7w5J zicM5p@XCHs)`5R{eLd>>{CESt^?wVv%>2^QMJ-iv^{;fhktXGr-^S~x67Iw9S#$TayP}qdj0kICJycR5e((MenB!+-e6YIy{VCw4EiEOr z5`r`MJZjvq0**gUhi`=gnf>I9VcTf;4X2Lu%;e=MFX`vU4BE2$ zIuFyw!rtk4iAV89=p^FpI-C_011J`i(uswfkflDMwXcPaCLDvBo7;WXZALhK8cyo- zgUKNzG}MeIcwl0JNp5+6>ZgDP6=rR&N zd=iTA9fSY(&)oCuPwx^1$G2=uYwz=h5%*kOM{uYTB{Aac?p`e`E1U7O7sIBXed_j6)-v=u zl5uftS1NErnA6cH2kQSA3iMZ7sqj{_8$g3P^|GI&q;g5eo3?&AJZSK^cjN?n{nUlO zc+S)FO?suki63%mXJlN36>xl5o@#e+a8T^FJ{gXLS9<+KBII3<&0Kr^wBEI(0bwO=(&`uS7Hc_0GCt}mM5WWG%0ovE&C zujuSUCV}*!wY9a@%h{RUu-n(e8BA;W*VoQX@~C!` zmq>6jF(>V40bQ z$pdMVsP1_>{T&@s*YmwE_r7$76?*fZ%x(MdPcH6}u>YT&=J;pVPL4HWyoBSuJh*dV zsJ{M2f^-G1;)mBx%_xMEB;5^bYJ^#$`V+7VTW^QTT0|sT=me)&B3%ASycxd)5KFy+ zCJivosPs9HV7{SE1q~R#@XqF36YmUE`w{5vhg#zWz@(9h%iSZY7f48Z?eJ5yjC-F4 z1NV*r^fBUk_(QwofGJyUw7SUUY%v`6wK&TViv_%qLg=4>I{1Wn(OwnrtP#O?C zJZTtYtiIm_G0IS}l=Voqd0wP5Vi4Y_ePL)t0PGkjHO6l+Ge1n+kvE$8iD72;#l9FH zYzB031J-T$N7kKdxkjVE`TQr_pizj`y(B`LMnl_IyO2OxBVx{)9K!c{Ct~mX2y3?8CdE%;e>TUTj zFQ*u#cXjqx6e7;o{LnsGpi{35XtDYJuSyzS#G(1;IN z6VbNiHO~-YwL+b-K zIV6J>u$@eP1TB96Pz3o8UY83IKMoQ-4gy4Pe1OQh#GMMqp4>7piiHCeD-V`n%K$xo z>nikmME{*bK69+AH^ez|&>YM^Lcx%P{*EuEK+K=vUC1x12{|m=@x%}ktl;|gj*(h= zr)`A%X&3ys?U=b0{2&c%Al1{32-g11+KSuia^TBA|D+>?X$wqf@+!k6`6lh}YDgNT zgPapKQ1mycc;-$0xo%7xe2X0jK&*HDX?D^D7K%uI zN@-(4;4eC}CkJP1$6r8@4wz(e#zq5xjh^^ZK)f4&&8c=95_U#guX*^`);`|#9o;Xx z8G}k@X22F(jv}mBRE`6yJd#^a0z#4!7@;l?EUBd^a2XPcuIziB$n2xXLjsr5b4$vq zw4)c6SfzZaQ|XJbW+!W#+BOv=JLAw31T{9hFSHie*pS(iZ;Unj^?=N=mLbyIc+FIn znvqZd{(l*BK>lIvmAU~XDvl!cmW(-$RMXL!e`i;>;_*oF8&YOwLtkqV1a(E_&-Qr4 z3MCd2lXeoej~p)2kq%@6Kj+BPzYMi8t)6@BH<#+u8hMGeNA z{J}#5u=NPB@&I5-7r+x-2!*%4*}N!=$j+$dGH>pU_Opu0GBrwL)@K$vC<(6J=I(~! zAbviHYkJP8c24>)TS_$@T|H|GYg%j%j&W+zQ=Jkp9!4t$Z~*QoahcoYH)p)$$)_O< zI*)6xQ{0d?>E1{ler$?sFFKrTLAGae_HkCK z1}9-}G*bp8#fGY(=mH5=2#g1Q)596N$Hx$8LB(O;a2z%g-snsIq-hUwC2WQjTNv82Q z^BqFP+Ws)UM+ZnkP{{lBfQ?4?Kbw40CQ>OXk_{H&f^~s%8UHd{9ab-0kdT|^p zxe;w)VdPQ(PN2%MEN-wGdI{)5OcS2aj*_JMYDP0C_vDGimA?S7h-@x&6%4dwz@5^u z9Y;p(U^Ou4Vmi-HL&PRS0A;TC>G8$PVA!)F=t8E$kLT6kb%7%QU?c!2peQh^t~pG! zRyTA8VO{B{k1b}$W?x6jB?z&mW3+7Yx3vjoa<)?$7sY;`7|D@XS9SVl{BcE7#Id<3 z@CkBfZ^cY$BO!|Cak7#R1%;9W{*Z_splo6;hK@n58?=_8`@k!Gr*pzFgUD@I=tv;! z4Pi`;6{N=c&kzj$9)Kdwpd@I~*d4r*O(j!bjHz&k)wO0<+MtUBuu^sas4AkffyVCQ z`KsC>M)P?*?)ytQANy&fRaUqD&Eg<2vklf*(sck~Y*JCBqiO(4>y#PAPQ1f!>jT`N zS`PToz>kVvdqEVf6K(ZH`+GJ(T2Es zY$Va@(_%d;K;X*{#aslbHBpYDr`eWJ`XGIF{XPou?d+e*wC3GR_;=i(!{YL_Cz(Y? zf)!{W3_qj=Aa;6Rm>!L}NGNDd;AfaR_IuntMP2|4kr~&8F;R+pxq^Qr!2DSdFz(Az z2}n3~3wIqMA0fmH%i+tdop`GlngF4977X-=Q1)9UJLXULDHXDF3V|BM2Na7p$LIR2 zE2LRnU08_G$s!`wb?iqc{ZwU!i-Jw=BnFda_?Ax=I zP3;ye$V0L%6ON1K0Y$_m#WL#I!<{xbVA1}W{rKjsWbBSk0y9C6&0HkMU+Tvqi8x;C zTj0|0vx*uUP2FV-#6yjk{Q!xZ=-EBuNz=oJ&H7i4U`xaEjK1Ca6_TYxu}zCV$3?TC2ijjTObfn5*;8W6=Xcr&70}@=w_a8bGId_LMEI*D<>j>MU9u3 z0&PtF0vR{XB%Pq}!Vbx&1YVPB2w~M5F5jJhY~@BKSf>W=a1V$!5}RZqaPTO5agK7hxph*7-_`+WW4>oUJwm6|c(MjMQ$S?AP39*&Xq4 z7T)He8_5E6`2Ag@r~fH8)$~3=!TXA@mz0A&j9T!#F6#gvjgZy3xOUL>Zp^f+`$ItS23(=7~KuG`Uu}C zk)RrtiF&Oy3;&rL{f*D5&R;yeEsCeZ78hj@5>g&7SPasg7HVgvN#De{XFwscTGS`2 zFaWA3_}E5;IgmUB{#P?oAn#7zioqF*ltL0uyG;YwfB4~wjte~NmtF@o`Uux+WmkEu z5>ggv){5IKnm-%ff%Jrs3g>ni$xkai2G@r@aasdz%6eEG$J3ic+i9>(O&X9MaGR~{LytR0k_8Nd2PLkZYSyUm43~_-1 zFxLJ76odxw?2OFP2{=EfM*dtSewRjRtSOfcRVcBZm6QBxR3EFn|56^tkoo}oG)eY@ zv&E(gLiJ3IqVEK&12z1(b9Wc4BHW%=9txGGGVs+%^#1>oV_D)Pjg|f3A z7O{Un!l{oq6%=zn$9#M}eh6XJmuUI=Z!Qmx1&&3s7(?>dcA`QxD6`4$) z;613kCuL;f;DTs9>izXElvK3^w=kGAkl8C?CS<|RayYhO-Qg@V*J{Z4E$`C}uN zgDp~#*b%uhr0cu_cYb(|Ghj!dV?G)Bb|%JYMi zq0t4T|DHJh-Dwr5w)on3O}z2tD}e+vh}9$kjYwYb#YMv7xnM%-5oNy7h#pUeun#8V zkg&Cz-Zh?{WGgQfJVf$Jh?)hptJ}vEFH=!HFEMv~VW&W7;q-rkS#&;FQ)yrhbfRjj zX6A0eW~Qouk1hUT?QO#2Yg!K>&W*1Ehc`(gHh6Wv0zt*lZc_p%VlJtO_AE3f9vI{n zWZ7l={43ogi7GBc}QsU9>S~y?ZZXQD7L7tVM$fnw^^|guU7(CQU_vZpT z*C)!PO94?0saBeA!8M~dQ10$`<@l>S6DNgsOTAU(@?r{`XcToRb%ij=MJECXWI)XO zYHBQ{DQJ8!k4HlQ5A(>&I5D{x#fuu{=im|gMvbGay)hBZZRbm&tSteFM}@9JWD8h3 zP1wx&fK&?hFCa)@*-h>=-+`~^MFk?SD$7pvjUOrox-bR>dil4yxAIJ=^S4n%%knMX zWto*%2UH20^hK7!K*Qf`TD-KLxBH_W&q>GD0zh>-=2UPyNF7xe_rqas-N2h&Vet!D zJqR;tCJ}1%%L}?brCRtL12>gq$i{6yCu1O*QpF2EDfy@X!2@shnK5WC9P>V`7+JWd z&zPJr0e2?~K%`^onaCKVMxy&4y%N?EQh7xyCA^Y}Q#pttW&K@p8i(#BP9U{EbGI#tv7abtRn2p+I6|n52H8X&Z?%TmVpG z3o(_H#Tz2ZdgUW$<$~+E54O}129@nBJF-(V{@jFhg$Ja5E39$q7DUnU?fh)@+PrlH zGBeex9=8@PIv3+RWE{J0x|9$q3Jm^_+gF0IRA1w!sYTm9rHOqdtyENY#8Y`skD(_~ zrB+{C(K@&!`FdZ-y4zgyuL&!o!=7s%BvsJd3AHAM|I^r6MYR!VYdE;OyBC+@8l2+p zUfkV^JHege?(R^W7K*#OyA^l1Ip^`d+?$uIH7gIvOt#Fw_x}xxSzBi0d%_IcjTc(r z@#Zz__9k%1DNo$I{c&xnexai3H~KD2r9oprxnuWh-el`u`DLl?Vfeh>U=pAOeVZ9t zs(-k(lan~F!{Tr|v;ResP{iy`g#(bZCub#t-BgZHyI$0;L zpkV|y2z`Co3tRTPE{M0lCYH^_RLTrjOL5w?PqtK^VpYT)#Hu`h}c1_tm|&m z-y&^nHV`&^+{lkEkjwH>1CNT){b{bKCTBR-?T@W!PFDk%>#t;=7ErTgTu>k@)=6f8 zm#Zz4&VPx&m6t;LNu^y~eLu+1A&N_(2hFJU0o|QM{ca37%^=PLjExxpWf9ocHFiv{ zOL)(&o0J@kn?fvk%gXR)SOJl(a#U0eak40qfcoSbYd^&>Hh22fZF(6)C#cl*Jx1EK z1u}3(k_#V$#~wpl!SI;4-CD)#$C6&qK=+4^tdDhsHkysYb2rP^_Ng?c^;cg?a-DK8$z9LR6;t6l1X^*B{v~Jz!5!T_f^sxJ4KKXqHN-ah0z(xKX86csH4XC zA&32ysy>DtT-%$rBuI}GsUA(h-!|7={T9OoqRQ$`q&viljSyttPe>SN^Q*NKP&XLe>^Lu*u7&LU4*ALBrrU?5fp4j;NewUwXG%J4rr#aR6QUj5_du~sh9yA5>%&J7icyVwL zj-`BYrk>M_?jQb1K)9$=%gxDu@2b#MwHYD}BL$#{F2i&ZcIw9;`!-&4TKKS=gvZ>E z$^Udw7jfs+xoXuAqMb=&?5 z&RZ;xTsLBoFSGz9i*kB3d6eyjUQS}n3_wLdCgoVT(o!}vUg(*i_qDH_9ZsPF7@?2q z7y8A+hlRy1(qb1#2lFVt04S0TRQc*38l;i~A0wM$d?%=_8l3@anf1v zeES&DUbnooed^&kbE=!{FtFbJ_nkRk4!mc(`s*dy$j!gp5N~FlTG$}sy-EFn0yk;e zj%4)%`meIw@Etj&cT$^UHR>AcuDy{p{8CITR?S%_N=+^#n{Epc3}j3ixb{+;e&5&m z8H}q=3d6&UGr=e$PCAP^Hmrf@p!-Fy+9Ta@! zr^Vu?v|Za$Cc6?=sY}6ib7X-latZn|$57AAQB(~bzbiHO_s<@+8>Db-2N%IkRC)HT zjSwg_{CY@g=T&@bXwmUAe*yAzBGy|cxP@cChS6nOKzy!_b48{&R`Kd%x}N-MXx6@j zN2%BS`O)2+=YYzAYpaEAmRTSWDv|-d)^;TxSvKLNUJybn&drW#mz3*3V33NxJ3%FX zeoquTiaQj3Fs6^4LCt-xj~$X{;yoBzq3%1>a`ITDc}hgKl5b$?Dt3`dD-7hqbFs-M z;*0Q_(G1jkfEYI|INu2?Z?U&h1iY$Cy4$c3Mva1%G&VL$@mEu@fb0H){dZbxFL=|; zzp%8sS$te50X%xC#32Ikpoayn0S_*^Khh za==Q2wN+`t#=y4C2Spc32Xc$S3q#oqERKDYwJGt(`+&m{& zeIl2%xh-vA8^sF5aGnbHIN&h)EE)Np|4PBMV%1@I!pC` zeuU@fF@THeqgw(TAz}ti6Uw46*fe5FLf1&@QBznYOF$HnF#SivOKM=dNoy>!Msk6W zQtin~3tGRODzvOz7A%U?pgw5;#E!iq$JQ{37Eu=~KM-+tMcGnN{)&OsYRn;pQ-_GT z(I{m|a#6!cr5}tT6~lv&!sNo|bH6BMdA?kIizAgF+!Y5@0y&KOA4%%^yEo^F%fDP| zlX)?Hmi#Eq2skiF!>GU_swi`%R)JTU(Mc;T!40zd_9lz%pNUok-^?8#pli?u2cscb z6i6AU!p+khpkmH>LD^R>mS~w0l3V+Xt}tqWWS+6w+{HR0J)2B%`lnf|>CZ2n?4x8P4<;IL%E%L*Z z#4LXhjQjL>Vc?i8p;uN2p7p@Ao~vW_jCcsFGaP59lpSU3Sbd6X4ynmUFPx=u7XO+i zN;m!mUlJKnsz0PP9|fcGYmwJ})|BU59DCZ#x)=^mO=dK~*RaOoF!*+B^R7c1bB*Jf z=uwnsINVJUZuZoQsGGGTg)^vCv^&velE-gQ0I`sIs?$YvSz8m~VXTSr!%l0{!upAD zPiHd!y++RO_=#o%1V?omTkrAxWig9C6Vg+)`G!$ghR=(s=7;HYDGJ5!)bFfm!t!Hj z=S`&`Qo(xU_vZR-c<&zh67HH9qE$;o^M3Y9NN~ek_=_-uq&O)^<(s%ay?1#`)&O|v z!!tnxrrjEo27e=B_8*osE0@D9caK5bIptS1ODVVF;UaPB%cghJd(2oG4H{n=Q^oisE0a=WWO6NkdBficv8)Aj|4x=&?%_Q!l|2ECihGR8!>`oVc za*q`wHUvKqGwF_e;d9WGLwa;nJ`mH$F*9$Msu<6=P967b)Cge*S!6e;E=j`HNHC;L zVC4p!i-64;`asRuTj;K-xa}eBt%DtWGrDbcZ1C=f!f4YX&Uh$G>U`r49-vt zy!Z z$lhhj1tua0Vg+%P5a&5o_Qcv!CzbCps?%bsS(M)}+eP7p1Z4V*S?(XYC)KwoCFfL+Z+~n{ru?011ovGv3D^-OqeKVGM$3NRs`$8>9dT%o9Gu#wq)%Q z&U=g(n@MJ7YT?iZW5V7HD!z~)=)Xh1W~32jM3J>3%>pMLfU*yZ60|OlQj0P-!VjO` z(6oWdsVr`)4`i2H{^(NKCpHEVrPjwrdJ%`TgX5VBjbUJ#p*whTcuajZ-4V27W#A1L z^Om7f^KNxwweVw3v5-JsSuHsCey^YIo#Sp?#G9rpPj!Osz!D6T1H|_ z3_SEenLyjA69`!cdtAzzX66$AI~Xll!;A?YvXi8Us~nqCPH?BZQH14TK7iM)*f{>5Clas;#NOLpV(eS7S#RqU7*Qn*70e zmJS{t%P;-Xo&N=7M#(SEF6fQA7r$|I5xMf%)h-r-8tHE+HiE%n`1BF$OAH2g`%xmH zcaJn`?d#`<4a3e>c!CV-9}%J2WMzr|Yu2aYk+xRiy`-O-1mO)-ZS+U+nF@zKnY^O=p9syIAC z3ZMwfoaoXO?h@L(w)s1NOMz|>;7Q5iWj+q$ZL3mHn3X{NyHf~WLShJiS1e{~)$$;_ zMak0hG%J~kq?lmRg#sNTwP(-eb+jV!;SQIh$7>v|9*`Qs9h??I3XvmQ7xOIZf);{4 zWBSqs@S`SzjT>3b+JC$3X+*fXta9?_Ib3e9#;*bVxF>I{PUO{ibFwHT2i4{f#MfEI z(KPTh7aXtkWC_2!*c@9L(b&_!Fp!bDQuX}8|7v?gO8AYPIEE$I1ZM)KB=&S%*$lk4 zXtwF{9+OY=7ujWURd45J6DUZbS8(mM>+@Qy$vR{e`%PxcUl4V@$>V80&-t&g1wT}v z{=dhhNM5;Jy^e-sqCh1b9J<4^#U#Woc8;Up+2!4Cp%ZN6qZj~;8C~Voj=>l+RkY9q zv9gmr$mP8CEVdIrV5qkH4^~p)XT^DI``4vjF0a{^m}}2~@oWIVrT^1=2q!OK4uzJ) zV~7Z=bhppTv+H?JzpJDjvMShW%+ly9Th!BwQRi`u6*@XiWFW|ji|!qY4NGS!MAWUk zlWdblVA4d)LqC!sboIX5kB3ZYt+wT`Mt;}uUUb#XcU!}=YbTKCAwDC!U^A#=;h8Fwl|^id-Vr3HuY(ivy#gt)fFu2b1_A%BcX>c^5kKOY+x5Bc>Uv0!A+T z$txFzVDK?o-sj_DsZ(7-j~W+<*#z}il&zx% zO8>OI&x7z-MNy59rw0Enybq|5zeBAcvfs6sznPCDVhxADoU|ed|F3MAA{lP;c%bK= zI1nVy)gl>c7w>Y7gpiU2`3i=@W?aAyssyV%Z?zs)9Y3I*^o~_d;14qC8m7yNp{NbbtI4 zftwPu^0C5G06cQ7BsN@y*#5r5!E_B{=ff5}Qvmn-x;%xh>w3AqZI4y@|%qFrvjM) zJk(M-KDmpftur4g{S$G#^k!_tm+&?yP#v_C|50aCGtzX5_^M$yoAgQ+fO{7)mXbaK$JOIvcl z5QY&5t>5vbtCE|A_TbHD$l%&DsqC5y6}U9XYpU~Y<=gv#S`qSw{VYm@{g0CDd)26A zP83x&FU4)NZDP2xg7sV1^^ZN?r3_+AL@P{3A(h!YU&1SQSkFsWec}Z9`t72d&FWuI zCJ2w4pj0R9lLleFx8)55Xo@gz=PRmMq6be1l-)%>m3?i^N|g8&JI7sY?MY ze-(c;$W-#5D=VP~P;!CL(SP(lviR@!X{tn9%~yTh&P@&UDTQvxoGz@33&r*4vb?bT zJOyu4vTDDl?a*li#jDyToT@2J@@#val`2O5g(#&g~hS5LbV8+DIU zF@TOQDpU^{3&stGBJg+Qpp5DI_1Gk9<_9Gi$4lO^hF>E)0J)oWfa6^s-qrC!0m0uk zpwDQ6qrjIqdzp*zl+f35(Qp6MpyjcXSl0_G%k^V+eGZG~)EqM!amr;yq%7qchCUhO z8N#pp8;KUgzqt(v=xKLJ#7|@Zf%Hg1m$M5n_=Atp+p+^#x!~VcHn!8Q^|0{VF(gpd ztFDk`DhdfF!CQ+}xh91c)kG2XgNLn~MOl-Lc(tt^|xkiblRcY{bQOm#$YTrofe zwW;fW#Y~nJ-;h_<7+J!YOkipSd6N+1tcz3etPbG%Xy>M>;9I= z&~T{=D0!+^+Iv)*%lH8doI^;&qK_9KFwwu@FZHjgAe&zI+m&Klq3x&QoanI~=Q zpQDIJef-p(gJ9N6&%Z{m^6k&DIiA;Te;{=M!1TIQ4~Qp$wFA#lxL1#stb8)e%dZC} zl+p060WPbTK>`NQGyGU^WrJ?)gEEiQfRi#huB4D5dm&=EI>`bG7$aS~m+JBFdu8S} zVbPPn;Cv<4g3hKV9PSz+ z6yDpa2V%7UX{v$lUT);cK2*aymJ)yxA|YZ{V2Q3%{+6+U?nLD1bTy+Y*$G)#VSz}w znx=`C6h2-y+7r`h9ZRXm_ZkjxaO;0ATtB6GaSNQ)~Kh< z8W~|JTWfT>+RS3(~J@p zW4g$2p!xgg#oKys!@UB8lX~0GkG=zjHoYie|8r+Rd=X~o(@RTtyN~AgBcEaXY=KP_+Pf?Wfg;7Y677B9)yEz}u0s`Y|ywT!3wTwJVczN5JMR~Pa0kU~0Q zQe+{8Q+vf}{VUOKAKB(BiD*MX0EIAO1qOv%$m=6l#naUq>E(wQeD2;(Y@UxDYOatO z>;oXjTo_6Cj137+kRLC={wSgs3kIx(3PSLYUaowLi{o9|Ohhm|ipP&+%+=P{dFZ?O zcN5{~W8yT&1QtI^{(K2f2&MevBvwjd^A$4m(#07%d6?%<8p^xJGJEe8?6Vp6`&Q%2 zpt$8uMm)f`ukqUHpVg91w#C|yy?|)AES<-3XUIu+H{%`CGmx;q9red~Mnk*Fg_ke= z3d~YBkiHUb3$K!V)f|?*aVOOeUytY+Wwsn&iV}RGlh=ohRs=1LK0sS1hytHPcG-W) zw4fwf1z(6gT$RBsL|OmMy9d(sqlfM~$1B0p9!C5QB~PGIJx9+SyNp}*Fg5S!W=>#< zWzN`H9_bCT|0c!FplWI!|`=~Ky9Sb<$+FTWe_jVtejg^c|L+=Qc{KW6r zvUMHTW`RMwlJ3$*51${!br@hN{B+I zph1L*ir`D2(;Mvu@=`(;N&>NAWigm<(CJH35^f&|kG3=ZAhZ2J-X43Q72r6^?&fi| zK<%w=c`)=rqr(tmTe+OeXvZ)?=oVkEm({7oX}4tgG-vxOG^bn2jHvJUb|%H(mZ9P~ z7LZv2e4CC@yVY{75Pe7+W9%hyv`$!TPK9~Wu7H-Hm;)5zzm-}lCAcv3pD=T{V{yrO zPEzz$B*0KQO%)2W;|_*217|T~vrhubB?;c+AzJLCVM@-9WbLDFa-B$WXe+AHu(pwh z!xMEefU1tnASc&x0?m0N3#$<=^Gb&mMI(4cIVg;F%eVM(`l8-bWxWq;X?w25{JZy! za(6s5wW^!o^3Hv=3{o9^AJjQr>WRjIahAZQ-j%}(L2%FiSM%IFW8rb16_ zNaW###uoE4F@en8p1b1Xa+&_<2!ow|^HFVyX)WDu8@ySJlpu>p57;@-ey0;c% z3MtHNS)axkzZ@^V9louW9KL0ibp8gdwf0;J^A^$weY!IbCu;*6f5QI4)Mn4{Df};l$`-?Bzhv@79kYVHgZQlMda(9AxeM-9;a57v_duq1 z)n!TXMX<_s2crj>%7(j3hW>aFwRLxnLCqE?4=I)~0X75cKTgF2^4aJFEi{Tq)ZTwy z?22ZqM2k&8J}W{>XgZ!v)W{2*(zqk6obN$P49R|`c{!m1A?fDNmDhREy{l+1YTD83 z0|P;O14Gs<>6%`h8 z`uX{x@d*f&Y3u50rl+UF^VqM>Lk$iOmv8+3T{t^3Lcg%Kw#FkUsLw-7Yl-3SN{4MT zDhqbOADy0Nn%LQ`w|xHtdshh8l10B8oYPrEQ`}o*u z?tnQ zmwp=g(+7t~N6Q(?%8sUZCXNgbE8qV6Ck@vwx|PM_cu9l%6UKj!VV03b`Gzca^JJT(m<5F>rrtVXDZpf zdC?xdCq8sP8T7Re4%I6&GlmXsDKu-hP5{EuLQr%xyBg{#S`?clutOio2(SGcQPbCF zSwac~p!2)zOZ)2SWyyNZGt<*QFHcQf!M9twk-wd!ep{^^`^l@--V2v{Z1#rQq=5x)lx5DiaKS(5B96(K| zFt@l^>gMJ~7l<)XRBP1h5Yh``-(a_w@wDwQt0sN@!csDTkMV42G#o6|f!X&Z0U~Wg zto&z+-)>J|BY&_`p6}`D=}R9t6kfr+gM6@ePn(^Ww_Fvz+xPj#Ku>SYI}jZJ(lPcj zQc`lvZ)w5#Hdl&Fu?WH=smm}#?{-E=g)dxC6;W0keMNj|OQR|~pehLr^8i6`8u>yV z%z^E|;0-ohTwDdQ0w)MRAP>*uhU?KJGblP*&*f~f24FSq~_^S=?L zE0pZdf||c+)G0w`K}8WoBEGQe0yKdjYsu&BIoiCa=xFzTl3+eDV$`y^!NHRG{{DjC zt~xFb4xj3}I$?nL#lHSVGOdz?v9yxXQ6FDmWMrhwQXGi@sV+EU@Cyk1O!kIUQ=8+T zH3WNsMeDua-rs4Mj=Bx1r9m&o%A?Ck8Iq1gpZHhjTZh|y)m#>VDD zG!Vzs(Gjhw^!51VA7WteBP=Ye_&$GLyK#lY-|=3V>e^oeFW$)aB|*c}(@yBXh?W+2 zKQ%QhT}d_P39wH{sZ+AJ=Iw{XK3R_`B)JGOeYVKi9>PloRW2=c-2xS}RA`Yo;_=}` zmMsL(7^R}JLL|IN z7uxz^IOvdyXwINLjaQ-8eLnkq9@R|d$JDdl-MI5^}sfnx+U-8ds9WuV{4 zj)B~Srb=;=a#sno)X%@ozH{*MC1kL&wEwq4L|U-=0G`ysAD&Z(b$=klg~ z=9}x)6zeCkf)QWBk_LhDQ&%vM#mcuuL_l%5P3ZXumv_?E>)vx{e6sX@M$AUz2}}0T zcMY`EQoFu?YN$fC{`WQyf4}-n$pIt>G`uGXu)PdVjMb?sxBrzn|2^pF&+296;P_O+ zb^4jH(fG+i=L=4WpJYfQJtsj-UHl#U*pkns!Sz-6eCYuBG3#X+=&LolA`cTkGh(aT znCld+Vm2@IBD(X~(;XiVPaEVHY^%rf{gLFoNe>5m`y5LLhs{Zk3jo8wEFq6ltTymv zS`k?Yqpz{Caj~DvMCF>3le13@QM{6FKht^WFvxn$6eIR(w3{GyPm$NRZeN9C6*D+g z-Se7}D>=-!w7g>Tr{_mgcxqXma@fTCrIY^R%i0^tS20;yMP9KbKH6hGNO#ESyj!Ax zcheoz%PDc2{blNtBY((-(%6RI-`g+KUYkizX@UGzJwats7cb68B_n#0D>(L7ZOp5i zX1)rx_>(?q0c&gTudW!)m%z@8`~Y|67J479mQJ%H*IP1_f=!Ua1EDO>tK4X6Z)fkz zk^q@w&kI*e+kD5*=M_2ro0|MyvdTB2{a==7Xz|#9L~wE9^<&}UT8Ja@f|b_R(-W?) zs37o6GF4ZfcW(88C8xwy+Vr|074%|T8XvDFF_8yQ)K5o6MM-GRgOP216yjSWBNc!O z@Q8}K@zQ<20hC1Bod2Z}6hmVoA=?aUG-OJ*{wEDF)27zVx)}nr43rj`amA3Bz3Rml zg+pxcC6-_rSSUj=eojGxbNfAN2oDh3=gPRGYTO<4ZObmpu_;y*b^Yuq4!@;DhX|b% zf+wl@R-OIq4;hcz?$JU+MLNlsc1{dAyx(l0J#&wFI>qvK*=ox=4~5?Ui^{VrlwYeS zpP}Q4@z{Il(tAlf$5t#?e=7vcYyhn1Zf|eTSfdR{>dA{tPkBrYpGkXoJVaf;bb`a8 ztay{n^$z6)!c&PHJ%(s+rOnX@D1wAIE?GF0$LIPNxldZ;9=)RQxIANET1ZQADMp|c zhAT0Zi{GZSC~3#1pDAPp(R>{byhL=ryC!w%{`SG*JVpo#6St4ShTVLC4^fK&O#v@IJmDi$+9fz0AkVNT(8-dKjI{t_{66iPY--}$pvar?^`F(CtkjRJ6w8+({OI5cDndOeZ+ zPaw;bdMuBDWpRry+^&CH&+##|PB^Lv*mjjHjlHv>m$A(^=3;5zZ2@eZ90IQZ{;mi= z(IaRfjBOh}bN2^;7h=}yPf-I9nSJ*(kXUC%jgZDTWlgB0Y}7Etu=+B#eLjH&GSMf) zG@`k;jbqE$Ybicly#EmuPON)5!t(EcXk)IvZvM~iaFbxVMwVju)1y`key=nEcZZ1$ zk%dP=?JrmFNdXQRAQMwl8j<<+b>tvR3XXsBaEX!8G_GNP!LLV;^W~WlRBdqqDypK* z)>iI}PA?H;_+5?K+FIYkXCyopE6^t}*s78|ldhiWYPzd_Kjj zzBr!pyBihywG#|4f&N>B?ziBJOv|i&k%8HIhQOj8J4`o{Br| z^EiIuaE;Fw<)xk*m;-}`IwW!&ry;q5r^to|X8~<~3JMBS+=fpuH-U0F$Pz@GBjlrb z$Pfhs1JiMrP40n85HnCxQnDgr3+{b*;sMoA!LwkF1SSh{y9Gc_TwL630y_>vW`rl} z^4v#*1&0w;|L0_}Ug^0{6z*L%)UlWT8DWdbIfwsAnCrkQfqPD7XM|f1-;%7K!l>u- zluIPh(LwGS4S+YJv9)!B?a)}G`y9rEQzW9Y$(gx0^T9EpR zxq>HxfBCzdOw9|xDsyu;mCek|*ucWG$o}D>CN-2XA%F-z zX=#35THc!6&pDqt;!nl5N;)fym4-CeoXKLXhr-v&E-lDVu5C(%$81W~w^Tn?&s8|w zS}G?#*R#Rnu2c>jp{}|@-HNDIh{ym;lY1}w7@WB2B3othK)-9lI;$Hp6v{fi$tnBj zR->?d@L7oeq9}?_&5QRQ6Btq(a_kHv{SPWYcllT<_qvLvP$cj;Ybu z-Q8XKY}nEW2?Ygb!hN;L60P(HoehSJ-nzdod9jekGe#=lSQbrNVn}9%(h%+VnlnV2 z##ZeoQUJ%3y_MO#q|N(QLD)rifNTptJJ}uc-AKUUS^5frYt)wgVAlhzgbxuou)KMm zazA@Qa9FOta}D{Kl7-8?pPx+NEo_W>L}a()p(`0vxg4z;Auwn95Zz_mH|ot|8O7&@ z-C}o7<>!+d4Ij8TJJTn62myhc$rri(U!B)}uedigHMRMDJRhw^9q)ytBpKC2$bfJ-B+as!tI_VBru3|Ch2KurBJx>iHRyo;KY^A83XPL8XEVW*TY$Kt$!vdTs=L1;tMgdvVKDAi%YW=r<|i%*2_I2$`TMI3{EjAvEV#-wq%GSJe7RgHJ3kwM(Hlk; zjK&kPrPUnOFBh*SAZ_r-@0K9%sIHce6}o{X=XX0!prbR;(fI|*~*=b;9WreLRdw6&lPeeqtHE1OBeM#QM$fv?*wrP^6#v6(< zLSel@Px(xmh2CTefS%o?{gaomHSMr#vkW=>&b0VKtMpirc@{Ce*uI5*u1GF zOQT-@_YMew0AwbdT@#q!s!f1~=4Q@s)_$}!G<>ph&)e5tnxdkjl*{`^N4D&1>KYpT z#HHUs1|z3mir%S!L~aha3-^Nm{ASkJhElVBc^q3*dIYIF2Iod9Caz$l@+xH@ z5ctD52b?5%?(grrhq|PybDzO?@R;;u|2Ai48VFs6L1EC(-{nf~eZYze&A?!iww*%P zsVP^rk(rIns)m{xM||z)>s43K8@rkp-5d7~Q<8jF|k~~BO7^AZ((RFCvYem zi!fu(zwvdLHQD7-sDbEkY&pt%aWY$KMDzQ1@Kdvl0U&T^8OBzZnwD0H@h@g20vV8; zlOuGA7{kZM_t$O-^#!7efrm#cy7p&wwlBOskPaLM?OgMBzip}q{~!aWzWj_1{N4>F zeb8=h;NbRLsfikQV0xqs(we>CkdTl-kCYjogocNQvoV$OC0Kr;`M2)#`f%|`Ti<%v zzdARb$m2A1esMvR3-n_c)r*daxeMy)r=-$ULH@v;L!_*Qb}ELjP8;+)1kLfOx3K97 zK_s962{@|27+3iEs6O`R%g(D=7|0+x5*iAM{^*lj1&bUC-JqbLukw$`FBJbU?A7+K zuE^kAlL3KXh>pVh?Ue9WQZON%+I|Gt&q7dUA+l07aMr~s2SBP8Iu+=3#j#HAjC(Q= ziPdjh03c>!X0~ND>Z#Vz(t0EvA1H;Iaw1u;9l6s|R?Z*MCnv)d5%7Cye!bl2l!NdB zR0IYFruXy!L#LZVr2l4<2hOg!k?jhN3d+dP9u#;TE%+0Zh!c;aVqHNt!smsqR1_Ce z6A1}*`v+pQYxw*7AHnlh9y6CrjF0n!!{xU}5<%yV~q7WDf7kDGXU~0&_HX21XwK9NiwxL^j-=+tmjX zlaMfiRn^kCEl$KRLM`yEcODiIku6wvw1FT;w+hz+_iik-v}KIYY#(KcIqbBT*%rRO zZ#FDHtFFO_&N?(i#9d;3x8b3|L7G4F^UW~f=LO8#lKbrxf`V^rtE;-%w4q`JU+Ttw zvi6GW)6EI_JnV~;;tS2A!7x!jh*>$2RMWli@29S;tfWdwN$HxKnT-!RVuy(-$bds# zE)r+p`tD9pa$8lTx+J3h0PVaL0%kFNv&YY;yQaoIv8u|Jw;^qugM;Je9#6#oA&09i beL{e0aNa4agq*=oS_a5SDoWIf83+Fl%?f@e diff --git a/webroot/rsrc/image/sprite-login.png b/webroot/rsrc/image/sprite-login.png index 58a2dcf5ec0da01b47e9b8b6c13579cd83a77857..8810023ceafc6303a4479d35ff98234d7edc6050 100644 GIT binary patch literal 13623 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 From 478012cd463d06040ae93497c94fb5fadfc76308 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 15 Jun 2023 21:22:10 +0200 Subject: [PATCH 205/425] Add serious business string for defrocking a user (remove admin rights) Summary: Allow humans to understand what happened when `phabricator.serious-business` mode is enabled: The administrator role was removed from a user account. Closes T15480 Test Plan: Enable `phabricator.serious-business` mode. Enable and disable an account as admin. Check that account's feed and the general feed. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15480 Differential Revision: https://we.phorge.it/D25298 --- .../PhabricatorUserEmpowerTransaction.php | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php b/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php index d17418636f..3f3440ede0 100644 --- a/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php +++ b/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php @@ -56,9 +56,15 @@ public function getTitle() { '%s empowered this user as an administrator.', $this->renderAuthor()); } else { - return pht( - '%s defrocked this user.', - $this->renderAuthor()); + if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) { + return pht( + '%s removed the administrator role from this user.', + $this->renderAuthor()); + } else { + return pht( + '%s defrocked this user.', + $this->renderAuthor()); + } } } @@ -70,10 +76,17 @@ public function getTitleForFeed() { $this->renderAuthor(), $this->renderObject()); } else { - return pht( - '%s defrocked %s.', - $this->renderAuthor(), - $this->renderObject()); + if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) { + return pht( + '%s removed the administrator role from %s.', + $this->renderAuthor(), + $this->renderObject()); + } else { + return pht( + '%s defrocked %s.', + $this->renderAuthor(), + $this->renderObject()); + } } } From 327ac921c28b917dfc9215a7537c232f016ef07e Mon Sep 17 00:00:00 2001 From: Mukunda Modell Date: Mon, 19 Jun 2023 08:54:32 +0200 Subject: [PATCH 206/425] Conduit column.search: add status, sequence and isDefault to API results Summary: This seems like a fairly obvious oversight with the column.search API. Knowing: 1. isHidden - boolean to indicate Active vs Archived 2. isDefaultColumn - the one that Tasks get dropped in by default, usually called "Backlog" 3. sequence - numerical order on the Workboard are all necessary for a lot of things that very sensible real-world API clients need to do when working with columns. Partial cherry-pick from: https://phabricator.wikimedia.org/rPHABebfe30890b52784d222ec4ed36c05462b2a57f92 Closes T15484 Test Plan: Tested on phabricator.wikimedia.org over many months and used by real client apps. To do additional tests, visit this page: /conduit/method/project.column.search/ Check that the new fields are returned correctly and nothing explodes. Reviewers: #blessed_committers, O1 Blessed Committers, 0, avivey, valerio.bozzolan Reviewed By: #blessed_committers, O1 Blessed Committers, 0, valerio.bozzolan Subscribers: Cigaryno, 0, speck, tobiaswiese, valerio.bozzolan, Matthew Maniphest Tasks: T15081, T15484 Differential Revision: https://we.phorge.it/D25038 --- .../project/storage/PhabricatorProjectColumn.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 49d7f28a9f..e4359acd1b 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -271,6 +271,19 @@ public function getFieldSpecificationsForConduit() { pht( 'For columns that proxy another object (like a subproject or '. 'milestone), the PHID of the object they proxy.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('isHidden') + ->setType('bool') + ->setDescription(pht('True if this column is hidden.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('isDefaultColumn') + ->setType('bool') + ->setDescription(pht('True if this is the default column.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('sequence') + ->setType('int') + ->setDescription( + pht('The sequence in which this column appears on the workboard.')), ); } @@ -279,6 +292,9 @@ public function getFieldValuesForConduit() { 'name' => $this->getDisplayName(), 'proxyPHID' => $this->getProxyPHID(), 'project' => $this->getProject()->getRefForConduit(), + 'isHidden' => $this->isHidden(), + 'isDefaultColumn' => $this->isDefaultColumn(), + 'sequence' => (int)$this->getSequence(), ); } From 324470e39b0d4539a7487c2144157d686f5d0906 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 19 Jun 2023 11:55:10 +0200 Subject: [PATCH 207/425] Give embedded image files with transparency a checkered background Summary: Have a consistent rendering experience between File page and file embedded in a task for PNG image files with an alpha channel: Show a checkered background on the embedded image instead of a white background; show full contrast when hovering over the embedded image. Closes T15473 Test Plan: See steps in T15473 - compare an image file embedded in a task before and after applying this patch. - test in Task description - test in Task comment - test in Task comment inside something weird like a table Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15473 Differential Revision: https://we.phorge.it/D25294 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/phui/phui-property-list-view.css | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index a7b8a41aa3..277db4a5b7 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'f3d35ae0', + 'core.pkg.css' => '8abf4250', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '525f9a1d', @@ -172,7 +172,7 @@ 'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', 'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64', - 'rsrc/css/phui/phui-property-list-view.css' => '5adf7078', + 'rsrc/css/phui/phui-property-list-view.css' => '118db252', 'rsrc/css/phui/phui-remarkup-preview.css' => '91767007', 'rsrc/css/phui/phui-segment-bar-view.css' => '5166b370', 'rsrc/css/phui/phui-spacing.css' => 'b05cadc3', @@ -875,7 +875,7 @@ 'phui-pager-css' => 'd022c7ad', 'phui-pinboard-view-css' => '1f08f5d8', 'phui-policy-section-view-css' => '139fdc64', - 'phui-property-list-view-css' => '5adf7078', + 'phui-property-list-view-css' => '118db252', 'phui-remarkup-preview-css' => '91767007', 'phui-segment-bar-view-css' => '5166b370', 'phui-spacing-css' => 'b05cadc3', diff --git a/webroot/rsrc/css/phui/phui-property-list-view.css b/webroot/rsrc/css/phui/phui-property-list-view.css index 4d2cbc9b50..93388c3d7c 100644 --- a/webroot/rsrc/css/phui/phui-property-list-view.css +++ b/webroot/rsrc/css/phui/phui-property-list-view.css @@ -204,13 +204,15 @@ div.phui-property-list-stacked .phui-property-list-properties } -.document-engine-image img { +.document-engine-image img, +.phabricator-remarkup-embed-image img { margin: 20px auto; background: url('/rsrc/image/checker_light.png'); max-width: 100%; } -.device-desktop .document-engine-image img:hover { +.device-desktop .document-engine-image img:hover, +.device-desktop .phabricator-remarkup-embed-image img:hover { background: url('/rsrc/image/checker_dark.png'); } From 1c59b65421362da9e7c31f8a3f2177383efece27 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 19 Jun 2023 12:08:19 +0200 Subject: [PATCH 208/425] Dashboards: add capability who can create Dashboards Summary: Add a `dashboard.create` capability to allow limiting dashboard creation (and creation of related panels). This can reduce spam in open Phorge installations in which anyone can create an account while you still want anyone to be able to view existing dashboards. Closes T15438 Test Plan: 1. As an admin, go to `/applications/view/PhabricatorDashboardApplication/` 2. See only two options "Can Use Application: All Users" and "Can Configure Application: Administrators" 3. Apply patch 4. As an admin, go to `/applications/view/PhabricatorDashboardApplication/` and see the new option "Can Create Dashboards" set to "All Users" 5. As an average user, go to `/dashboard/` and successfully create a new Dashboard 6. As an admin, go to `/applications/edit/PhabricatorDashboardApplication/` and change "Can Create Dashboards" from "All Users" to "Administrators", select "Save Policies" button 7. As an average user, go to `/dashboard/` and see that "Create Dashboard" is disabled 8. As an average user, go to an existing dashboard not created by this user and see that selecting "Edit Dashboard" shows "You do not have permission to edit this object." 9. As an average user, go to the existing dashboard that you created yourself and see that the Edit Policy is set to this user. 10. As an average user, go to the existing dashboard that you created yourself and see that you can still create or add panels. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15438 Differential Revision: https://we.phorge.it/D25270 --- src/__phutil_library_map__.php | 2 ++ .../PhabricatorDashboardCreateCapability.php | 16 ++++++++++++++++ .../PhabricatorDashboardApplication.php | 8 ++++++++ .../editor/PhabricatorDashboardEditEngine.php | 5 +++++ 4 files changed, 31 insertions(+) create mode 100644 src/applications/countdown/capability/PhabricatorDashboardCreateCapability.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 17560e11d1..09b8b86da4 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3081,6 +3081,7 @@ 'PhabricatorDashboardColumn' => 'applications/dashboard/layoutconfig/PhabricatorDashboardColumn.php', 'PhabricatorDashboardConsoleController' => 'applications/dashboard/controller/PhabricatorDashboardConsoleController.php', 'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php', + 'PhabricatorDashboardCreateCapability' => 'applications/countdown/capability/PhabricatorDashboardCreateCapability.php', 'PhabricatorDashboardDAO' => 'applications/dashboard/storage/PhabricatorDashboardDAO.php', 'PhabricatorDashboardDashboardPHIDType' => 'applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php', 'PhabricatorDashboardDatasource' => 'applications/dashboard/typeahead/PhabricatorDashboardDatasource.php', @@ -9496,6 +9497,7 @@ 'PhabricatorDashboardColumn' => 'Phobject', 'PhabricatorDashboardConsoleController' => 'PhabricatorDashboardController', 'PhabricatorDashboardController' => 'PhabricatorController', + 'PhabricatorDashboardCreateCapability' => 'PhabricatorPolicyCapability', 'PhabricatorDashboardDAO' => 'PhabricatorLiskDAO', 'PhabricatorDashboardDashboardPHIDType' => 'PhabricatorPHIDType', 'PhabricatorDashboardDatasource' => 'PhabricatorTypeaheadDatasource', diff --git a/src/applications/countdown/capability/PhabricatorDashboardCreateCapability.php b/src/applications/countdown/capability/PhabricatorDashboardCreateCapability.php new file mode 100644 index 0000000000..c610df069e --- /dev/null +++ b/src/applications/countdown/capability/PhabricatorDashboardCreateCapability.php @@ -0,0 +1,16 @@ + array( + 'default' => PhabricatorPolicies::POLICY_USER, + 'caption' => pht('Default create policy for Dashboards.'), + ), + ); + } } diff --git a/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php b/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php index 06b0a3a6fb..84b36fe546 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php @@ -66,6 +66,11 @@ protected function getObjectViewURI($object) { return $object->getURI(); } + protected function getCreateNewObjectPolicy() { + return $this->getApplication()->getPolicy( + PhabricatorDashboardCreateCapability::CAPABILITY); + } + protected function buildCustomEditFields($object) { $layout_options = PhabricatorDashboardLayoutMode::getLayoutModeMap(); From 7c1487e4dcfab19e0ef42cd4596780a37e0c8c4e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 19 Jun 2023 14:10:29 +0200 Subject: [PATCH 209/425] Fix Pholio RuntimeException: Undefined variable $dictionary (when adding an empty Inline Comment) Summary: Trying to create an empty inline comment in a Pholio mock, `$dictionary` does not get set as both `strlen($v_content)` and `$inline->getID()` are not true. Thus show a more explanatory error message ('Comment cannot be empty.') to users instead of exposing internal variable names. ``` EXCEPTION: (RuntimeException) Undefined variable $dictionary at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=b325304b6e52), phorge(head=master, ref.master=980293b707a0) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/pholio/controller/PholioInlineController.php:117] ``` Closes T15456 Test Plan: After applying this change, try to add an empty Inline Comment in a Pholio mock. See that the error message is now "Comment cannot be empty." instead of "Undefined variable $dictionary". Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15456 Differential Revision: https://we.phorge.it/D25281 --- .../pholio/controller/PholioInlineController.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/applications/pholio/controller/PholioInlineController.php b/src/applications/pholio/controller/PholioInlineController.php index ddced1f9eb..2201e97f74 100644 --- a/src/applications/pholio/controller/PholioInlineController.php +++ b/src/applications/pholio/controller/PholioInlineController.php @@ -102,6 +102,7 @@ public function handleRequest(AphrontRequest $request) { ->addCancelButton($mock_uri, pht('Close')); } + $error = null; if ($request->isFormPost()) { $v_content = $request->getStr('content'); @@ -112,9 +113,13 @@ public function handleRequest(AphrontRequest $request) { } else if ($inline->getID()) { $inline->delete(); $dictionary = array(); + } else { + $error = pht('Comment cannot be empty.'); } - return id(new AphrontAjaxResponse())->setContent($dictionary); + if ($error === null) { + return id(new AphrontAjaxResponse())->setContent($dictionary); + } } switch ($mode) { @@ -151,6 +156,7 @@ public function handleRequest(AphrontRequest $request) { ->setUser($viewer) ->setName('content') ->setLabel(pht('Comment')) + ->setError($error) ->setValue($v_content)); return $this->newDialog() From a25af8ccef69de5978b0904866b028d96732202b Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 23 Jun 2023 08:48:18 +0200 Subject: [PATCH 210/425] Diviner: add a minimal systemd example Summary: After this change the section "Managing Daemons with phd" mentions a minimally viable systemd configuration file to make systemd-users happier and systemd-haters saddest. Closes T15467 Test Plan: - read the page, see systemd, don't cry Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: 20after4, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15467 Differential Revision: https://we.phorge.it/D25293 --- .../configuration/managing_daemons.diviner | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/docs/user/configuration/managing_daemons.diviner b/src/docs/user/configuration/managing_daemons.diviner index 0e56a95995..d4af3162a4 100644 --- a/src/docs/user/configuration/managing_daemons.diviner +++ b/src/docs/user/configuration/managing_daemons.diviner @@ -46,6 +46,62 @@ If you want finer-grained control, you can use: NOTE: When you upgrade Phorge or change configuration, you should restart the daemons by running `phd restart`. +Automatically start phd +======================= + +NOTE: If you are opinionated against systemd, cover the eyes of your children +right now!1! + +Computers are good in automatically starting stuff, thanks to the invention +of the "init system". + +Phorge virtually supports any init system. Which one is yours? Don't worry. +If you don't know, it's systemd. + +We propose a minimal systemd configuration file, following some assumptions: + +- your lovely Phorge is installed `/somewhere` +- you have a database service called `mariadb` +- you have a dedicated Unix user called `daemon-user` - coming from + @{article:Diffusion User Guide} + +With the above assumptions, create this configuration file as super-user: + +```lang=ini,name=/etc/systemd/system/phorge-phd.service +[Unit] +Description=Phorge Daemons +Documentation=https://we.phorge.it/book/phorge/article/managing_daemons/ +After=syslog.target network.target mariadb.service + +[Service] +Type=forking +User=daemon-user +Group=daemon-user +ExecStart=/somewhere/phorge/bin/phd start +ExecStop=/somewhere/phorge/bin/phd stop + +[Install] +WantedBy=multi-user.target +``` + +To install this new systemd configuration, execute these commands as +super-user: + +``` +systemctl daemon-reload +systemctl enable --now phorge-phd +``` + +Now the process has started and will survive after any reboot. + +To check if everything is OK: + +``` +systemctl status phorge-phd +``` + +Anything else can be explored in depth by reading the systemd documentation. + = Daemon Console = You can view status and debugging information for daemons in the Daemon Console From e4fe54edf0d368fa87c8b29b293e4ad4e7b14abf Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 26 Jun 2023 14:19:01 +0200 Subject: [PATCH 211/425] Fix typo "recipeint" in metamta.one-mail-per-recipient description Test Plan: Go to `/config/settings/all` and look at the description of `metamta.one-mail-per-recipient` Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25311 --- .../config/option/PhabricatorMetaMTAConfigOptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php index 36cbd9bd51..7499c978d3 100644 --- a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php +++ b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php @@ -227,7 +227,7 @@ public function getOptions() { pht( 'Controls whether email for multiple recipients is sent by '. 'creating one message with everyone in the "To:" line, or '. - 'multiple messages that each have a single recipeint in the '. + 'multiple messages that each have a single recipient in the '. '"To:" line.')) ->setDescription($one_mail_per_recipient_desc), $this->newOption('metamta.can-send-as-user', 'bool', false) From 659e13fa6d0e92f129e4c940e765846dd53c9ad4 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 27 Jun 2023 08:14:29 +0200 Subject: [PATCH 212/425] Fix UX regression in Dashboard Tabs Panel Summary: Before this change, it seems that the Dashboard Tabs Panel could be empty as default. This was the regression: cbc0e661544a31290099ab3d88481bb8d4a3003c The variable $is_selected was defined inside a foreach loop, but that loop was terminated when it was then read again. This redefines that variable in the right scope. | Before | After | |-----------|-----------| | {F312797} | {F312798} | Closes T15474 Test Plan: - see that now you have something visibile in your Dashboard Tabs Panel Reviewers: speck, O1 Blessed Committers Reviewed By: speck, O1 Blessed Committers Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15474, T15146 Differential Revision: https://we.phorge.it/D25291 --- .../dashboard/paneltype/PhabricatorDashboardTabsPanelType.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php index 54c26dd9d3..831229bab1 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php @@ -282,6 +282,8 @@ public function renderPanelContent( $panel_content = pht('(Invalid Panel)'); } + $is_selected = (string)$idx === (string)$selected; + $content_id = celerity_generate_unique_node_id(); $content[] = phutil_tag( From 1005e381604eb51d5bccc4f198d5333eb0ee28e4 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 27 Jun 2023 13:29:24 +0200 Subject: [PATCH 213/425] Fix notification message when Aphlict is not configured Summary: Before this change the footer in the notification bar was a bit weird on the message "Notification server not enabled": | Before | After | |-----------|-----------| | {F315024} | {F315001} | Also a bit wrong on the "Connecting..." message: | Before | After | |-----------|-----------| | {F315061} | {F315072} | The fix involves a refactor in the HTML structure in order to match the graphic rendered when Aphlict is configured and running. In short, we pass from this: COUNTEREXAMPLE Notification server not enabled To this: Notification server not enabled See the Task for additional details. Closes T15415 Test Plan: Follow the official Notification documentation about the config `notification.servers`, open the Notification top-bar, and check the message in these conditions: - without the config - with the config - with/without Aphlict running https://we.phorge.it/book/phorge/article/notifications/ Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15415 Differential Revision: https://we.phorge.it/D25312 --- .../PhabricatorNotificationStatusView.php | 59 +++++++++++-------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/src/applications/notification/view/PhabricatorNotificationStatusView.php b/src/applications/notification/view/PhabricatorNotificationStatusView.php index e962395bba..69939525cc 100644 --- a/src/applications/notification/view/PhabricatorNotificationStatusView.php +++ b/src/applications/notification/view/PhabricatorNotificationStatusView.php @@ -40,34 +40,41 @@ protected function getTagAttributes() { protected function getTagContent() { $have = PhabricatorEnv::getEnvConfig('notification.servers'); if ($have) { - $icon = id(new PHUIIconView()) - ->setIcon('fa-circle-o yellow'); - $text = pht('Connecting...'); - return phutil_tag( - 'span', - array( - 'class' => 'connection-status-text '. - 'aphlict-connection-status-connecting', - ), - array( - $icon, - $text, - )); + return $this->buildMessageView( + 'aphlict-connection-status-connecting', + 'fa-circle-o yellow', + pht('Connecting...')); } else { - $text = pht('Notification server not enabled'); - $icon = id(new PHUIIconView()) - ->setIcon('fa-circle-o grey'); - return phutil_tag( - 'span', - array( - 'class' => 'connection-status-text '. - 'aphlict-connection-status-notenabled', - ), - array( - $icon, - $text, - )); + return $this->buildMessageView( + 'aphlict-connection-status-notenabled', + 'fa-circle-o grey', + pht('Notification server not enabled')); } } + /** + * Create an icon and a message. + * + * @param string $class_name Raw CSS class name(s) space separated + * @param string $icon_name Icon name + * @param string $text Text to be shown + * @return array + */ + private function buildMessageView($class_name, $icon_name, $text) { + $icon = id(new PHUIIconView()) + ->setIcon($icon_name); + + $message = phutil_tag( + 'span', + array( + 'class' => 'connection-status-text '.$class_name, + ), + $text); + + return array( + $icon, + $message, + ); + } + } From 8ffa6044626dacaddff8aaa1f291e57135416f0c Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 28 Jun 2023 23:14:09 +0200 Subject: [PATCH 214/425] Diviner: mention how to flag Arcanist and Phorge as "safe" git repos Summary: This tip is useful if you are affected by the following error in git or similar: fatal: detected dubious ownership in repository at '/var/www/phorge' Just follow the new tip from Diviner to fix at system level. Ref T15282 Test Plan: - see if you like the documentation tip - try to execute the command, and verify that your computer does not explode - eventually visit the /config/ page with and without that change (with and without a /etc/gitconfig) Reviewers: O1 Blessed Committers, avivey, Cigaryno Reviewed By: O1 Blessed Committers, avivey, Cigaryno Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15282 Differential Revision: https://we.phorge.it/D25236 --- src/docs/user/installation_guide.diviner | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index 1a09a2a3d6..f3c2102059 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -137,6 +137,12 @@ dependencies: somewhere/ $ git clone https://we.phorge.it/source/arcanist.git somewhere/ $ git clone https://we.phorge.it/source/phorge.git +After cloning, flag them as safe (to avoid the error //"fatal: +detected dubious ownership in repository at ..."//): + + $ sudo git config --system --add safe.directory somewhere/arcanist + $ sudo git config --system --add safe.directory somewhere/phorge + Next Steps ========== From 0a17287e08cd0eea2675ff46efa3135fe6994b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taavi=20V=C3=A4=C3=A4n=C3=A4nen?= Date: Thu, 29 Jun 2023 14:40:11 +0300 Subject: [PATCH 215/425] Generalize references to PHP 5.x Debian/Ubuntu packages Summary: Both Debian and Ubuntu have been shipping PHP 7.x (or now 8.x) for a while. This patch replaces the versioned php5-foo package references with more general php-foo package names. Test Plan: Navigated to the documentation pages that this patch updates. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25315 --- .../auth/provider/PhabricatorLDAPAuthProvider.php | 2 +- .../config/check/PhabricatorPHPConfigSetupCheck.php | 4 ++-- src/docs/user/configuration/configuration_guide.diviner | 2 +- src/docs/user/field/restarting.diviner | 2 +- src/docs/user/installation_guide.diviner | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php index d1db832aa2..19f8c625d7 100644 --- a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php @@ -264,7 +264,7 @@ public static function assertLDAPExtensionInstalled() { 'talk to LDAP. Usually you can install it with '. '`%s`, `%s`, or a similar package manager command.', 'yum install php-ldap', - 'apt-get install php5-ldap')); + 'apt-get install php-ldap')); } } diff --git a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php index 09b96d05cf..1862308052 100644 --- a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php @@ -83,7 +83,7 @@ protected function executeChecks() { // NOTE: We're intentionally telling you to install "mysqlnd" here; on // Ubuntu, there's no separate "mysqli" package. - phutil_tag('tt', array(), 'sudo apt-get install php5-mysqlnd')); + phutil_tag('tt', array(), 'sudo apt-get install php-mysqlnd')); $this->newIssue('php.mysqli') ->setName(pht('MySQLi Extension Not Available')) @@ -103,7 +103,7 @@ protected function executeChecks() { 'native driver is recommended.'. "\n\n". 'You may be able to install the native driver with a command like: %s', - phutil_tag('tt', array(), 'sudo apt-get install php5-mysqlnd')); + phutil_tag('tt', array(), 'sudo apt-get install php-mysqlnd')); $this->newIssue('php.myqlnd') diff --git a/src/docs/user/configuration/configuration_guide.diviner b/src/docs/user/configuration/configuration_guide.diviner index 1fde967a95..c5aa82af86 100644 --- a/src/docs/user/configuration/configuration_guide.diviner +++ b/src/docs/user/configuration/configuration_guide.diviner @@ -139,7 +139,7 @@ server.modules list: Finally, you should run the following commands to enable php support: - $ sudo apt-get install php5-cgi # for Ubuntu; other distros should be similar + $ sudo apt-get install php-cgi # for Ubuntu; other distros should be similar $ sudo lighty-enable-mod fastcgi-php Restart lighttpd after making your edits, then continue below. diff --git a/src/docs/user/field/restarting.diviner b/src/docs/user/field/restarting.diviner index 638e1f1490..8e21c2ac77 100644 --- a/src/docs/user/field/restarting.diviner +++ b/src/docs/user/field/restarting.diviner @@ -112,5 +112,5 @@ like one of these on your system, or a different command: ``` $ sudo /etc/init.d/php-fpm restart -$ sudo service php5-fpm reload +$ sudo service php-fpm reload ``` diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index f3c2102059..ec7aa186d2 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -123,7 +123,7 @@ Here's a general description of what you need to install: - MySQL Server (usually "mysqld" or "mysql-server" or "mariadb-server") - PHP (usually "php") - Required PHP extensions: mbstring, iconv, mysql (or mysqli), curl, pcntl - (these might be something like "php-mysql" or "php5-mysqlnd") + (these might be something like "php-mysql" or "php-mysqlnd") - Optional PHP extensions: gd, zip If you already have LAMP setup, you've probably already got everything you need. From 101ce834b8ad297a6692e62a85717de21bb48ad5 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Fri, 30 Jun 2023 13:17:03 +0100 Subject: [PATCH 216/425] Fix PHP 8.1 strlen() error when database replication Seconds_Behind_Master is NULL Summary: When MySQL database replication is configured, but not running, the slave status Seconds_Behind_Master is NULL. When this value is passed to strlen() an error is thrown. Fixes T15507 Test Plan: * Configure database replication such that Seconds_Behind_Master is NULL * Restart php-fpm * Go to Phorge web site. Reviewers: O1 Blessed Committers, chris Reviewed By: O1 Blessed Committers, chris Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15507 Differential Revision: https://we.phorge.it/D25318 --- src/infrastructure/cluster/PhabricatorDatabaseRef.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRef.php b/src/infrastructure/cluster/PhabricatorDatabaseRef.php index e526eb1639..0fe2aabbcb 100644 --- a/src/infrastructure/cluster/PhabricatorDatabaseRef.php +++ b/src/infrastructure/cluster/PhabricatorDatabaseRef.php @@ -393,7 +393,7 @@ private static function queryRefs(array $refs) { if ($is_replica) { $latency = idx($replica_status, 'Seconds_Behind_Master'); - if (!strlen($latency)) { + if (!phutil_nonempty_string($latency)) { $ref->setReplicaStatus(self::REPLICATION_NOT_REPLICATING); } else { $latency = (int)$latency; From 73253489bf002f3fd29dba3af3845cb4938fbf56 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Fri, 30 Jun 2023 14:34:41 +0100 Subject: [PATCH 217/425] Make src/infrastructure/javelin/markup.php phabricator_form PHP 8.1 compliant Summary: src/infrastructure/javelin/markup.php phabricator_form() performs a strcasecmp() operation on an optional attribute withoiut checking to see if it exists first. This causes an 'Passing null to parameter #1 ($string1) of type string is deprecated' error. Because we subsequenly check to see that the value equals /POST/i, all we need to do is check that the value has some 'true' value before doing the strcasecmp(). If it's not true, then it won't be POST! Fixes T15509 Test Plan: View a diff. Eg: https://my.phorge.site/D1234 Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15509 Differential Revision: https://we.phorge.it/D25319 --- src/infrastructure/javelin/markup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/javelin/markup.php b/src/infrastructure/javelin/markup.php index 8a040031cc..285f027d3d 100644 --- a/src/infrastructure/javelin/markup.php +++ b/src/infrastructure/javelin/markup.php @@ -74,7 +74,7 @@ function phabricator_form(PhabricatorUser $user, $attributes, $content) { $body = array(); $http_method = idx($attributes, 'method'); - $is_post = (strcasecmp($http_method, 'POST') === 0); + $is_post = $http_method && (strcasecmp($http_method, 'POST') === 0); $http_action = idx($attributes, 'action'); From aebd7a2f7239c025f65171065e3fab54bdb98200 Mon Sep 17 00:00:00 2001 From: MacFan4000 Date: Thu, 29 Jun 2023 19:21:54 -0400 Subject: [PATCH 218/425] Update a couple of lingering instances of secure.phabricator.com to we.phorge.it Summary: We were still pointint to secure.phabricator.com in a couple places. This fixes that. Test Plan: No testing needed Reviewers: O1 Blessed Committers, speck, Cigaryno Reviewed By: O1 Blessed Committers, speck, Cigaryno Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25316 --- src/applications/diviner/controller/DivinerMainController.php | 2 +- src/view/widget/AphrontStackTraceView.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/diviner/controller/DivinerMainController.php b/src/applications/diviner/controller/DivinerMainController.php index 21450698e6..791e7520b6 100644 --- a/src/applications/diviner/controller/DivinerMainController.php +++ b/src/applications/diviner/controller/DivinerMainController.php @@ -50,7 +50,7 @@ public function handleRequest(AphrontRequest $request) { $text = pht( "(NOTE) **Looking for documentation?** ". "If you're looking for help and information about %s, ". - "you can [[https://secure.phabricator.com/diviner/ | ". + "you can [[https://we.phorge.it/diviner/ | ". "browse the public %s documentation]] on the live site.\n\n". "Diviner is the documentation generator used to build this ". "documentation.\n\n". diff --git a/src/view/widget/AphrontStackTraceView.php b/src/view/widget/AphrontStackTraceView.php index ff8cac43bc..15b39be878 100644 --- a/src/view/widget/AphrontStackTraceView.php +++ b/src/view/widget/AphrontStackTraceView.php @@ -15,7 +15,7 @@ public function render() { $libraries = PhutilBootloader::getInstance()->getAllLibraries(); // TODO: Make this configurable? - $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/'; + $path = 'https://we.phorge.it/diffusion/%s/browse/master/src/'; $callsigns = array( 'arcanist' => 'ARC', From 9aafbb42ef8d33437696c4b55fc24ea1de0a3da9 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Sun, 2 Jul 2023 08:47:59 -0700 Subject: [PATCH 219/425] fix strlen in PhabricatorMailManagementShowOutboundWorkflow Summary: Ref T15064 Test Plan: run `./bin/mail show-outbound --id 1` on a non-html email with php 8.1, no crash. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Tags: #php_8_support Maniphest Tasks: T15064 Differential Revision: https://we.phorge.it/D25275 --- .../PhabricatorMailManagementSendTestWorkflow.php | 5 +---- .../PhabricatorMailManagementShowOutboundWorkflow.php | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php index f390ff27df..6ea2c9de5a 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php @@ -63,6 +63,7 @@ protected function didConstruct() { array( 'name' => 'type', 'param' => 'message-type', + 'default' => PhabricatorMailEmailMessage::MESSAGETYPE, 'help' => pht( 'Send the specified type of message (email, sms, ...).'), ), @@ -74,10 +75,6 @@ public function execute(PhutilArgumentParser $args) { $viewer = $this->getViewer(); $type = $args->getArg('type'); - if (!strlen($type)) { - $type = PhabricatorMailEmailMessage::MESSAGETYPE; - } - $type_map = PhabricatorMailExternalMessage::getAllMessageTypes(); if (!isset($type_map[$type])) { throw new PhutilArgumentUsageException( diff --git a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php index f29a63c2eb..1f4f5a3e2a 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php @@ -65,7 +65,7 @@ public function execute(PhutilArgumentParser $args) { foreach ($messages as $message_key => $message) { if ($args->getArg('dump-html')) { $html_body = $message->getHTMLBody(); - if (strlen($html_body)) { + if (phutil_nonempty_string($html_body)) { $template = "{$html_body}"; $console->writeOut("%s\n", $html_body); @@ -188,7 +188,7 @@ public function execute(PhutilArgumentParser $args) { $info[] = null; $info[] = $this->newSectionHeader(pht('TEXT BODY')); - if (strlen($message->getBody())) { + if (phutil_nonempty_string($message->getBody())) { $info[] = tsprintf('%B', $message->getBody()); } else { $info[] = pht('(This message has no text body.)'); @@ -203,7 +203,7 @@ public function execute(PhutilArgumentParser $args) { $info[] = null; $info[] = $this->newSectionHeader(pht('HTML BODY')); - if (strlen($message->getHTMLBody())) { + if (phutil_nonempty_string($message->getHTMLBody())) { $info[] = $message->getHTMLBody(); $info[] = null; } else { From d725ffaa7728bdbcd07870e9e8f4d244eb9dcf41 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Sun, 2 Jul 2023 08:48:51 -0700 Subject: [PATCH 220/425] Fix "Register with Approval flow" for php 8 Summary: Ref T15064. Test Plan: - Register as a new user - as admin, view user's page and approve it - As new user, hit "wait patiently" to go back to Home No crashes in this flow. Reviewers: O1 Blessed Committers, chris Reviewed By: O1 Blessed Committers, chris Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15064 Differential Revision: https://we.phorge.it/D25317 --- .../auth/controller/PhabricatorAuthNeedsApprovalController.php | 2 +- src/applications/auth/engine/PhabricatorAuthPasswordEngine.php | 2 ++ .../customfield/standard/PhabricatorStandardCustomField.php | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php b/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php index 1121dfb14a..b098a07ddd 100644 --- a/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php +++ b/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php @@ -51,7 +51,7 @@ private function newCustomWaitForApprovalInstructions() { $viewer, PhabricatorAuthWaitForApprovalMessageType::MESSAGEKEY); - if (!strlen($text)) { + if (!phutil_nonempty_string($text)) { return null; } diff --git a/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php b/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php index a1fec4a6d2..0efb1fe4fc 100644 --- a/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php @@ -150,6 +150,8 @@ public function checkNewPassword( $base_uri = new PhutilURI($base_uri); $blocklist[] = $base_uri->getDomain(); + $blocklist = array_filter($blocklist); + // Generate additional subterms by splitting the raw blocklist on // characters like "@", " " (space), and "." to break up email addresses, // readable names, and domain names into components. diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php index c41148a4aa..37f9e808ab 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -301,7 +301,7 @@ public function shouldAppearInPropertyView() { } public function renderPropertyViewValue(array $handles) { - if (!strlen($this->getFieldValue())) { + if (!phutil_nonempty_string($this->getFieldValue())) { return null; } return $this->getFieldValue(); From 603cf474ee1d098ae9e4107d4fefdf1cacc71475 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Mon, 3 Jul 2023 13:46:25 -0700 Subject: [PATCH 221/425] Fix int fields for now Summary: Fix T15516 Test Plan: Viewed an "int" custom field, no boom. Reviewers: speck, valerio.bozzolan, O1 Blessed Committers Reviewed By: valerio.bozzolan, O1 Blessed Committers Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15516 Differential Revision: https://we.phorge.it/D25325 --- .../standard/PhabricatorStandardCustomField.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php index 37f9e808ab..373aa9b66a 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -301,10 +301,14 @@ public function shouldAppearInPropertyView() { } public function renderPropertyViewValue(array $handles) { - if (!phutil_nonempty_string($this->getFieldValue())) { - return null; + // If your field needs to render anything more complicated then a string, + // then you should override this method. + $value_str = phutil_string_cast($this->getFieldValue()); + + if (phutil_nonempty_string($value_str)) { + return $value_str; } - return $this->getFieldValue(); + return null; } public function shouldAppearInApplicationSearch() { From 4a0d3ba3e14b65ae88131c87e1b1ce0dd9b927c8 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 4 Jul 2023 17:10:19 +0200 Subject: [PATCH 222/425] Locate File: allow to search './path/to/something.txt' Summary: Before this change, if you search a file using Locate File, this was the only accepted syntax for a full-path: path/to/something.txt After this change, some relative/absolute Unix-like variants are also accepted: ./path/to/something.txt /path/to/something.txt Similar prefixes can be frequent when you quickly copy-paste things from shell commands like 'grep' or 'find' etc. Ref T15508 Test Plan: Visit a lovely repository like Phorge and use Locate File with these: 1. src/applications/maniphest/editor/ManiphestEditEngine.php 2. /src/applications/maniphest/editor/ManiphestEditEngine.php 3. ./src/applications/maniphest/editor/ManiphestEditEngine.php After this change, also 2. and 3. return the expected file. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15508 Differential Revision: https://we.phorge.it/D25320 --- resources/celerity/map.php | 16 ++++++++-------- .../diffusion/DiffusionLocateFileSource.js | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 277db4a5b7..0f0ee9d3f4 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -387,7 +387,7 @@ 'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b', 'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd', 'rsrc/js/application/differential/behavior-populate.js' => 'b86ef6c2', - 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => '94243d89', + 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => '6c798a10', 'rsrc/js/application/diffusion/ExternalEditorLinkEngine.js' => '48a8641f', 'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'b7b73831', 'rsrc/js/application/diffusion/behavior-commit-branches.js' => '4b671572', @@ -707,7 +707,7 @@ 'javelin-chart-function-label' => '81de1dab', 'javelin-color' => '78f811c9', 'javelin-cookie' => '05d290ef', - 'javelin-diffusion-locate-file-source' => '94243d89', + 'javelin-diffusion-locate-file-source' => '6c798a10', 'javelin-dom' => 'e4c7622a', 'javelin-dynval' => '202a2e85', 'javelin-event' => 'c03f2fb4', @@ -1555,6 +1555,12 @@ 'phabricator-textareautils', 'phabricator-remarkup-metadata', ), + '6c798a10' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-typeahead-preloaded-source', + 'javelin-util', + ), '6cfa0008' => array( 'javelin-dom', 'javelin-dynval', @@ -1762,12 +1768,6 @@ 'phabricator-prefab', 'javelin-json', ), - '94243d89' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-typeahead-preloaded-source', - 'javelin-util', - ), '9623adc1' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js b/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js index 1f94f349d3..3a3bcb9bce 100644 --- a/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js +++ b/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js @@ -99,6 +99,10 @@ JX.install('DiffusionLocateFileSource', { return []; } + // Be nice with terminal users that may have "./" or "/" prefixes. + // Otherwise, not a single result is returned. + search = search.replace(/^\.?\//, ''); + // We know that the results for "abc" are always a subset of the results // for "a" and "ab" -- and there's a good chance we already computed // those result sets. Find the longest cached result which is a prefix From 80484b76a5ff815a4f4849e2899fe39650bcd560 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 4 Jul 2023 17:14:23 +0200 Subject: [PATCH 223/425] Remarkup Code-block: parse language specifier in markdown Summary: We add support to code blocks with the language expressed as GitLab/GitHub/StackOverflow/... "flavored markdown". So we support this syntax: (to avoid confusion see it online on the Diff) lang=text ```php $asd = 1; ``` Before this change, this was the only supposed syntax in Remarkup, with an explicit "lang=": lang=text ```lang=php $asd = 1; ``` This change introduces a minor risk to eat legitimate Remarkup content, since Remarkup allows to do a multi-line in this way: lang=text ```$asd = 1; $asd = 2;``` The above example still works, but, there is a chance that hardcore Remarkup people have a problem when doing a code block to mention programming languages. In short, this can be problematic since "cpp" will be eaten from this list: COUNTEREXAMPLE ```cpp php python ``` Using the above example is not socially nice because it is not usable in GitLab, GitHub and Stack Overflow. If your first line is eaten: Just *add* a newline on the top to reach a valid raw Markdown list (suggested, valid in Remarkup + Markdown): lang=text ``` cpp php python ``` Or, just add "text" to specify that as language (suggested, valid in Remarkup + Markdown): lang=text ```text cpp php python ``` Or, just *remove* a newline from the bottom to reach a valid raw Remarkup list (Remarkup-only): lang=text ```cpp php python``` Or, just specify that you are writing in the language "text" (Remarkup-only): lang=text ```lang=text cpp php python``` To reduce impact and help you, the logic of this strict implementation is: - must have backticks - must not have any valid remarkup option, like lang=, counterexample, etc. - must not have content in the same line of the last backticks - must have a known language in our proposed subset If everything is OK, we remove that language from the content since it would be otherwise displayed. Interestingly, this could improve performance when rendering README files or snippets from external websites, since - in case - we do not need to guess the language using our deep dark magic. Closes T15481 Test Plan: We added some nice unit tests. Ensure that this test passes: PhutilRemarkupEngineTestCase::testEngine Optionally, take vision of these, before and after: https://we.phorge.it/P16 Change the test plan slightly every time, to make sure it is not in your cache. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15481 Differential Revision: https://we.phorge.it/D25299 --- .../blockrule/PhutilRemarkupCodeBlockRule.php | 102 +++++++++++++++++- .../PhutilRemarkupEngineTestCase.php | 2 + .../remarkup/tick-block-flavored.txt | 7 ++ .../tick-block-multi-flavored-comment.txt | 18 ++++ .../tick-block-multi-flavored-empty.txt | 9 ++ .../remarkup/tick-block-multi-flavored.txt | 20 ++++ 6 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt create mode 100644 src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt create mode 100644 src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt create mode 100644 src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php index b8fef85b98..8b23fe6541 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php @@ -44,7 +44,18 @@ public function getMatchingLineCount(array $lines, $cursor) { } public function markupText($text, $children) { - if (preg_match('/^\s*```/', $text)) { + // Header/footer eventually useful to be nice with "flavored markdown". + // When it starts with ```stuff the header is 'stuff' (->language) + // When it ends with stuff``` the footer is 'stuff' (->garbage) + $header_line = null; + $footer_line = null; + + $matches = null; + if (preg_match('/^\s*```(.*)/', $text, $matches)) { + if (isset($matches[1])) { + $header_line = $matches[1]; + } + // If this is a ```-style block, trim off the backticks and any leading // blank line. $text = preg_replace('/^\s*```(\s*\n)?/', '', $text); @@ -52,6 +63,13 @@ public function markupText($text, $children) { } $lines = explode("\n", $text); + + // If we have a flavored header, it has sense to look for the footer. + if ($header_line !== null && $lines) { + $footer_line = $lines[last_key($lines)]; + } + + // Strip final empty lines while ($lines && !strlen(last($lines))) { unset($lines[last_key($lines)]); } @@ -65,20 +83,39 @@ public function markupText($text, $children) { $parser = new PhutilSimpleOptions(); $custom = $parser->parse(head($lines)); + $valid_options = null; if ($custom) { - $valid = true; + $valid_options = true; foreach ($custom as $key => $value) { if (!array_key_exists($key, $options)) { - $valid = false; + $valid_options = false; break; } } - if ($valid) { + if ($valid_options) { array_shift($lines); $options = $custom + $options; } } + // Parse flavored markdown strictly to don't eat legitimate Remarkup. + // Proceed only if we tried to parse options and we failed + // (no options also mean no language). + // For example this is not a valid option: ```php + // Proceed only if the footer exists and it is not: blabla``` + // Accept only 2 lines or more. First line: header; then content. + if ( + $valid_options === false && + $header_line !== null && + $footer_line === '' && + count($lines) > 1 + ) { + if (self::isKnownLanguageCode($header_line)) { + array_shift($lines); + $options['lang'] = $header_line; + } + } + // Normalize the text back to a 0-level indent. $min_indent = 80; foreach ($lines as $line) { @@ -249,4 +286,61 @@ private function highlightSource($text, array $options) { $engine->highlightSource($options['lang'], $text))); } + /** + * Check if a language code can be used in a generic flavored markdown. + * @param string $lang Language code + * @return bool + */ + private static function isKnownLanguageCode($lang) { + $languages = self::knownLanguageCodes(); + return isset($languages[$lang]); + } + + /** + * Get the available languages for a generic flavored markdown. + * @return array Languages as array keys. Ignore the value. + */ + private static function knownLanguageCodes() { + // This is a friendly subset from https://pygments.org/languages/ + static $map = array( + 'arduino' => 1, + 'assembly' => 1, + 'awk' => 1, + 'bash' => 1, + 'bat' => 1, + 'c' => 1, + 'cmake' => 1, + 'cobol' => 1, + 'cpp' => 1, + 'css' => 1, + 'csharp' => 1, + 'dart' => 1, + 'delphi' => 1, + 'fortran' => 1, + 'go' => 1, + 'groovy' => 1, + 'haskell' => 1, + 'java' => 1, + 'javascript' => 1, + 'kotlin' => 1, + 'lisp' => 1, + 'lua' => 1, + 'matlab' => 1, + 'make' => 1, + 'perl' => 1, + 'php' => 1, + 'powershell' => 1, + 'python' => 1, + 'r' => 1, + 'ruby' => 1, + 'rust' => 1, + 'scala' => 1, + 'sh' => 1, + 'sql' => 1, + 'typescript' => 1, + 'vba' => 1, + ); + return $map; + } + } diff --git a/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php b/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php index c3b4960d0c..38831d034a 100644 --- a/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php +++ b/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php @@ -2,6 +2,8 @@ /** * Test cases for @{class:PhutilRemarkupEngine}. + * @TODO: This unit is not always triggered when you need it. + * https://we.phorge.it/T15500 */ final class PhutilRemarkupEngineTestCase extends PhutilTestCase { diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt new file mode 100644 index 0000000000..f224942b1d --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt @@ -0,0 +1,7 @@ +```cpp +code +``` +~~~~~~~~~~ +

+~~~~~~~~~~ + code diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt new file mode 100644 index 0000000000..bcdaca8063 --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt @@ -0,0 +1,18 @@ +```#comment +code + +#more comment +more code``` + +~~~~~~~~~~ +
#comment
+code
+
+#more comment
+more code
+~~~~~~~~~~ + #comment + code + + #more comment + more code diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt new file mode 100644 index 0000000000..c05d44ec0f --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt @@ -0,0 +1,9 @@ +``` +cpp +second line``` +~~~~~~~~~~ +
cpp
+second line
+~~~~~~~~~~ + cpp + second line diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt new file mode 100644 index 0000000000..5dc2cef421 --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt @@ -0,0 +1,20 @@ +```cpp +code + +more code + +more code +``` + +~~~~~~~~~~ +
code
+
+more code
+
+more code
+~~~~~~~~~~ + code + + more code + + more code From 5e48e16f77fd918607cb943f1b1f78293bb00cee Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Tue, 4 Jul 2023 16:24:12 +0100 Subject: [PATCH 224/425] Update DiffusionDocumentRenderingEngine to cope with null code coverage in PHP 8.1 Summary: Update DiffusionDocumentRenderingEngine to cope with null code coverage in PHP 8.1 Fixes T15523 Test Plan: https://my.phorge.site/source/myrepo/browse/master/myfile Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15523 Differential Revision: https://we.phorge.it/D25330 --- .../diffusion/document/DiffusionDocumentRenderingEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php b/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php index 800d195726..2c33a82ad5 100644 --- a/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php +++ b/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php @@ -87,7 +87,7 @@ protected function willRenderRef(PhabricatorDocumentRef $ref) { $ref->setSymbolMetadata($this->getSymbolMetadata()); $coverage = $drequest->loadCoverage(); - if (strlen($coverage)) { + if (phutil_nonempty_string($coverage)) { $ref->addCoverage($coverage); } } From 9bf5e1735287f6f4113040fa5eb763e20d94906d Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Tue, 4 Jul 2023 12:01:51 +0100 Subject: [PATCH 225/425] PHP8.1 fix for DiffusionServeController serveRequest() Summary: When a 'git pull' is done to an https git URL, the $_SERVER variables PHP_AUTH_USER and PHP_AUTH_PW will be unset, causing PHP 8.1 to throw strlen(null) errors. This update fixes the issue by defaulting the values to '', which results in $have_user and $have_pass having false values as desired. Fixes T15520 Test Plan: arc unit Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15520 Differential Revision: https://we.phorge.it/D25327 --- src/__phutil_library_map__.php | 2 ++ .../controller/DiffusionServeController.php | 4 ++-- .../DiffusionServeControllerTestCase.php | 21 +++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 09b8b86da4..3de392e025 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1052,6 +1052,7 @@ 'DiffusionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSSHWorkflow.php', 'DiffusionSearchQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php', 'DiffusionServeController' => 'applications/diffusion/controller/DiffusionServeController.php', + 'DiffusionServeControllerTestCase' => 'applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php', 'DiffusionServiceRef' => 'applications/diffusion/ref/DiffusionServiceRef.php', 'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php', 'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php', @@ -7103,6 +7104,7 @@ 'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow', 'DiffusionSearchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 'DiffusionServeController' => 'DiffusionController', + 'DiffusionServeControllerTestCase' => 'PhabricatorTestCase', 'DiffusionServiceRef' => 'Phobject', 'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel', 'DiffusionSetupException' => 'Exception', diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index 89430eef37..93656ea607 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -183,8 +183,8 @@ private function serveRequest(AphrontRequest $request) { // won't prompt users who provide a username but no password otherwise. // See T10797 for discussion. - $have_user = strlen(idx($_SERVER, 'PHP_AUTH_USER')); - $have_pass = strlen(idx($_SERVER, 'PHP_AUTH_PW')); + $have_user = strlen(idx($_SERVER, 'PHP_AUTH_USER', '')); + $have_pass = strlen(idx($_SERVER, 'PHP_AUTH_PW', '')); if ($have_user && $have_pass) { $username = $_SERVER['PHP_AUTH_USER']; $password = new PhutilOpaqueEnvelope($_SERVER['PHP_AUTH_PW']); diff --git a/src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php b/src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php new file mode 100644 index 0000000000..67c5e63a57 --- /dev/null +++ b/src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php @@ -0,0 +1,21 @@ + true, + ); + } + + public function testHandleRequest() { + $aphront_request = new AphrontRequest('example.com', '/'); + $diffusion_serve_controller = new DiffusionServeController(); + + $diffusion_serve_controller->setRequest($aphront_request); + $result = $diffusion_serve_controller->handleRequest($aphront_request); + $this->assertTrue(true, 'handleRequest did not throw an error'); + $this->assertTrue($result instanceof PhabricatorVCSResponse, + 'handleRequest() returns PhabricatorVCSResponse object'); + } + +} From 9d512595c0e2a37bb50097f3494af806b8322d45 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Wed, 5 Jul 2023 14:24:47 +0100 Subject: [PATCH 226/425] Fix PHP 8.1 DifferentialBranchField getBranchDescription strlen(null) error Summary: Fix PHP 8.1 strlen(null) error in DifferentialBranchField getBranchDescription() Fixes T15531 Test Plan: Simply viewing a diff (eg https://my.phorge.site/D1234) on a PHP 8.1 server generated the error fixed by this diff. Also created a unit test. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15531 Differential Revision: https://we.phorge.it/D25335 --- src/__phutil_library_map__.php | 2 + .../customfield/DifferentialBranchField.php | 2 +- .../DifferentialBranchFieldTestCase.php | 39 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 3de392e025..ddee83b5f1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -466,6 +466,7 @@ 'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php', 'DifferentialBlockingReviewerDatasource' => 'applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php', 'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php', + 'DifferentialBranchFieldTestCase' => 'applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php', 'DifferentialBuildableEngine' => 'applications/differential/harbormaster/DifferentialBuildableEngine.php', 'DifferentialChangeDetailMailView' => 'applications/differential/mail/DifferentialChangeDetailMailView.php', 'DifferentialChangeHeraldFieldGroup' => 'applications/differential/herald/DifferentialChangeHeraldFieldGroup.php', @@ -6472,6 +6473,7 @@ 'DifferentialBlockHeraldAction' => 'HeraldAction', 'DifferentialBlockingReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DifferentialBranchField' => 'DifferentialCustomField', + 'DifferentialBranchFieldTestCase' => 'PhabricatorTestCase', 'DifferentialBuildableEngine' => 'HarbormasterBuildableEngine', 'DifferentialChangeDetailMailView' => 'DifferentialMailView', 'DifferentialChangeHeraldFieldGroup' => 'HeraldFieldGroup', diff --git a/src/applications/differential/customfield/DifferentialBranchField.php b/src/applications/differential/customfield/DifferentialBranchField.php index 60291b6be3..9d5d8f81a9 100644 --- a/src/applications/differential/customfield/DifferentialBranchField.php +++ b/src/applications/differential/customfield/DifferentialBranchField.php @@ -45,7 +45,7 @@ private function getBranchDescription(DifferentialDiff $diff) { return pht('%s (bookmark)', $bookmark); } else if (strlen($branch)) { $onto = $diff->loadTargetBranch(); - if (strlen($onto) && ($onto !== $branch)) { + if (phutil_nonempty_string($onto) && ($onto !== $branch)) { return pht( '%s (branched from %s)', $branch, diff --git a/src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php b/src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php new file mode 100644 index 0000000000..e96b4bd245 --- /dev/null +++ b/src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php @@ -0,0 +1,39 @@ + true, + ); + } + + private function getTestDiff() { + $parser = new ArcanistDiffParser(); + $raw_diff = <<parseDiff($raw_diff)); + } + + public function testRenderDiffPropertyViewValue() { + $test_object = new DifferentialBranchField(); + $diff = $this->getTestDiff(); + $diff->setBranch('test'); + $this->assertEqual('test', + $test_object->renderDiffPropertyViewValue($diff)); + } +} From f45d6421a13cd3cbec21b0be5fbedc5c04605d96 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 5 Jul 2023 19:36:56 +0200 Subject: [PATCH 227/425] Fix PHP 8.1 "strlen(null)" exception opening a File overlay (Lightbox comment frame) Summary: The error was caused by the overlay lightbox, since it gives the possibility to share a comment on any file in any general Remarkup text, but that comment textarea is indeed empty as default. `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=T15498, ref.master=a25af8ccef69, ref.T15498=a25af8ccef69) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/files/controller/PhabricatorFileLightboxController.php:23] ``` Closes T15498 Test Plan: Visit a blog post with a file/image dropped in the content and click on the thumbnail. No error overlay dialog is displayed anymore. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15498 Differential Revision: https://we.phorge.it/D25310 --- .../files/controller/PhabricatorFileLightboxController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/files/controller/PhabricatorFileLightboxController.php b/src/applications/files/controller/PhabricatorFileLightboxController.php index 59a826dd42..28123d9002 100644 --- a/src/applications/files/controller/PhabricatorFileLightboxController.php +++ b/src/applications/files/controller/PhabricatorFileLightboxController.php @@ -20,7 +20,7 @@ public function handleRequest(AphrontRequest $request) { return new Aphront404Response(); } - if (strlen($comment)) { + if (phutil_nonempty_string($comment)) { $xactions = array(); $xactions[] = id(new PhabricatorFileTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) From 71f38a90a3582aff885a18a1ae9d3f0ffe766aa4 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 5 Jul 2023 19:40:53 +0200 Subject: [PATCH 228/425] Fix PHP 8.1 "strlen(null)" exception on History page of Diffusion repo after setting maintenance mode Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=master, ref.master=1c098c273d06) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php:28] ``` Closes T15475 Test Plan: Play a bit with maintenance messages: ./bin/repository maintenance --start "Start" R1 ./bin/repository maintenance --start "Start Yeah" R1 ./bin/repository maintenance --stop R1 Then visit `/diffusion/1/manage/history/` and look at `Diffusion put this repository into maintenance mode.` as expected, instead of an exception. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15475 Differential Revision: https://we.phorge.it/D25295 --- .../PhabricatorRepositoryMaintenanceTransaction.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php index caf9e84527..c1259aa983 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php @@ -25,11 +25,14 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (strlen($old) && !strlen($new)) { + $old_nonempty = phutil_nonempty_string($old); + $new_nonempty = phutil_nonempty_string($new); + + if ($old_nonempty && !$new_nonempty) { return pht( '%s took this repository out of maintenance mode.', $this->renderAuthor()); - } else if (!strlen($old) && strlen($new)) { + } else if (!$old_nonempty && $new_nonempty) { return pht( '%s put this repository into maintenance mode.', $this->renderAuthor()); From d5a28e12a005e7082dab685dd6e615da5213db33 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Wed, 5 Jul 2023 13:28:12 -0700 Subject: [PATCH 229/425] Project Hovercards: Show Description Summary: Fixes T15275. Test Plan: Project pages have "Developer > View Hovercard" button. Or hover over a project handle, I guess. Also test for empty description and with the description field disabled. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15275 Differential Revision: https://we.phorge.it/D25331 --- ...ricatorProjectHovercardEngineExtension.php | 20 +++++++++++++++++ .../view/PhabricatorProjectCardView.php | 22 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php b/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php index d2d1f9ab82..ef8964463f 100644 --- a/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php +++ b/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php @@ -28,6 +28,26 @@ public function willRenderHovercards(array $objects) { ->execute(); $projects = mpull($projects, null, 'getPHID'); + $custom_fields = array(); + foreach ($projects as $project) { + $field = PhabricatorCustomField::getObjectField( + $project, + PhabricatorCustomField::ROLE_VIEW, + 'std:project:internal:description'); + if ($field === null) { + // This means the field is disabled, it would always be null. + break; + } + $field + ->setViewer($viewer) + ->readValueFromObject($project); + $custom_fields[] = $field; + } + + id(new PhabricatorCustomFieldStorageQuery()) + ->addFields($custom_fields) + ->execute(); + return array( 'projects' => $projects, ); diff --git a/src/applications/project/view/PhabricatorProjectCardView.php b/src/applications/project/view/PhabricatorProjectCardView.php index a56697ba7e..03d342b666 100644 --- a/src/applications/project/view/PhabricatorProjectCardView.php +++ b/src/applications/project/view/PhabricatorProjectCardView.php @@ -68,6 +68,28 @@ protected function getTagContent() { $description = null; + // This getProxy() feels hacky - see also PhabricatorProjectDatasource:67 + $description_field = PhabricatorCustomField::getObjectField( + $project, + PhabricatorCustomField::ROLE_VIEW, + 'std:project:internal:description'); + + if ($description_field !== null) { + $description_field = $description_field->getProxy(); + + $description = $description_field->getFieldValue(); + if (phutil_nonempty_string($description)) { + $description = PhabricatorMarkupEngine::summarizeSentence($description); + $description = id(new PHUIRemarkupView($viewer, $description)) + ->setContextObject($project); + + $description = phutil_tag( + 'div', + array('class' => 'project-card-body phui-header-shell'), + $description); + } + } + $card = phutil_tag( 'div', array( From b3ac5ceb387fc96e172129e2388080308dcf71f7 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Wed, 5 Jul 2023 09:08:02 +0100 Subject: [PATCH 230/425] Fix DifferentialGetCommitMessageConduitAPIMethod execute strlen(null) Summary: When iterating through the fields of a differential commit, the DifferentialGetCommitMessageConduitAPIMethod execute method explicitly allows a value to be either a string or a null. It then calls strlen upon this possibly null value. We could replace the strlen with phutil_nonempty_string, but as the code has already eliminated variable types other than string or null, it is more efficient to explicitly check for null or '' ``` $value === null or $value == '' ``` Fixes T15527 Test Plan: Run ``` arc diff ``` Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15527 Differential Revision: https://we.phorge.it/D25332 --- .../conduit/DifferentialGetCommitMessageConduitAPIMethod.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php index 51225023ff..a4a2ccc85e 100644 --- a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php @@ -115,7 +115,7 @@ protected function execute(ConduitAPIRequest $request) { $is_title = ($field_key == $key_title); - if (!strlen($value)) { + if ($value === null || $value === '') { if ($is_template) { $commit_message[] = $label.': '; } From cae1246359c64b33eccf61b3fd6ddda6a6101418 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 6 Jul 2023 13:31:00 +0200 Subject: [PATCH 231/425] Dashboard Tabs: render "Move" buttons grayed out more consistently Summary: If you happen to create a Dashboard by retrieving other people's panels, you were probably affected by this visual bug. It seems that you cannot Move things, but you really can (and that is OK). The reason is, to move things in a Parent Panel, you only need edit permission there, and you do not need to also have edit permissions on every Child Panel. Closes T15492 Test Plan: - From user A: - Create two panels - editable by: User A - From user B: - Create a "Tabs Panel" in a Dashboard - Attach the panels from User A (so, editable by, not you) The meaningful "Move" actions should be not disabled anymore. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15492 Differential Revision: https://we.phorge.it/D25308 --- .../dashboard/paneltype/PhabricatorDashboardTabsPanelType.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php index 831229bab1..9bc84d820e 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php @@ -181,7 +181,7 @@ public function renderPanelContent( ->setIcon('fa-chevron-left') ->setHref($prev_uri) ->setWorkflow(true) - ->setDisabled(($prev_key === null) || !$can_edit)); + ->setDisabled($prev_key === null)); $dropdown_menu->addAction( id(new PhabricatorActionView()) @@ -189,7 +189,7 @@ public function renderPanelContent( ->setIcon('fa-chevron-right') ->setHref($next_uri) ->setWorkflow(true) - ->setDisabled(($next_key === null) || !$can_edit)); + ->setDisabled($next_key === null)); $dropdown_menu->addAction( id(new PhabricatorActionView()) From 92861cc1f000f3143be2ba8a0523a0422d1db2d3 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 6 Jul 2023 13:31:20 +0200 Subject: [PATCH 232/425] Dashboard Tabs: fix action "Move Right" Summary: It seems that if you use the "Move Right" action, you can get an unrelated error message about "Move Left": This is already the first tab. It can not move any farther to the left. This happened because there was an incomplete condition. Closes T15493 Test Plan: - visit /dashboard/panel/ and create at least two panels (please, owned by you) - visit a Dashboard, and add a Tabs Panel - take the first Tab: "Move Right" After this change, "Move Right" now always works. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15493 Differential Revision: https://we.phorge.it/D25307 --- .../panel/PhabricatorDashboardPanelTabsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php index 2b36d3f8a2..193e4580d6 100644 --- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php +++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php @@ -327,7 +327,7 @@ private function handleMoveOperation( 'This is already the last tab. It can not move any farther to '. 'the right.')) ->addCancelButton($cancel_uri); - } else if ((string)head_key($old_config) === $target) { + } else if (!$is_next && (string)head_key($old_config) === $target) { return $this->newDialog() ->setTitle(pht('Impossible!')) ->appendParagraph( From 7bebfa289aa18e3ff043ea3a5b00d178ec6756e1 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 6 Jul 2023 13:31:42 +0200 Subject: [PATCH 233/425] Diffusion commits: add some edge phrases about Revisions Summary: This change implements some messages that were only present from this class: DifferentialRevisionHasCommitEdgeType.php But not in its inverse class: DiffusionCommitHasRevisionEdgeType.php In short: | Before | After | |-----------|-----------| | {F313282} | {F313281} | Closes T15314 Test Plan: Visit a Diffusion commit and use the sidebar menu "Edit Related Objects" to: - add 1 revision - add 2 revisions - remove 1 revision - remove 2 revisions - add 1 revision, remove 1 revision - add 2 revisions, remove 2 revisions Check that you don't see phrases like "added an edge" anymore. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15314 Differential Revision: https://we.phorge.it/D25306 --- .../DiffusionCommitHasRevisionEdgeType.php | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php b/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php index ce7a899bda..f59cb8e564 100644 --- a/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php +++ b/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php @@ -25,4 +25,45 @@ public function getConduitDescription() { 'The source commit is associated with the destination revision.'); } + public function getTransactionAddString( + $actor, + $add_count, + $add_edges) { + + return pht( + '%s added %s revision(s): %s.', + $actor, + $add_count, + $add_edges); + } + + public function getTransactionRemoveString( + $actor, + $rem_count, + $rem_edges) { + + return pht( + '%s removed %s revision(s): %s.', + $actor, + $rem_count, + $rem_edges); + } + + public function getTransactionEditString( + $actor, + $total_count, + $add_count, + $add_edges, + $rem_count, + $rem_edges) { + + return pht( + '%s edited revision(s), added %s: %s; removed %s: %s.', + $actor, + $add_count, + $add_edges, + $rem_count, + $rem_edges); + } + } From fb822ba84999cfadf1ce6b53f3133e0603380273 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 10 Jul 2023 12:09:33 +0200 Subject: [PATCH 234/425] Update Facebook icon on Auth Provider page Summary: Closes T15545 Test Plan: Go to `/auth/config/new/` in browser and look at icons. Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15545 Differential Revision: https://we.phorge.it/D25342 --- resources/celerity/map.php | 10 +++++----- resources/sprite/login_1x/Facebook.png | Bin 773 -> 746 bytes resources/sprite/login_2x/Facebook.png | Bin 860 -> 822 bytes resources/sprite/manifest/login.json | 2 +- webroot/rsrc/image/sprite-login-X2.png | Bin 29663 -> 29001 bytes webroot/rsrc/image/sprite-login.png | Bin 13623 -> 13399 bytes 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 0f0ee9d3f4..58d7a61a59 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '8abf4250', + 'core.pkg.css' => 'a4ed0b29', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '525f9a1d', @@ -184,7 +184,7 @@ 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', 'rsrc/css/phui/workboards/phui-workcard.css' => '62056e3b', 'rsrc/css/phui/workboards/phui-workpanel.css' => 'bc06f022', - 'rsrc/css/sprite-login.css' => '0a370010', + 'rsrc/css/sprite-login.css' => '07052ee0', 'rsrc/css/sprite-tokens.css' => 'f1896dc5', 'rsrc/css/syntax/syntax-default.css' => '055fc231', 'rsrc/externals/d3/d3.min.js' => '9d068042', @@ -342,8 +342,8 @@ 'rsrc/image/phrequent_active.png' => 'de66dc50', 'rsrc/image/phrequent_inactive.png' => '79c61baf', 'rsrc/image/resize.png' => '9cc83373', - 'rsrc/image/sprite-login-X2.png' => 'c1458879', - 'rsrc/image/sprite-login.png' => 'f11dad90', + 'rsrc/image/sprite-login-X2.png' => '02896524', + 'rsrc/image/sprite-login.png' => 'e0508107', 'rsrc/image/sprite-tokens-X2.png' => '21621dd9', 'rsrc/image/sprite-tokens.png' => 'bede2580', 'rsrc/image/texture/card-gradient.png' => 'e6892cb4', @@ -906,7 +906,7 @@ 'project-triggers-css' => 'cd9c8bb9', 'project-view-css' => '2f7caa20', 'setup-issue-css' => '5eed85b2', - 'sprite-login-css' => '0a370010', + 'sprite-login-css' => '07052ee0', 'sprite-tokens-css' => 'f1896dc5', 'syntax-default-css' => '055fc231', 'syntax-highlighting-css' => '548567f6', diff --git a/resources/sprite/login_1x/Facebook.png b/resources/sprite/login_1x/Facebook.png index 8c7201b05e1bec48624135498493b7fbdf77f7bd..397046ffba0f118b4c284b041ac548a730973f75 100644 GIT binary patch literal 746 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*s3?yAI>n~$qU{nt932_B-TUY<@?CjjLXV3rt z|79yaNtJ(+EdL|{L&$9VhR;Cpmv8^VNgxBL3$7Rs!=x5!=98CykaYpg2ihoI@d;?P zLd7Sg%1_FbpLD7}=~jO-sQCocWmfmeqVAJr{U;kBYWQT|_}RDZb705kxIQ5Ik}>H^ z=HxHgQ@#|<_|mlK>#U96=56`5@z9S=hkhKn`1AOspI7eux_0mPjr+fEJotV0$seHY zPhb6c21Kv_ym=59qo7?A4v^ zz+loU3GxdD(!h{oV&~-I=H^i}Hjasjjm^r*$<51eZJRUq_MI0mfBg9U=g;52|I(#D zegG=<@pN$vk&t9P$enlDL4@Hz5Omc|rD?~vx*3dMV;K}Y7>i~F z2mZb&B@wI0v@yo(q-Xxtd)paBEgXzZXBvlp*=H z-FF^Gz9U65z5J?Hf8EC0qXrC%d6$=5-D_%K1qmI7f6N;rCi#|X?-T(Ec)I$ztaD0e F0sxVlfu{ff delta 760 zcmVgyT%f7@ ziUFe}Bz!xglj;DmfqQGXeE#1m;mqDe&KDR|F6LmgU6y1y7uFrY9rTjOUwn=p zGkh4l@EC?IK28l2MDJPt5`eRLcbcVu)QNj>tm35(OC~euMK6~V={U)a_7h#`Hr zcIAb-XWfolXg$_$y@n(Pq^=|bk63yF<%H+lGnr{wU8<`+0f#R+UKVi0rWS%Fb z-@HJ94u8~jvemzQk1CIm*Qkscm_z>BL}Mv+{oBtNMlL@oyVV-VEg;ofmUpheP|9)` zsp-fga?Ny@gp6NvMp}cepTOWn$Ix@8Pd-~>NtyZk&Q<&9pgiWJZrl#{`KKmZq!g^- z@WV1Is)$=nvhN(t0!SD08J(Y#%|P8~&QN96esPkEPu8NOI*^&&_#HKXmpSnG?=@o+ zwgMV<$oDYMkONtE)@v9gaR@I{!3;Z?frg^RfQ9}88PKAnOvReUZvGG=Apc-eB4CJ0 q8Umn1zz_jL1kCqwWw%wv|BPQc)XYe6y?zw{0000vV6ukfc|L_0* z|K%z^DOY}$EdK;V5~QQE*M9+ZK}ce>18u}q1`$M)n!EM;?o&UHU;cIL(eLj+|NR4N zw5b1N*YG){^K)p|=ah+Gie`MNoBy?S$=8l$Uni{oHh0Un6??v~-ur#W@gHZd{knGV z_uVId9=-VU{LP=2Z~wme@b}}Ff4_bMP5QrR_RjObaIh;0@(Tt92m>P{7dNkvk+Ge# zi>q5qaYi(nKNh4oxgnT?%jL$K79Q24+7NG-6sLH zFYUs~EC#+?wa9U-9OaqoX1OY38`&AjV>zEAix zck<@lHzoVih5b^(_e9-UF>`KTq`ARI?@TEvlaYZ!8kxM%ROa%>nr*ba6 z!nsKGY=f7eVNg&;LAJbMFz2L&hYE{VPFN7)a%YN%0qgwTc5`duZcdkf|G2uQbxuI* z>z`jz*BeFrUCzYu{r%Bxia9$M^4&Sy!!q$*(}y{}IZ9jR81UR#>)WRBsv(kL{`8b@ zRx_SITR-9W!tLA-ZVK_;ZLMQymb!EJgNxJ~(Bqw_7s3l<;x%AVBNuK8dQm$ED7 z{CxR7=Xb~LSI_xjCnj@0*+u41rYQ zX{t%HSAR$dRW!er(8{~|_LZ=>q=TCd&9J&uGGpS6$6nn$w-qlko@8WYTjILtrybw# x))^0U)K4?&?!25KBWEsE?RoaR1e3YqGwG{4x2P62K3NHhZckS~mvv4FO#o_7qzM23 delta 838 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 Date: Fri, 7 Jul 2023 15:35:32 +0100 Subject: [PATCH 235/425] Fix PHP 8.1 strlen(null) in DiffusionView linkBrowse() Summary: Fix PHP 8.1 strlen(null) in DiffusionView linkBrowse(). Fixes T15542 Test Plan: # Go into a diff (eg https://my.phorge.site/D1234) # Click on the Revision Contents - History tab # Click on a base link (the short git revision ID) Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15542 Differential Revision: https://we.phorge.it/D25340 --- src/applications/diffusion/view/DiffusionView.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/view/DiffusionView.php b/src/applications/diffusion/view/DiffusionView.php index 795b47c773..268514ff53 100644 --- a/src/applications/diffusion/view/DiffusionView.php +++ b/src/applications/diffusion/view/DiffusionView.php @@ -71,7 +71,7 @@ final public function linkBrowse( $display_name = idx($details, 'name'); unset($details['name']); - if (strlen($display_name)) { + if (phutil_nonempty_string($display_name)) { $display_name = phutil_tag( 'span', array( From 85539ce25559b0a4dcfea54562a31d75ae7b27ad Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 12 Jul 2023 22:02:30 +0200 Subject: [PATCH 236/425] Blur profile image when user account disabled Summary: Add blur and sepia filter to disabled user profile image. Before this change, the image was just greyed out but still fully recognizable. Closes T15515 Test Plan: * Create an account with a custom profile picture * Disable the account * Go to user profile of the account * Go to a task with activity of that account and hover over the account to open card view Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15515 Differential Revision: https://we.phorge.it/D25322 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/phui/phui-icon.css | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 58d7a61a59..0cb4208a04 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'a4ed0b29', + 'core.pkg.css' => '0ef1e5d9', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '525f9a1d', @@ -161,7 +161,7 @@ 'rsrc/css/phui/phui-header-view.css' => '36c86a58', 'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0', 'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec', - 'rsrc/css/phui/phui-icon.css' => '4cbc684a', + 'rsrc/css/phui/phui-icon.css' => '084ac612', 'rsrc/css/phui/phui-image-mask.css' => '62c7f4d2', 'rsrc/css/phui/phui-info-view.css' => 'a10a909b', 'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4', @@ -857,7 +857,7 @@ 'phui-hovercard-list' => 'de4b4919', 'phui-hovercard-view-css' => '6ca90fa0', 'phui-icon-set-selector-css' => '7aa5f3ec', - 'phui-icon-view-css' => '4cbc684a', + 'phui-icon-view-css' => '084ac612', 'phui-image-mask-css' => '62c7f4d2', 'phui-info-view-css' => 'a10a909b', 'phui-inline-comment-view-css' => 'a864426f', diff --git a/webroot/rsrc/css/phui/phui-icon.css b/webroot/rsrc/css/phui/phui-icon.css index 5436bb04b1..21097b9232 100644 --- a/webroot/rsrc/css/phui/phui-icon.css +++ b/webroot/rsrc/css/phui/phui-icon.css @@ -41,8 +41,8 @@ a.phui-icon-view:hover { img.phui-image-disabled { opacity: .8; - -webkit-filter: grayscale(100%); - filter: grayscale(100%); + -webkit-filter: blur(4px) grayscale(100%) sepia(25%); + filter: blur(4px) grayscale(100%) sepia(25%); } .phui-icon-view.bluetext { From f7dd8f579a360e7758e9a07ca933106e9f61dcd1 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Thu, 13 Jul 2023 10:23:49 +0100 Subject: [PATCH 237/425] Fix /dashboard/adjust/remove/ PHP 8.1 strlen(null) errors Summary: /dashboard/adjust/remove/ POST requests fail under PHP 8.1 with strlen(null) errors. This change fixes it. Fixes T15549 Test Plan: View a dashboard which has some panels. Eg https://my.phorge.site/dashboard/view/1/ Click on a 'Manage Panel' dropdown and click on 'Remove Panel' Confirm the panel is removed and no strlen(null) errors are generated. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15549 Differential Revision: https://we.phorge.it/D25345 --- .../dashboard/PhabricatorDashboardAdjustController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php index 86a3ff5805..fc04b94603 100644 --- a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php +++ b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php @@ -42,7 +42,7 @@ public function handleRequest(AphrontRequest $request) { } $column_key = $request->getStr('columnKey'); - if (strlen($column_key)) { + if (phutil_nonempty_string($column_key)) { $columns = $ref_list->getColumns(); if (!isset($columns[$column_key])) { return new Aphront404Response(); @@ -52,7 +52,7 @@ public function handleRequest(AphrontRequest $request) { $after_ref = null; $after_key = $request->getStr('afterKey'); - if (strlen($after_key)) { + if (phutil_nonempty_string($after_key)) { $after_ref = $ref_list->getPanelRef($after_key); if (!$after_ref) { return new Aphront404Response(); From 8cbbc9f961079fa44821faf450d450fbae8a7596 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Tue, 4 Jul 2023 15:16:13 +0100 Subject: [PATCH 238/425] Fix strlen(null) in DifferentialChangesetViewController loadCoverage() Summary: DifferentialChangesetViewController loadCoverage() fails under PHP 8.1 when test coverage is provided for some but not all modified files. Update the code to replace strlen() with phutil_nonempty_string(). Fixes T15521 Test Plan: View a diff with test coverage for some but not all files. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15521 Differential Revision: https://we.phorge.it/D25328 --- .../controller/DifferentialChangesetViewController.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php index 0e263d013c..c4722e8d37 100644 --- a/src/applications/differential/controller/DifferentialChangesetViewController.php +++ b/src/applications/differential/controller/DifferentialChangesetViewController.php @@ -451,10 +451,9 @@ private function loadCoverage(DifferentialChangeset $changeset) { continue; } $coverage_data = idx($test_coverage, $changeset->getFileName()); - if (!strlen($coverage_data)) { - continue; + if (phutil_nonempty_string($coverage_data)) { + $coverage[] = $coverage_data; } - $coverage[] = $coverage_data; } if (!$coverage) { From 682fe971a4a907611a4bc51952acef622509eb5e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 17 Jul 2023 12:02:26 +0200 Subject: [PATCH 239/425] Do not crop user avatar image in top bar when no Real Name set Summary: Increase required height for avatar image Patch provided by Stang in https://phabricator.wikimedia.org/T307492 Closes T15552 Test Plan: * Set `/config/edit/user.require-real-name/` to `Make real names optional` * Log in as a user without a real name set * Open the user menu in the top bar and look at the user avatar image Reviewers: O1 Blessed Committers, Cigaryno, avivey Reviewed By: O1 Blessed Committers, Cigaryno, avivey Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15552 Differential Revision: https://we.phorge.it/D25348 --- resources/celerity/map.php | 12 ++++++------ .../rsrc/css/phui/object-item/phui-oi-simple-ui.css | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 0cb4208a04..35001dff29 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '0ef1e5d9', + 'core.pkg.css' => 'e51a98e2', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '525f9a1d', @@ -134,7 +134,7 @@ 'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc', 'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e', 'rsrc/css/phui/object-item/phui-oi-list-view.css' => '9275ff55', - 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '6a30fa46', + 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '9b03a61f', 'rsrc/css/phui/phui-action-list.css' => '1b0085b2', 'rsrc/css/phui/phui-action-panel.css' => '6c386cbf', 'rsrc/css/phui/phui-badge.css' => '666e25ad', @@ -871,7 +871,7 @@ 'phui-oi-drag-ui-css' => 'da15d3dc', 'phui-oi-flush-ui-css' => '490e2e2e', 'phui-oi-list-view-css' => '9275ff55', - 'phui-oi-simple-ui-css' => '6a30fa46', + 'phui-oi-simple-ui-css' => '9b03a61f', 'phui-pager-css' => 'd022c7ad', 'phui-pinboard-view-css' => '1f08f5d8', 'phui-policy-section-view-css' => '139fdc64', @@ -1538,9 +1538,6 @@ '6a18c42e' => array( 'javelin-install', ), - '6a30fa46' => array( - 'phui-oi-list-view-css', - ), '6a85bc5a' => array( 'javelin-behavior', 'javelin-dom', @@ -1791,6 +1788,9 @@ 'javelin-install', 'javelin-util', ), + '9b03a61f' => array( + 'phui-oi-list-view-css', + ), '9c01e364' => array( 'javelin-behavior', 'javelin-dom', diff --git a/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css b/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css index e838f1ce3e..fcf2e7ad1b 100644 --- a/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css +++ b/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css @@ -4,7 +4,7 @@ */ .phui-oi-list-simple .phui-oi-with-image .phui-oi-frame { - min-height: 26px; + min-height: 36px; } .phui-oi-list-simple .phui-oi-image { From 65e57fe23dd30d4d3a7f31b761de3e73b08911cc Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Mon, 3 Jul 2023 17:39:52 +0100 Subject: [PATCH 240/425] Fix PHP 8.1 PhabricatorEditorURIEngine::newForViewer() trim(NULL) error Summary: Under PHP 8.1, PhabricatorEditorURIEngine::newForViewer() is throwing a trim(NULL) error when trying to view a diff. This is because it tries to apply string operations to a user setting which will be null by default. Fixes T15518 Test Plan: Unit test added - arc unit Or just view a diff. Eg: https://my.phorge.site/D1234 Reviewers: O1 Blessed Committers, valerio.bozzolan, avivey Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15518 Differential Revision: https://we.phorge.it/D25324 --- .../editor/PhabricatorEditorURIEngine.php | 2 +- .../PhabricatorEditorURIEngineTestCase.php | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/infrastructure/editor/PhabricatorEditorURIEngine.php b/src/infrastructure/editor/PhabricatorEditorURIEngine.php index d4821c4ffd..76c95dead2 100644 --- a/src/infrastructure/editor/PhabricatorEditorURIEngine.php +++ b/src/infrastructure/editor/PhabricatorEditorURIEngine.php @@ -16,7 +16,7 @@ public static function newForViewer(PhabricatorUser $viewer) { $pattern = $viewer->getUserSetting(PhabricatorEditorSetting::SETTINGKEY); - if (!strlen(trim($pattern))) { + if ($pattern === null || trim($pattern) === '') { return null; } diff --git a/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php b/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php index 43cebff2e6..b6a2b4d8db 100644 --- a/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php +++ b/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php @@ -3,6 +3,12 @@ final class PhabricatorEditorURIEngineTestCase extends PhabricatorTestCase { + protected function getPhabricatorTestCaseConfiguration() { + return array( + self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true, + ); + } + public function testPatternParsing() { $map = array( '' => array(), @@ -129,4 +135,15 @@ public function testPatternProtocols() { } } + public function testNewForViewer() { + $phabricator_user = $this->generateNewTestUser(); + try { + $engine = PhabricatorEditorURIEngine::newForViewer($phabricator_user); + $this->assertTrue(true, 'newForViewer did not throw an error'); + } catch (Throwable $ex) { + $this->assertTrue(false, + 'newForViewer threw an exception:'.$ex->getMessage()); + } + } + } From 8af16246920c5fec42035bdd43a571ca20719f59 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Mon, 17 Jul 2023 11:05:03 +0100 Subject: [PATCH 241/425] Fix passing null to trim() error in DiffusionBrowseTableView render() Summary: When viewing the top level of a diffusion repository, $request->getPath() returns null, which then causes trim() to throw an error. Fix is to update DiffusionRequest getPath() such that it returns '' instead of null, thus fixing it's return type. Fixes T15522 Test Plan: Go to https://my.phorge.site/source/myrepo Reviewers: O1 Blessed Committers, valerio.bozzolan, avivey Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15522 Differential Revision: https://we.phorge.it/D25329 --- src/applications/diffusion/request/DiffusionRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php index 4bcb4d3bc5..c301b89b28 100644 --- a/src/applications/diffusion/request/DiffusionRequest.php +++ b/src/applications/diffusion/request/DiffusionRequest.php @@ -254,7 +254,7 @@ public function setPath($path) { } public function getPath() { - return $this->path; + return coalesce($this->path, ''); } public function getLine() { From 849e18ccbbea99643f51a8ee300446a42f61b418 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Wed, 5 Jul 2023 15:33:02 +0100 Subject: [PATCH 242/425] Fix PhabricatorRepository generateURI PHP 8.1 strlen(null) errors Summary: When viewing a repository in Diffusion, clicking on a folder (eg https://my.phorge.site/source/myrepo/browse/master/myfolder/) will generate multiple strlen(null) exceptions under PHP 8.1 Fix is to replace all the strlen() calls with phutil_nonempty_string() Fixes T15532 Test Plan: View a folder in a repo in Diffusion. Eg https://my.phorge.site/source/myrepo/browse/master/myfolder/ Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15532 Differential Revision: https://we.phorge.it/D25336 --- .../repository/storage/PhabricatorRepository.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index eb57b31b57..4a1c923622 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -779,15 +779,15 @@ public function generateURI(array $params) { break; case 'compare': $uri = $this->getPathURI("/{$action}/"); - if (strlen($head)) { + if (phutil_nonempty_scalar($head)) { $query['head'] = $head; - } else if (strlen($raw_commit)) { + } else if (phutil_nonempty_scalar($raw_commit)) { $query['commit'] = $raw_commit; - } else if (strlen($raw_branch)) { + } else if (phutil_nonempty_scalar($raw_branch)) { $query['head'] = $raw_branch; } - if (strlen($against)) { + if (phutil_nonempty_scalar($against)) { $query['against'] = $against; } break; From dbc101ca8ae1f766a099471de0a39337307e87cd Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Mon, 17 Jul 2023 13:02:17 +0100 Subject: [PATCH 243/425] Fix PHP 8.1 substr(null) in People > Activity Logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: When someone tries to log on with invalid credentials, a record is inserted in the phabricator_user.user_log table with action = login-fail. The session value of this record is set to null in this case. When you open the People → Activity Logs screen, you will see an exception because of this. Fixes T15555 Test Plan: Go to https://my.phorge.site/people/logs/ after a login failure and a login success. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15555 Differential Revision: https://we.phorge.it/D25351 --- src/applications/people/view/PhabricatorUserLogView.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/people/view/PhabricatorUserLogView.php b/src/applications/people/view/PhabricatorUserLogView.php index cf728af973..ef4fa6f10c 100644 --- a/src/applications/people/view/PhabricatorUserLogView.php +++ b/src/applications/people/view/PhabricatorUserLogView.php @@ -36,7 +36,7 @@ public function render() { $rows = array(); foreach ($logs as $log) { - $session = substr($log->getSession(), 0, 6); + $session = substr(coalesce($log->getSession(), ''), 0, 6); $actor_phid = $log->getActorPHID(); $user_phid = $log->getUserPHID(); From 27fa498966222482996a3b1efb0e0b1910e4145e Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Mon, 17 Jul 2023 13:25:32 +0100 Subject: [PATCH 244/425] Fix PHP 8.1 strlen(null) error in PhabricatorFile::newChunkedFile() Summary: Fix PHP 8.1 strlen(null) error in PhabricatorFile::newChunkedFile(). Fixes T15499 Test Plan: Added a unit test which replicated the fault, so you can test simply by 'arc unit' Alternatively, see test in T15499 Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15499 Differential Revision: https://we.phorge.it/D25352 --- src/applications/files/storage/PhabricatorFile.php | 2 +- .../files/storage/__tests__/PhabricatorFileTestCase.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index 44e35c6b72..6782a5a90e 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -287,7 +287,7 @@ public static function newChunkedFile( // NOTE: Once we receive the first chunk, we'll detect its MIME type and // update the parent file if a MIME type hasn't been provided. This matters // for large media files like video. - $mime_type = idx($params, 'mime-type'); + $mime_type = idx($params, 'mime-type', ''); if (!strlen($mime_type)) { $file->setMimeType('application/octet-stream'); } diff --git a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php index 21cb2daf11..21ad929c3f 100644 --- a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php +++ b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php @@ -532,4 +532,11 @@ public function testFileTransformDelete() { $this->assertEqual(array(), $alternate_c); } + public function testNewChunkedFile() { + $engine = new PhabricatorTestStorageEngine(); + $file = PhabricatorFile::newChunkedFile($engine, 10, []); + $this->assertTrue($file instanceof PhabricatorFile, + pht('newChunkedFile returns a PhabricatorFile')); + } + } From cb9fcf18d5c33ebce1ab20d9115967e46f0cc205 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Mon, 17 Jul 2023 14:39:42 +0100 Subject: [PATCH 245/425] Fix PHP 8.1 "strlen(null)" exception trying to stop maintenance mode of Diffusion repo Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Use `isset` instead to check if the argument "start" was passed to the CLI command. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=97e163187418), phorge(head=repoMaintStartHistory, ref.master=1c098c273d06, ref.repoMaintStartHistory=0a4a34143528) #0 PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php:45] ``` Closes T15476 Test Plan: After applying this change, executing `./bin/repository maintenance --stop R1` on the CLI shows `Took repository "R1" out of maintenance mode.` as expected, instead of an exception. Reviewers: O1 Blessed Committers, valerio.bozzolan, aklapper, avivey Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey Subscribers: Sten, avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15476 Differential Revision: https://we.phorge.it/D25296 --- .../PhabricatorRepositoryManagementMaintenanceWorkflow.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php index 6c2f64b31c..513891c5c6 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php @@ -42,7 +42,7 @@ public function execute(PhutilArgumentParser $args) { } $message = $args->getArg('start'); - $is_start = (bool)strlen($message); + $is_start = $message !== null; $is_stop = $args->getArg('stop'); if (!$is_start && !$is_stop) { From 0dfc84e2abef6d14d51de5da63c22a023b8130dd Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 19 Jul 2023 21:27:06 +0200 Subject: [PATCH 246/425] Ponder Answer Comments: indent textarea as Comments Summary: In a Ponder Answer, when adding a Comment, the textarea indentation was a a little bit unintuitive. After this change, the textarea is aligned as a Comment: | Before | After | |-----------|-----------| | {F286220} | {F286221} | Other changes are welcome but this seems to me a good minimum. I also hope this could help not to confuse the Comment field with the Answer field. Closes T15350 Test Plan: - create a Ponder Question - create an Answer - look at the Comments input field that now should be more nicely indented - the normal Response field is not changed at all - the mobile view is not changed at all - click on "Pin Form On Screen" and it must be as before Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15350 Differential Revision: https://we.phorge.it/D25192 --- resources/celerity/map.php | 4 ++-- webroot/rsrc/css/application/ponder/ponder-view.css | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 35001dff29..29bc9d9eb8 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -100,7 +100,7 @@ 'rsrc/css/application/policy/policy-edit.css' => '8794e2ed', 'rsrc/css/application/policy/policy-transaction-detail.css' => 'c02b8384', 'rsrc/css/application/policy/policy.css' => 'ceb56a08', - 'rsrc/css/application/ponder/ponder-view.css' => '05a09d0a', + 'rsrc/css/application/ponder/ponder-view.css' => 'b04bbaff', 'rsrc/css/application/project/project-card-view.css' => 'c1200da7', 'rsrc/css/application/project/project-triggers.css' => 'cd9c8bb9', 'rsrc/css/application/project/project-view.css' => '2f7caa20', @@ -901,7 +901,7 @@ 'policy-css' => 'ceb56a08', 'policy-edit-css' => '8794e2ed', 'policy-transaction-detail-css' => 'c02b8384', - 'ponder-view-css' => '05a09d0a', + 'ponder-view-css' => 'b04bbaff', 'project-card-view-css' => 'c1200da7', 'project-triggers-css' => 'cd9c8bb9', 'project-view-css' => '2f7caa20', diff --git a/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index 966fb6a1fb..567d7ae99b 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -44,10 +44,17 @@ margin-top: 16px; } +/* Indent comments so that are more related to the answer */ .device-desktop .ponder-answer-view .phui-timeline-view { margin-left: 32px; } +/* Indent input box so that it's more related to answer's comments */ +.device-desktop .ponder-answer-view +.phui-comment-form-view:not(.remarkup-assist-pinned) { + margin-left: 94px; +} + .ponder-answer-view .phui-header-subheader { display: inline; margin-left: 12px; From 0f07c07b3bc3f412e591128780e8e969b7382897 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 22 Jul 2023 13:52:13 +0200 Subject: [PATCH 247/425] Attach Diff to Revision: fix Repository asked twice Summary: If you manually upload a Diff (patch) from the Differential web interface you can select the Repository. We fix the default value of the Repository field during creation mode, guessing the repo from the potentially already-available diff. Closes T15537 Test Plan: - Create a cute Differential Revision from web - Pick a Repository > Continue - Create a new Revision > Continue At this point you should still have that repository instead of nothing. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15537 Differential Revision: https://we.phorge.it/D25338 --- .../editor/DifferentialRevisionEditEngine.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/applications/differential/editor/DifferentialRevisionEditEngine.php b/src/applications/differential/editor/DifferentialRevisionEditEngine.php index 995179a864..bd1cee7c26 100644 --- a/src/applications/differential/editor/DifferentialRevisionEditEngine.php +++ b/src/applications/differential/editor/DifferentialRevisionEditEngine.php @@ -189,7 +189,7 @@ protected function buildCustomEditFields($object) { // Don't show the "Author" field when creating a revision using the web // workflow, since it adds more noise than signal to this workflow. - if ($this->getIsCreate()) { + if ($is_create) { $author_field->setIsHidden(true); } @@ -239,6 +239,12 @@ protected function buildCustomEditFields($object) { ->setConduitTypeDescription(pht('New reviewers.')) ->setValue($object->getReviewerPHIDsForEdit()); + // Prefill Repository for example when coming from "Attach To". + $repository_phid = $object->getRepositoryPHID(); + if ($is_create && !$repository_phid && $diff) { + $repository_phid = $diff->getRepositoryPHID(); + } + $fields[] = id(new PhabricatorDatasourceEditField()) ->setKey('repositoryPHID') ->setLabel(pht('Repository')) @@ -248,7 +254,7 @@ protected function buildCustomEditFields($object) { ->setDescription(pht('The repository the revision belongs to.')) ->setConduitDescription(pht('Change the repository for this revision.')) ->setConduitTypeDescription(pht('New repository.')) - ->setSingleValue($object->getRepositoryPHID()); + ->setSingleValue($repository_phid); // This is a little flimsy, but allows "Maniphest Tasks: ..." to continue // working properly in commit messages until we fully sort out T5873. From bf025c5fb491be1d20de1d6ac16ccf32c3c69c2b Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Mon, 24 Jul 2023 00:41:16 -0700 Subject: [PATCH 248/425] Aphlict - search for default config in the right place Summary: Fix T15502. Test Plan: Renamed `phorge` dir few times, run `./bin/aphlict status` to see message. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15502 Differential Revision: https://we.phorge.it/D25344 --- .../management/PhabricatorAphlictManagementWorkflow.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php b/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php index 3d1a26c91d..16bdc69bc1 100644 --- a/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php +++ b/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php @@ -30,16 +30,16 @@ protected function parseLaunchArguments(PhutilArgumentParser $args) { $full_path = Filesystem::resolvePath($config_file); $show_path = $full_path; } else { - $root = dirname(dirname(phutil_get_library_root('phabricator'))); + $root = dirname(phutil_get_library_root('phorge')); $try = array( - 'phabricator/conf/aphlict/aphlict.custom.json', - 'phabricator/conf/aphlict/aphlict.default.json', + 'conf/aphlict/aphlict.custom.json', + 'conf/aphlict/aphlict.default.json', ); foreach ($try as $config) { $full_path = $root.'/'.$config; - $show_path = $config; + $show_path = '/'.$config; if (Filesystem::pathExists($full_path)) { break; } From 1e3c9bdfee76b79389104fe00b5f36ad622d0236 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 25 Jul 2023 14:12:46 +0200 Subject: [PATCH 249/425] Update Diff: fix dead-end when not yours Summary: Fix "Update Patch" for generic Differential revisions. You can now help a coworker to update their patch, from the web. So, if you visit Diff 111 (not yours) and you click "Update Diff", and you paste a valid diff, you arrive here: https://we.phorge.it/differential/diff/222/?revisionID=111 That page as default was suggesting some Diffs that are yours. Plus, now it shows Diff 111. Omitting the Diff from which the workflow was started made no sense and it's certainly an oversight. Any follow-up change is welcome to modernize the selector using AphrontFormTokenizerControl. Closes T15538 Test Plan: - create a Diff on Differential with user A - click on Update Diff from user B (pasting a valid diff) and Continue Now that patch is selected (and selectable). No dead-end anymore. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15538 Differential Revision: https://we.phorge.it/D25337 --- .../differential/controller/DifferentialDiffViewController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/applications/differential/controller/DifferentialDiffViewController.php b/src/applications/differential/controller/DifferentialDiffViewController.php index e56eb27d99..ff6623efe3 100644 --- a/src/applications/differential/controller/DifferentialDiffViewController.php +++ b/src/applications/differential/controller/DifferentialDiffViewController.php @@ -197,7 +197,6 @@ private function loadSelectableRevisions( if (empty($revisions[$selected_id])) { $selected = id(new DifferentialRevisionQuery()) ->setViewer($viewer) - ->withAuthors(array($viewer->getPHID())) ->withIDs(array($selected_id)) ->requireCapabilities( array( From 1aa532a4768553d3cdba53858fb86ffea371b933 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 28 Jul 2023 19:01:44 +0200 Subject: [PATCH 250/425] Workboard Columns: avoid page reload on Edit > Cancel Summary: The rationale for this change is that reloading a page unnecessarily wastes resources that we can save. This includes human resources (time) but also computational resources as well. We probably save a fawn for every 1,000 clicks on that button. A message to Greta Thunberg: now Phorge is with you. This is the involved "Cancel" button, that does not reload the page anymore: {F309894} Closes T15471 Test Plan: Workboard > Column > Edit > Cancel: The popup closes gracefully instead of a page refresh. Also try opening the workflow in another tab (/project/board/1/hide/2/) > Cancel: You turn back to your Workboard just as before. Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15471 Differential Revision: https://we.phorge.it/D25302 --- .../controller/PhabricatorProjectColumnHideController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/applications/project/controller/PhabricatorProjectColumnHideController.php b/src/applications/project/controller/PhabricatorProjectColumnHideController.php index 254beab78c..09217064e2 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnHideController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnHideController.php @@ -136,7 +136,6 @@ public function handleRequest(AphrontRequest $request) { ->setWidth(AphrontDialogView::WIDTH_FORM) ->setTitle($title) ->appendChild($body) - ->setDisableWorkflowOnCancel(true) ->addCancelButton($view_uri) ->addSubmitButton($button); From 189a5d60478e097ef4f65c8bcf1704bfd8e5b774 Mon Sep 17 00:00:00 2001 From: c7574226 Date: Fri, 28 Jul 2023 18:22:42 +0100 Subject: [PATCH 251/425] Fix strlen(null) PHP 8.1 error when pushing into a fresh repository Summary: Doing a git push into a fresh repository over https fails with a PHP 8.1 strlen(null) error. This fixes it. Fixes T15569 Test Plan: * Create an empty repository on a PHP 8.1 Phorge instance * Clone it * Add some data * git push Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15569 Differential Revision: https://we.phorge.it/D25361 --- src/applications/diffusion/engine/DiffusionCommitHookEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index c172f27466..e4e8fd2293 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -1133,7 +1133,7 @@ private function newPushEvent() { ->setHookWait(phutil_microseconds_since($hook_start)); $identifier = $this->getRequestIdentifier(); - if (strlen($identifier)) { + if ($identifier !== null && strlen($identifier)) { $event->setRequestIdentifier($identifier); } From fa687b4ba0116593f502ffd3f2bfdadf55859240 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 29 Jul 2023 12:12:49 +0300 Subject: [PATCH 252/425] Always ellipsize long filename in file upload dialog Summary: Firefox does not ellipsize long filenames after selecting them in the File Upload dialog; Chromium does (for unknown reasons). Could hardcode the "remaining" width for the `` element itself (approx. 340px) based on calculating the CSS widths and margins of all surrounding elements but that is error-prone if CSS for one of those surrounding elements ever changed. Thus instead use `max-width: stretch` for the `` element itself. Per its limited support (see https://caniuse.com/?search=max-width), set also `-moz-available` (https://bugzilla.mozilla.org/show_bug.cgi?id=1495868) and `-webkit-fill-available` (see https://bugs.chromium.org/p/chromium/issues/detail?id=611857) aliases. Closes T15553 Test Plan: * Both in Firefox and in Chromium, go to an existing task, select "File Upload", select a file with a long name. * See that the filename is now correctly ellipsized in Firefox. * See no changes in Chromium. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15553 Differential Revision: https://we.phorge.it/D25347 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/phui/phui-form-view.css | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 29bc9d9eb8..17a93021a4 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'e51a98e2', + 'core.pkg.css' => '67a5ecf3', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '525f9a1d', @@ -154,7 +154,7 @@ 'rsrc/css/phui/phui-document.css' => '52b748a5', 'rsrc/css/phui/phui-feed-story.css' => 'a0c05029', 'rsrc/css/phui/phui-fontkit.css' => '1ec937e5', - 'rsrc/css/phui/phui-form-view.css' => '7536aef9', + 'rsrc/css/phui/phui-form-view.css' => '57edecb7', 'rsrc/css/phui/phui-form.css' => 'd1adb52c', 'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed', 'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', @@ -849,7 +849,7 @@ 'phui-font-icon-base-css' => '303c9b87', 'phui-fontkit-css' => '1ec937e5', 'phui-form-css' => 'd1adb52c', - 'phui-form-view-css' => '7536aef9', + 'phui-form-view-css' => '57edecb7', 'phui-formation-view-css' => 'd2dec8ed', 'phui-head-thing-view-css' => 'd7f293df', 'phui-header-view-css' => '36c86a58', diff --git a/webroot/rsrc/css/phui/phui-form-view.css b/webroot/rsrc/css/phui/phui-form-view.css index 47f7d1bb2c..0c5331551e 100644 --- a/webroot/rsrc/css/phui/phui-form-view.css +++ b/webroot/rsrc/css/phui/phui-form-view.css @@ -63,6 +63,11 @@ color:{$greytext} !important; } +.aphront-form-input > input[type="file"] { + max-width: stretch; + max-width: -moz-available; + max-width: -webkit-fill-available; +} .aphront-form-error { width: 18%; From 1802ebd2ccc1d8beb1594d3becb57b69f9c1a1aa Mon Sep 17 00:00:00 2001 From: sten Date: Sat, 29 Jul 2023 12:51:00 +0100 Subject: [PATCH 253/425] Fix PHP 8.1 Diffusion history errors Summary: Fix multiple PHP 8.1 errors when viewing Diffusion history. Fixes T15573 Test Plan: View a diffusion history page. Eg https://my.php81.phorge.site/source/myrepo/history/master/ Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15573 Differential Revision: https://we.phorge.it/D25366 --- .../diffusion/controller/DiffusionHistoryController.php | 9 ++++----- .../diffusion/query/pathid/DiffusionPathIDQuery.php | 3 +++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionHistoryController.php b/src/applications/diffusion/controller/DiffusionHistoryController.php index fb35d9b6ad..eeb8f13118 100644 --- a/src/applications/diffusion/controller/DiffusionHistoryController.php +++ b/src/applications/diffusion/controller/DiffusionHistoryController.php @@ -50,7 +50,7 @@ public function handleRequest(AphrontRequest $request) { // ancestors appropriately, but this would currrently be prohibitively // expensive in the general case. - $show_graph = !strlen($drequest->getPath()); + $show_graph = !phutil_nonempty_string($drequest->getPath()); if ($show_graph) { $history_list ->setParents($history_results['parents']) @@ -98,11 +98,10 @@ private function buildHeader(DiffusionRequest $drequest) { $viewer = $this->getViewer(); $repository = $drequest->getRepository(); - $no_path = !strlen($drequest->getPath()); - if ($no_path) { - $header_text = pht('History'); - } else { + if (phutil_nonempty_string($drequest->getPath())) { $header_text = $this->renderPathLinks($drequest, $mode = 'history'); + } else { + $header_text = pht('History'); } $header = id(new PHUIHeaderView()) diff --git a/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php b/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php index 7c9e721431..d994410034 100644 --- a/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php +++ b/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php @@ -48,6 +48,9 @@ public function loadPathIDs() { */ public static function normalizePath($path) { + // Ensure we have a string, not a null. + $path = coalesce($path, ''); + // Normalize to single slashes, e.g. "///" => "/". $path = preg_replace('@[/]{2,}@', '/', $path); From 007e7621f2988b3959af780ecd90fc5dedea4f37 Mon Sep 17 00:00:00 2001 From: sten Date: Sat, 29 Jul 2023 12:17:05 +0100 Subject: [PATCH 254/425] Fix HarbormasterBuildUnitMessage PHP 8.1 strlen(null) error Summary: Fix HarbormasterBuildUnitMessage PHP 8.1 strlen(null) error. Fixes T15572 Test Plan: Do an 'arc diff' to a PHP 8.1 Phorge server when doing so will trigger unit tests. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15572 Differential Revision: https://we.phorge.it/D25365 --- .../harbormaster/storage/build/HarbormasterBuildUnitMessage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php b/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php index f2632153c3..0f373babf1 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php @@ -102,7 +102,7 @@ public static function newFromDictionary( $obj->setDuration((float)idx($dict, 'duration')); $path = idx($dict, 'path'); - if (strlen($path)) { + if ($path !== null && strlen($path)) { $obj->setProperty('path', $path); } From 4cedb928aa2032c92e5ef509a3541b191b9d88f6 Mon Sep 17 00:00:00 2001 From: sten Date: Sat, 29 Jul 2023 17:36:01 +0100 Subject: [PATCH 255/425] Fix arc patch PHP 8.1 strlen(null) error Summary: Fix arc patch PHP 8.1 strlen(null) error. When doing an 'arc patch Dxxxx' against a diff on a PHP 8.1 Phorge server we get a strlen(null) error. Updating the idx call to provide a default empty string resolves it. Providing a default empty string to Fixes T15571 Test Plan: arc patch Dxxxx Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15571 Differential Revision: https://we.phorge.it/D25364 --- .../conduit/controller/PhabricatorConduitAPIController.php | 2 +- .../conduit/DifferentialGetCommitMessageConduitAPIMethod.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index 208ee81d53..994f282f44 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -289,7 +289,7 @@ private function authenticateUser( ); } - $token_string = idx($metadata, 'token'); + $token_string = idx($metadata, 'token', ''); if (strlen($token_string)) { if (strlen($token_string) != 32) { diff --git a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php index a4a2ccc85e..7f659ab729 100644 --- a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php @@ -56,7 +56,7 @@ protected function execute(ConduitAPIRequest $request) { // show "Field:" templates for some fields even if they are empty. $edit_mode = $request->getValue('edit'); - $is_any_edit = (bool)strlen($edit_mode); + $is_any_edit = (bool)$edit_mode; $is_create = ($edit_mode == 'create'); $field_list = DifferentialCommitMessageField::newEnabledFields($viewer); From df3ac50cb74215d19238019fc8cfb3bab27533d8 Mon Sep 17 00:00:00 2001 From: Benjamin Kausch Date: Mon, 19 Jun 2023 09:13:25 +0200 Subject: [PATCH 256/425] Implements a more informative hovercard for wiki documents Summary: The current hovercard of a wiki document has no further information except the title. This commit adds object type, project tags, parent documents, last author and last edited time to the card. Preview: {F313614} Preview in a pessimistic case: {F325478} Closes T15433 Test Plan: Edit a wiki document with/without project tags and parent documents and see the hovercard in the feed. Reviewers: O1 Blessed Committers, valerio.bozzolan, avivey, Cigaryno Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey, Cigaryno Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15433 Differential Revision: https://we.phorge.it/D25303 --- resources/celerity/map.php | 4 +- src/__phutil_library_map__.php | 2 + .../PhrictionHovercardEngineExtension.php | 169 ++++++++++++++++++ webroot/rsrc/css/phui/phui-hovercard.css | 21 +++ 4 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 17a93021a4..e31d56d36f 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -159,7 +159,7 @@ 'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed', 'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', 'rsrc/css/phui/phui-header-view.css' => '36c86a58', - 'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0', + 'rsrc/css/phui/phui-hovercard.css' => '39fd2e14', 'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec', 'rsrc/css/phui/phui-icon.css' => '084ac612', 'rsrc/css/phui/phui-image-mask.css' => '62c7f4d2', @@ -855,7 +855,7 @@ 'phui-header-view-css' => '36c86a58', 'phui-hovercard' => '6199f752', 'phui-hovercard-list' => 'de4b4919', - 'phui-hovercard-view-css' => '6ca90fa0', + 'phui-hovercard-view-css' => '39fd2e14', 'phui-icon-set-selector-css' => '7aa5f3ec', 'phui-icon-view-css' => '084ac612', 'phui-image-mask-css' => '62c7f4d2', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index ddee83b5f1..804290d0d4 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -5644,6 +5644,7 @@ 'PhrictionEditEngineController' => 'applications/phriction/controller/PhrictionEditEngineController.php', 'PhrictionHistoryConduitAPIMethod' => 'applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php', 'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php', + 'PhrictionHovercardEngineExtension' => 'applications/phriction/engineextension/PhrictionHovercardEngineExtension.php', 'PhrictionInfoConduitAPIMethod' => 'applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php', 'PhrictionListController' => 'applications/phriction/controller/PhrictionListController.php', 'PhrictionMarkupPreviewController' => 'applications/phriction/controller/PhrictionMarkupPreviewController.php', @@ -12545,6 +12546,7 @@ 'PhrictionEditEngineController' => 'PhrictionController', 'PhrictionHistoryConduitAPIMethod' => 'PhrictionConduitAPIMethod', 'PhrictionHistoryController' => 'PhrictionController', + 'PhrictionHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 'PhrictionInfoConduitAPIMethod' => 'PhrictionConduitAPIMethod', 'PhrictionListController' => 'PhrictionController', 'PhrictionMarkupPreviewController' => 'PhabricatorController', diff --git a/src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php b/src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php new file mode 100644 index 0000000000..14e63b9521 --- /dev/null +++ b/src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php @@ -0,0 +1,169 @@ + $this->getProjectHandlesOfDocuments($objects), + 'ancestors' => $this->getAncestorHandlesOfDocuments($objects), + ); + } + + public function renderHovercard( + PHUIHovercardView $hovercard, + PhabricatorObjectHandle $handle, + $object, + $data) { + + $viewer = $this->getViewer(); + $phid = $object->getPHID(); + + $detail_content = array( + id(new PHUIIconView())->setIcon('fa-book'), + ); + + $ancestor_handles = $data['ancestors'][$phid]; + if ($ancestor_handles) { + foreach ($ancestor_handles as $ancestor_handle) { + $detail_content[] = phutil_tag( + 'a', + array( + 'href' => $ancestor_handle->getUri(), + ), + $ancestor_handle->getName()); + + $detail_content[] = id(new PHUIIconView()) + ->setIcon('fa-angle-right') + ->addClass('phui-crumb-divider'); + } + array_pop($detail_content); + } else { + $detail_content[] = pht('Wiki Document'); + } + + $project_handles = $data['projects'][$phid]; + if ($project_handles) { + $list = id(new PHUIHandleTagListView()) + ->setHandles($project_handles) + ->setSlim(true) + ->setShowHovercards(false); + + $detail_content[] = $list; + } + + $hovercard->setDetail( + phutil_tag( + 'div', + array( + 'class' => 'phui-hovercard-object-type', + ), + $detail_content)); + + $content = $object->getContent(); + + if ($content) { + $hovercard->addField( + pht('Last Author'), + $viewer->renderHandle($content->getAuthorPHID())); + + $hovercard->addField( + pht('Last Edited'), + phabricator_dual_datetime($content->getDateCreated(), $viewer)); + } + } + + private function getProjectHandlesOfDocuments($documents) { + $viewer = $this->getViewer(); + $project_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; + $project_phids = array(); + $project_map = array(); + + $project_edges = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(mpull($documents, 'getPHID')) + ->withEdgeTypes(array($project_edge_type)) + ->execute(); + + foreach ($project_edges as $document_phid => $edge_types) { + $document_project_phids = array_keys($edge_types[$project_edge_type]); + + $project_map[$document_phid] = array_reverse($document_project_phids); + foreach ($document_project_phids as $project_phid) { + if (!in_array($project_phid, $project_phids, true)) { + $project_phids[] = $project_phid; + } + } + } + + if ($project_phids) { + $project_handles = $viewer->loadHandles($project_phids); + $project_handles = iterator_to_array($project_handles); + $project_handles = mpull($project_handles, null, 'getPHID'); + + foreach ($project_map as $key => $document_project_phids) { + $project_map[$key] = array_select_keys( + $project_handles, + $document_project_phids); + } + } + + return $project_map; + } + + private function getAncestorHandlesOfDocuments($documents) { + $viewer = $this->getViewer(); + $ancestor_slugs = array(); + $ancestor_map = array(); + + foreach ($documents as $document) { + $document_phid = $document->getPHID(); + $document_ancestor_slugs = PhabricatorSlug::getAncestry( + $document->getSlug()); + + $ancestor_map[$document_phid] = $document_ancestor_slugs; + foreach ($document_ancestor_slugs as $slug) { + if (!in_array($slug, $ancestor_slugs, true)) { + $ancestor_slugs[] = $slug; + } + } + } + + if ($ancestor_slugs) { + $ancestors = id(new PhrictionDocumentQuery()) + ->setViewer($viewer) + ->withSlugs($ancestor_slugs) + ->execute(); + $ancestor_phids = mpull($ancestors, 'getPHID', 'getSlug'); + $ancestor_handles = $viewer->loadHandles($ancestor_phids); + $ancestor_handles = iterator_to_array($ancestor_handles); + $ancestor_handles = mpull($ancestor_handles, null, 'getPHID'); + + foreach ($ancestor_map as $key => $document_ancestor_slugs) { + $document_ancestor_phids = array_select_keys( + $ancestor_phids, + $document_ancestor_slugs); + $ancestor_map[$key] = array_select_keys( + $ancestor_handles, + $document_ancestor_phids); + } + } + + return $ancestor_map; + } + +} diff --git a/webroot/rsrc/css/phui/phui-hovercard.css b/webroot/rsrc/css/phui/phui-hovercard.css index 876b0b6e53..ba8057d08d 100644 --- a/webroot/rsrc/css/phui/phui-hovercard.css +++ b/webroot/rsrc/css/phui/phui-hovercard.css @@ -115,3 +115,24 @@ .hovercard-task-view .phui-oi-disabled.phui-workcard { background-color: #fff; } + +.phui-hovercard-object-type { + font-size: 12px; +} + +.phui-hovercard-object-type .phui-icon-view { + margin-right: 6px; +} + +.phui-hovercard-object-type .phui-crumb-divider { + margin: 0px 6px; +} + +.phui-hovercard-object-type .phabricator-handle-tag-list { + margin-top: 10px; +} + +.phui-hovercard-object-type .phabricator-handle-tag-list-item { + display: inline-block; + margin: 0 4px 2px 0; +} From 840a7fab2bc8844c52c3fb3b09d3e2afc84e7879 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 1 Aug 2023 09:39:18 +0200 Subject: [PATCH 257/425] Hide Archived Panels from Dashboards Summary: In edit mode, Archived Panels are shown differently, with content text in grey and with the usual red tag. In view mode, Archived Panels are just not rendered. This makes it easier to design Dashboards with individual components that can be de-activated and re-activated on the fly. Closes T15366. {F325637} Test Plan: Have a Dashboard somewhere (e.g in your homepage). Visit a Panel (W-ID) and Archive it. See that now the Panel is not shown anymore on the Dashboard. Edit the Dashboard and you see the Panel again as "Archived". Anything around is just normal. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15366 Differential Revision: https://we.phorge.it/D25368 --- resources/celerity/map.php | 6 +++--- .../PhabricatorDashboardPanelRenderingEngine.php | 15 +++++++++++++++ .../PhabricatorDashboardRenderingEngine.php | 8 +++++++- .../query/PhabricatorDashboardPanelQuery.php | 7 +++++++ webroot/rsrc/css/phui/phui-list.css | 4 ++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index e31d56d36f..3fc6387e77 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '67a5ecf3', + 'core.pkg.css' => '1a5169fe', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '525f9a1d', @@ -167,7 +167,7 @@ 'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4', 'rsrc/css/phui/phui-left-right.css' => '68513c34', 'rsrc/css/phui/phui-lightbox.css' => '4ebf22da', - 'rsrc/css/phui/phui-list.css' => '0c04affd', + 'rsrc/css/phui/phui-list.css' => 'ccf73664', 'rsrc/css/phui/phui-object-box.css' => 'fdffed5c', 'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', @@ -864,7 +864,7 @@ 'phui-invisible-character-view-css' => 'c694c4a4', 'phui-left-right-css' => '68513c34', 'phui-lightbox-css' => '4ebf22da', - 'phui-list-view-css' => '0c04affd', + 'phui-list-view-css' => 'ccf73664', 'phui-object-box-css' => 'fdffed5c', 'phui-oi-big-ui-css' => 'fa74cc35', 'phui-oi-color-css' => 'b517bfa0', diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php index 52e8cc70d5..cce6ee768f 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php @@ -273,6 +273,11 @@ private function renderPanelDiv( ->addClass('dashboard-box') ->addSigil('dashboard-panel'); + // Allow to style Archived Panels differently. + if ($panel && $panel->getIsArchived()) { + $box->addClass('dashboard-panel-disabled'); + } + if ($this->getMovable()) { $box->addSigil('panel-movable'); } @@ -302,6 +307,16 @@ private function renderPanelHeader() { $header = id(new PHUIHeaderView()) ->setHeader($header_text); $header = $this->addPanelHeaderActions($header); + + // If the Panel is Archived, show in edit mode as such. + if ($panel && $panel->getIsArchived()) { + $header->setSubheader( + id(new PHUITagView()) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor(PHUITagView::COLOR_RED) + ->setIcon('fa-ban') + ->setName(pht('Archived'))); + } break; case self::HEADER_MODE_NORMAL: default: diff --git a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php index 9b63fcac55..935e85f8bb 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php @@ -71,6 +71,13 @@ public function renderDashboard() { foreach ($column->getPanelRefs() as $panel_ref) { $panel_phid = $panel_ref->getPanelPHID(); + $panel = idx($panels, $panel_phid); + + // Do not render Archived panels in view mode. + if ($panel && $panel->getIsArchived() && !$is_editable) { + continue; + } + $panel_engine = id(new PhabricatorDashboardPanelRenderingEngine()) ->setViewer($viewer) ->setEnableAsyncRendering(true) @@ -83,7 +90,6 @@ public function renderDashboard() { ->setMovable(true) ->setPanelHandle($handles[$panel_phid]); - $panel = idx($panels, $panel_phid); if ($panel) { $panel_engine->setPanel($panel); } diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php index c67b756262..54f627eb00 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php @@ -19,6 +19,13 @@ public function withPHIDs(array $phids) { return $this; } + /** + * Whether to get only the Archived (`true`), only the not + * Archived (`false`) or all (`null`). Default to `null` (no filter). + * + * @param null|bool $archived + * @return self + */ public function withArchived($archived) { $this->archived = $archived; return $this; diff --git a/webroot/rsrc/css/phui/phui-list.css b/webroot/rsrc/css/phui/phui-list.css index dee105f25b..0289c58fff 100644 --- a/webroot/rsrc/css/phui/phui-list.css +++ b/webroot/rsrc/css/phui/phui-list.css @@ -287,6 +287,10 @@ border-bottom: 1px solid {$thinblueborder}; } +.dashboard-panel-disabled { + color: {$lightgreytext}; +} + /* - Info Stack ------------------------------------------------------------ */ .phui-info-view + .phui-list-view { From 9fe2bfebe8d66dd8a71cb1d1752b52671ff75497 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 4 Aug 2023 17:00:53 +0200 Subject: [PATCH 258/425] Fix PHP 8.1 "strlen(null)" exception adding LDAP authentication Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=788098096e11), phorge(head=master, ref.master=840a7fab2bc8) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php:430] ``` Closes T15587 Test Plan: Go to `/auth/config/edit/?provider=PhabricatorLDAPAuthProvider` and see that "Add Auth Provider: LDAP" page is rendered. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15587 Differential Revision: https://we.phorge.it/D25372 --- src/applications/auth/provider/PhabricatorLDAPAuthProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php index 19f8c625d7..36a83f3678 100644 --- a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php @@ -427,7 +427,7 @@ public function extendEditForm( } $instruction_text = idx($instructions, $key); - if (strlen($instruction_text)) { + if (phutil_nonempty_string($instruction_text)) { $form->appendRemarkupInstructions($instruction_text); } From 39b576b145ae19a0b728240aa2490116d15ad07b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 5 Aug 2023 23:23:05 +0200 Subject: [PATCH 259/425] Add HTML header for Atom/RSS discovery on Phame blog pages Summary: Allow easier discovery and subscribing to Atom feeds of Phame blogs. To be extra cautious, also make sure that the added line is a `PhutilSafeHTML`. Original author: @20after4 Closes T15550 Test Plan: Go to `/phame/blog/view/1/` and check the HTML source code. See an additional `` item with `type="application/atom+xml"` in the `` section. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, 20after4, Cigaryno Maniphest Tasks: T15550 Differential Revision: https://we.phorge.it/D25346 --- .../controller/blog/PhameBlogViewController.php | 9 +++++++++ src/applications/phame/storage/PhameBlog.php | 9 +++++++++ src/view/page/PhabricatorStandardPageView.php | 16 +++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index 758fe955ad..d8b61d8352 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -117,6 +117,15 @@ public function handleRequest(AphrontRequest $request) { $about, )); + $page->addHeadItem(phutil_tag( + 'link', + array( + 'rel' => 'alternate', + 'type' => 'application/atom+xml', + 'href' => $blog->getFeedURI(), + 'title' => $blog->getName(), + ))); + return $page; } diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index 717658ec03..587f5daea3 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -200,6 +200,15 @@ public function getManageURI() { return '/phame/blog/manage/'.$this->getID().'/'; } + /** + * Get relative URI of Phame blog feed. + * + * @return string Relative URI of Phame blog feed + */ + public function getFeedURI() { + return '/phame/blog/feed/'.$this->getID().'/'; + } + public function getProfileImageURI() { return $this->getProfileImageFile()->getBestURI(); } diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index 17ce1db8bb..a81820de3c 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -23,6 +23,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView private $crumbs; private $navigation; private $footer; + private $headItems = array(); public function setShowFooter($show_footer) { $this->showFooter = $show_footer; @@ -375,6 +376,18 @@ protected function willRenderPage() { } + /** + * Insert a HTML element into of the page to render. + * Used by PhameBlogViewController. + * + * @param PhutilSafeHTML HTML header to add + */ + public function addHeadItem($html) { + if ($html instanceof PhutilSafeHTML) { + $this->headItems[] = $html; + } + } + protected function getHead() { $monospaced = null; @@ -407,9 +420,10 @@ protected function getHead() { } return hsprintf( - '%s%s%s', + '%s%s%s%s', parent::getHead(), $font_css, + phutil_implode_html('', $this->headItems), $response->renderSingleResource('javelin-magical-init', 'phabricator')); } From 226f3150933dfd735da17f19147e2648a5f54e63 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 5 Aug 2023 23:28:47 +0200 Subject: [PATCH 260/425] Fix PHP 8.1 "strlen(null)" exception creating Blueprint Working Copy Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement, however use `phutil_nonempty_scalar()` as the value might not necessarily be a string object. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_scalar() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=788098096e11), phorge(head=arcpatch-D25239, ref.master=840a7fab2bc8, ref.arcpatch-D25239=870d62ce0ed9) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php:36] ``` Closes T15581 Test Plan: Page `Create Blueprint` at `/drydock/blueprint/edit/form/default/` renders as expected in web browser. Try also creating a custom integer field and put some fuzzy data. https://we.phorge.it/book/phorge/article/custom_fields/ Test these values: - 1 (stays as-is) - 0 (stays as-is) - 123.45 (becomes 123) - a "lizard" (becomes zero) - empty ("") (becomes empty) Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15581 Differential Revision: https://we.phorge.it/D25371 --- .../customfield/standard/PhabricatorStandardCustomFieldInt.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php index e8a5b99e0d..0ea7233768 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php @@ -33,7 +33,7 @@ public function getValueForStorage() { } public function setValueFromStorage($value) { - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $value = (int)$value; } else { $value = null; From 2da7594344c92ca998f83d25793123baf2c61668 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 10 Aug 2023 17:36:18 +0200 Subject: [PATCH 261/425] Fix a PHP 8.1/8.2 deprecated call to strlen with a NULL argument Summary: This call was preventing notification servers configuration to be properly initialized. Indeed, strlen() was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts phutil_nonempty_string() as a replacement. Fix T15596 Test Plan: Sign in as an administrator, configure the notification server without filling admin path field, you shouldn't get an invalid configuration error Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15596 Differential Revision: https://we.phorge.it/D25381 --- .../config/PhabricatorNotificationServersConfigType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/notification/config/PhabricatorNotificationServersConfigType.php b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php index f13105a249..8fbac5806c 100644 --- a/src/applications/notification/config/PhabricatorNotificationServersConfigType.php +++ b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php @@ -92,7 +92,7 @@ public function validateStoredValue( } $path = idx($spec, 'path'); - if ($type == 'admin' && strlen($path)) { + if ($type == 'admin' && phutil_nonempty_string($path)) { throw $this->newException( pht( 'Notification server configuration describes an invalid host '. From eb2be08f9f0e1656e31f68c2c701bf026c7f5050 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 10 Aug 2023 17:40:31 +0200 Subject: [PATCH 262/425] Fix PHP 8.1/8.2 "strlen(null)" exception in SearchController Summary: strlen() was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts phutil_nonempty_string() as a replacement. EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261]; PHP message: arcanist(head=master, ref.master=6e4947b55f09), phorge(head=master, ref.master=7bebfa289aa1); PHP message: #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/search/controller/PhabricatorSearchController.php:16]; Fix T15595 Test Plan: Search something using the main search bar. It should return something (or not) instead to throwing a RuntimeException. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15595 Differential Revision: https://we.phorge.it/D25380 --- .../search/controller/PhabricatorSearchController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/search/controller/PhabricatorSearchController.php b/src/applications/search/controller/PhabricatorSearchController.php index b3d3ab18fa..336604d228 100644 --- a/src/applications/search/controller/PhabricatorSearchController.php +++ b/src/applications/search/controller/PhabricatorSearchController.php @@ -13,7 +13,7 @@ public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $query = $request->getStr('query'); - if ($request->getStr('jump') != 'no' && strlen($query)) { + if ($request->getStr('jump') != 'no' && phutil_nonempty_string($query)) { $jump_uri = id(new PhabricatorDatasourceEngine()) ->setViewer($viewer) ->newJumpURI($query); From 9c8b9a6bbf048af7a18d027c73b23f874e83bad6 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Fri, 11 Aug 2023 10:37:15 +0100 Subject: [PATCH 263/425] Fix PHP 8.1 trim(null) exception in ./bin/diviner generate Summary: When running './bin/diviner generate' under PHP 8.1, we get a trim(null) exception. Fixes T15548 Test Plan: Run './bin/diviner generate' Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15548 Differential Revision: https://we.phorge.it/D25353 --- src/applications/diviner/atom/DivinerAtom.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diviner/atom/DivinerAtom.php b/src/applications/diviner/atom/DivinerAtom.php index f7f8d2c4ac..93e24d5d03 100644 --- a/src/applications/diviner/atom/DivinerAtom.php +++ b/src/applications/diviner/atom/DivinerAtom.php @@ -346,7 +346,7 @@ public static function newFromDictionary(array $dictionary) { ->setContext(idx($dictionary, 'context')) ->setLanguage(idx($dictionary, 'language')) ->setParentHash(idx($dictionary, 'parentHash')) - ->setDocblockRaw(idx($dictionary, 'docblockRaw')) + ->setDocblockRaw(coalesce(idx($dictionary, 'docblockRaw'), '')) ->setProperties(idx($dictionary, 'properties')); foreach (idx($dictionary, 'warnings', array()) as $warning) { From 4b3c384856daa30aaf0f6e9439ea5c5363995c52 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Fri, 11 Aug 2023 10:38:08 +0100 Subject: [PATCH 264/425] Fix DifferentialCommitMessageField renderFieldValue PHP 8.1 strlen(null) error Summary: arc diff throws strlen(null) error from DifferentialCommitMessageField renderFieldValue when calling a Phorge server running PHP 8.1 Add unit test, which required a new DifferentialTestCommitMessageField class so as to be able to test the abstract DifferentialCommitMessageField class methods. Fixes T15530 Test Plan: Make a change in a git repo with remote a Phorge server running PHP 8.1 Run: ``` arc diff ``` See exception thrown as per T15530 Reviewers: O1 Blessed Committers, valerio.bozzolan, avivey Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15530 Differential Revision: https://we.phorge.it/D25334 --- src/__phutil_library_map__.php | 2 ++ .../field/DifferentialCommitMessageField.php | 2 +- ...DifferentialRevisionIDCommitMessageField.php | 2 +- .../DifferentialTestCommitMessageField.php | 10 ++++++++++ .../DifferentialCommitMessageFieldTestCase.php | 17 +++++++++++++++++ 5 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/applications/differential/field/DifferentialTestCommitMessageField.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 804290d0d4..615985beaa 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -714,6 +714,7 @@ 'DifferentialTabReplacementTestCase' => 'applications/differential/parser/__tests__/DifferentialTabReplacementTestCase.php', 'DifferentialTagsCommitMessageField' => 'applications/differential/field/DifferentialTagsCommitMessageField.php', 'DifferentialTasksCommitMessageField' => 'applications/differential/field/DifferentialTasksCommitMessageField.php', + 'DifferentialTestCommitMessageField' => 'applications/differential/field/DifferentialTestCommitMessageField.php', 'DifferentialTestPlanCommitMessageField' => 'applications/differential/field/DifferentialTestPlanCommitMessageField.php', 'DifferentialTestPlanField' => 'applications/differential/customfield/DifferentialTestPlanField.php', 'DifferentialTitleCommitMessageField' => 'applications/differential/field/DifferentialTitleCommitMessageField.php', @@ -6760,6 +6761,7 @@ 'DifferentialTabReplacementTestCase' => 'PhabricatorTestCase', 'DifferentialTagsCommitMessageField' => 'DifferentialCommitMessageField', 'DifferentialTasksCommitMessageField' => 'DifferentialCommitMessageField', + 'DifferentialTestCommitMessageField' => 'DifferentialCommitMessageField', 'DifferentialTestPlanCommitMessageField' => 'DifferentialCommitMessageField', 'DifferentialTestPlanField' => 'DifferentialCoreCustomField', 'DifferentialTitleCommitMessageField' => 'DifferentialCommitMessageField', diff --git a/src/applications/differential/field/DifferentialCommitMessageField.php b/src/applications/differential/field/DifferentialCommitMessageField.php index ebe10bcf88..55c5ce0dad 100644 --- a/src/applications/differential/field/DifferentialCommitMessageField.php +++ b/src/applications/differential/field/DifferentialCommitMessageField.php @@ -60,7 +60,7 @@ public function readFieldValueFromObject(DifferentialRevision $revision) { } public function renderFieldValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return null; } diff --git a/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php b/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php index ac8ba4ebd4..b39145e9df 100644 --- a/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php +++ b/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php @@ -72,7 +72,7 @@ public function readFieldValueFromConduit($value) { } public function renderFieldValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return null; } diff --git a/src/applications/differential/field/DifferentialTestCommitMessageField.php b/src/applications/differential/field/DifferentialTestCommitMessageField.php new file mode 100644 index 0000000000..9d398bc4fd --- /dev/null +++ b/src/applications/differential/field/DifferentialTestCommitMessageField.php @@ -0,0 +1,10 @@ +assertEqual('foo', $test_object->renderFieldValue('foo'), + 'Normal strings should be rendered unaltered'); + + $this->assertEqual(null, $test_object->renderFieldValue(''), + 'Empty strings should be returned as null'); + + $this->assertEqual(null, $test_object->renderFieldValue(null), + 'null values strings should be returned as null'); + + $test_object = new DifferentialRevisionIDCommitMessageField(); + $expected = 'http://phabricator.example.com/D123'; + $this->assertEqual($expected, $test_object->renderFieldValue('123')); + $this->assertEqual(null, $test_object->renderFieldValue(null)); + } + } From 8310591523f59150184f2ab7bfc271325628b28e Mon Sep 17 00:00:00 2001 From: sten Date: Fri, 11 Aug 2023 10:39:59 +0100 Subject: [PATCH 265/425] Fix DifferentialDiff getFieldValuesForConduit PHP 8.1 strlen(null) errors Summary: Fix DifferentialDiff getFieldValuesForConduit PHP 8.1 strlen(null) errors by replacing the strlen() calls with phutil_nonempty_string(). Also needed to update DifferentialDiffTestCase.php to being a PhabricatorTestCase, as it was throwing an exception prior to any code changes - ``` EXCEPTION (Exception): Trying to read configuration "policy.locked" before configuration has been initialized. ``` Fixes T15529 Test Plan: arc diff Reviewers: O1 Blessed Committers, valerio.bozzolan, avivey Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15529 Differential Revision: https://we.phorge.it/D25333 --- src/__phutil_library_map__.php | 2 +- .../differential/storage/DifferentialDiff.php | 8 ++-- .../__tests__/DifferentialDiffTestCase.php | 38 ++++++++++++++++++- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 615985beaa..83737eea52 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -6565,7 +6565,7 @@ 'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField', 'DifferentialDiffSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'DifferentialDiffSearchEngine' => 'PhabricatorApplicationSearchEngine', - 'DifferentialDiffTestCase' => 'PhutilTestCase', + 'DifferentialDiffTestCase' => 'PhabricatorTestCase', 'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction', 'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'DifferentialDiffViewController' => 'DifferentialController', diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index dfa6d1f791..8f537cff77 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -780,7 +780,7 @@ public function getFieldValuesForConduit() { $refs = array(); $branch = $this->getBranch(); - if (strlen($branch)) { + if (phutil_nonempty_string($branch)) { $refs[] = array( 'type' => 'branch', 'name' => $branch, @@ -788,7 +788,7 @@ public function getFieldValuesForConduit() { } $onto = $this->loadTargetBranch(); - if (strlen($onto)) { + if (phutil_nonempty_string($onto)) { $refs[] = array( 'type' => 'onto', 'name' => $onto, @@ -796,7 +796,7 @@ public function getFieldValuesForConduit() { } $base = $this->getSourceControlBaseRevision(); - if (strlen($base)) { + if ($base !== null && strlen($base)) { $refs[] = array( 'type' => 'base', 'identifier' => $base, @@ -804,7 +804,7 @@ public function getFieldValuesForConduit() { } $bookmark = $this->getBookmark(); - if (strlen($bookmark)) { + if (phutil_nonempty_string($bookmark)) { $refs[] = array( 'type' => 'bookmark', 'name' => $bookmark, diff --git a/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php b/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php index 45547aed42..6959b33e27 100644 --- a/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php +++ b/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php @@ -1,6 +1,12 @@ true, + ); + } public function testDetectCopiedCode() { $copies = $this->detectCopiesIn('lint_engine.diff'); @@ -73,5 +79,35 @@ public function testDetectSlowCopiedCode() { $this->assertTrue(true); } + public function testGetFieldValuesForConduit() { + + $parser = new ArcanistDiffParser(); + $raw_diff = <<parseDiff($raw_diff)); + $this->assertTrue(true); + + $field_values = $diff->getFieldValuesForConduit(); + $this->assertTrue(is_array($field_values)); + foreach (['revisionPHID', 'authorPHID', 'repositoryPHID', 'refs'] + as $key) { + $this->assertTrue(array_key_exists($key, $field_values)); + } + + } } From eb0bb1736891e0d8bfc5bcb5ea16d60b016e58d7 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 11 Aug 2023 11:27:50 -0700 Subject: [PATCH 266/425] Replace dedicated Commit Field with an existing one Summary: fix T15600 Test Plan: arc diff against this Reviewers: Sten, O1 Blessed Committers! Reviewed By: Sten Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15600 Differential Revision: https://we.phorge.it/D25385 --- src/__phutil_library_map__.php | 2 -- .../field/DifferentialTestCommitMessageField.php | 10 ---------- .../DifferentialCommitMessageFieldTestCase.php | 2 +- 3 files changed, 1 insertion(+), 13 deletions(-) delete mode 100644 src/applications/differential/field/DifferentialTestCommitMessageField.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 83737eea52..213b6cf6b1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -714,7 +714,6 @@ 'DifferentialTabReplacementTestCase' => 'applications/differential/parser/__tests__/DifferentialTabReplacementTestCase.php', 'DifferentialTagsCommitMessageField' => 'applications/differential/field/DifferentialTagsCommitMessageField.php', 'DifferentialTasksCommitMessageField' => 'applications/differential/field/DifferentialTasksCommitMessageField.php', - 'DifferentialTestCommitMessageField' => 'applications/differential/field/DifferentialTestCommitMessageField.php', 'DifferentialTestPlanCommitMessageField' => 'applications/differential/field/DifferentialTestPlanCommitMessageField.php', 'DifferentialTestPlanField' => 'applications/differential/customfield/DifferentialTestPlanField.php', 'DifferentialTitleCommitMessageField' => 'applications/differential/field/DifferentialTitleCommitMessageField.php', @@ -6761,7 +6760,6 @@ 'DifferentialTabReplacementTestCase' => 'PhabricatorTestCase', 'DifferentialTagsCommitMessageField' => 'DifferentialCommitMessageField', 'DifferentialTasksCommitMessageField' => 'DifferentialCommitMessageField', - 'DifferentialTestCommitMessageField' => 'DifferentialCommitMessageField', 'DifferentialTestPlanCommitMessageField' => 'DifferentialCommitMessageField', 'DifferentialTestPlanField' => 'DifferentialCoreCustomField', 'DifferentialTitleCommitMessageField' => 'DifferentialCommitMessageField', diff --git a/src/applications/differential/field/DifferentialTestCommitMessageField.php b/src/applications/differential/field/DifferentialTestCommitMessageField.php deleted file mode 100644 index 9d398bc4fd..0000000000 --- a/src/applications/differential/field/DifferentialTestCommitMessageField.php +++ /dev/null @@ -1,10 +0,0 @@ -assertEqual('foo', $test_object->renderFieldValue('foo'), 'Normal strings should be rendered unaltered'); From 10ee019785d5331150c3d83bd3549fc8c9e42fb0 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 11 Aug 2023 20:56:23 +0200 Subject: [PATCH 267/425] Fix PHP 8.1 "strlen(null)" exception setting Passphrase Credential Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=788098096e11), phorge(head=master, ref.master=840a7fab2bc8) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/passphrase/view/PassphraseCredentialControl.php:53] ``` Closes T15580 Test Plan: Set URI for a Diffusion Repository, select URI and select "Set Credential". Page "Edit Repository URI" at `/diffusion/123/uri/edit/456/` renders as expected in web browser. Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15580 Differential Revision: https://we.phorge.it/D25370 --- .../passphrase/view/PassphraseCredentialControl.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/applications/passphrase/view/PassphraseCredentialControl.php b/src/applications/passphrase/view/PassphraseCredentialControl.php index 8ba1ef9bc8..98294832f7 100644 --- a/src/applications/passphrase/view/PassphraseCredentialControl.php +++ b/src/applications/passphrase/view/PassphraseCredentialControl.php @@ -50,7 +50,8 @@ protected function renderInput() { // credential. Populate it into the menu to allow them to save the form // without making any changes. $current_phid = $this->getValue(); - if (strlen($current_phid) && empty($options_map[$current_phid])) { + if (phutil_nonempty_string($current_phid) && + empty($options_map[$current_phid])) { $viewer = $this->getViewer(); $current_name = null; From 085769ceb687e04cfca983cbb2dee59a889d489b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 11 Aug 2023 20:57:34 +0200 Subject: [PATCH 268/425] Fix PHP 8.1 "strlen(null)" exceptions trying to authenticate via custom OAuth provider Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. This change also replaces two other `strlen()` calls, only based on //reading// the code but not based on triggering exceptions. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(), ava(), phorge(), wmf-ext-misc() #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/applications/auth/adapter/PhutilOAuth1AuthAdapter.php:121] ``` ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(), ava(), phorge(), wmf-ext-misc() #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/applications/auth/adapter/PhutilOAuth1AuthAdapter.php:125] ``` Closes T15588 Test Plan: Go to `/auth/login/mediawiki:whatever/` and at least see no `strlen()` errors anymore (while further issues remain). See T15588 for details. Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15588 Differential Revision: https://we.phorge.it/D25373 --- src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php b/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php index 08cc65a235..389763da7d 100644 --- a/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php +++ b/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php @@ -104,7 +104,7 @@ protected function newOAuth1Future($uri, $data = array()) { ->setSignatureMethod($this->getSignatureMethod()); $consumer_key = $this->getConsumerKey(); - if (strlen($consumer_key)) { + if (phutil_nonempty_string($consumer_key)) { $future->setConsumerKey($consumer_key); } else { throw new Exception( @@ -118,11 +118,11 @@ protected function newOAuth1Future($uri, $data = array()) { $future->setConsumerSecret($consumer_secret); } - if (strlen($this->getToken())) { + if (phutil_nonempty_string($this->getToken())) { $future->setToken($this->getToken()); } - if (strlen($this->getTokenSecret())) { + if (phutil_nonempty_string($this->getTokenSecret())) { $future->setTokenSecret($this->getTokenSecret()); } @@ -137,7 +137,7 @@ public function getClientRedirectURI() { $request_token_uri = $this->getRequestTokenURI(); $future = $this->newOAuth1Future($request_token_uri); - if (strlen($this->getCallbackURI())) { + if (phutil_nonempty_string($this->getCallbackURI())) { $future->setCallbackURI($this->getCallbackURI()); } From c61c6a1ec3c1bbfa6c7254ce824bca8a860d4417 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 11 Aug 2023 21:00:25 +0200 Subject: [PATCH 269/425] Fix PHP 8.1 "strlen(null)" exception adding a Harbormaster Build Plan Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=customOAuthUrlencodeNull, ref.master=788098096e11, ref.customOAuthUrlencodeNull=4f0f2043b7e9), phorge(head=master, ref.master=226f3150933d) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php:106] ``` Closes T15591 Test Plan: Visit `/harbormaster/plan/edit/form/default/`. "Create Build Plan" page renders as expected. Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15591 Differential Revision: https://we.phorge.it/D25376 --- .../harbormaster/editor/HarbormasterBuildPlanEditEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php index c0fa80d71b..b7d73f238b 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php @@ -103,7 +103,7 @@ protected function buildCustomEditFields($object) { $key); $behavior_option = $object->getPlanProperty($storage_key); - if (!strlen($behavior_option)) { + if (!phutil_nonempty_string($behavior_option)) { $behavior_option = $behavior->getPlanOption($object)->getKey(); } From a2e8ab3180703305026da92f075c141537cfe5a1 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 11 Aug 2023 21:01:19 +0200 Subject: [PATCH 270/425] Fix PHP 8.1 "strlen(null)" exception adding a "Drydock: Run Command" build step in Harbormaster Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=customOAuthUrlencodeNull, ref.master=788098096e11, ref.customOAuthUrlencodeNull=4f0f2043b7e9), phorge(head=master, ref.master=226f3150933d) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/harbormaster/controller/HarbormasterStepEditController.php:142] ``` Closes T15592 Test Plan: Add a build step for an existing Harbormaster Build Plan, see that page `New Step: Drydock: Run Command` is correctly rendered at `/harbormaster/step/new/1/HarbormasterDrydockCommandBuildStepImplementation/` Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15592 Differential Revision: https://we.phorge.it/D25377 --- .../harbormaster/controller/HarbormasterStepEditController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/harbormaster/controller/HarbormasterStepEditController.php b/src/applications/harbormaster/controller/HarbormasterStepEditController.php index 8cd91bb5a4..a420d02627 100644 --- a/src/applications/harbormaster/controller/HarbormasterStepEditController.php +++ b/src/applications/harbormaster/controller/HarbormasterStepEditController.php @@ -139,7 +139,7 @@ public function handleRequest(AphrontRequest $request) { ->setUser($viewer); $instructions = $implementation->getEditInstructions(); - if (strlen($instructions)) { + if (phutil_nonempty_string($instructions)) { $form->appendRemarkupInstructions($instructions); } From 98dfac53ba721467a32b96641f3a18d428fb5441 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 11 Aug 2023 21:02:44 +0200 Subject: [PATCH 271/425] Fix PHP 8.1 "strlen(null)" exceptions blocking account registration with custom OAuth provider after redirect Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(), ava(), phorge(), wmf-ext-misc() #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/applications/auth/provider/PhabricatorOAuth1AuthProvider.php:70] ``` ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(), ava(), phorge(), wmf-ext-misc() #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/applications/auth/view/PhabricatorAuthAccountView.php:32] ``` Closes T15590 Test Plan: * As an admin, set up custom "MediaWiki" OAuth provider from from https://gitlab.wikimedia.org/-/ide/project/repos/phabricator/extensions/edit/wmf/stable/-/src/oauth/ * As an admin, apply D25373 * As a user, go to `/auth/login/mediawiki:whatever/` * Select login button * Allow authentication on third-party site * Get redirected to Phorge instance Phorge user account registration page "Create a New Account" at `/auth/register/abcdefghijklmnopqrstuvwxyz0123456/` now renders as expected, instead of displaying errors only. Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15590 Differential Revision: https://we.phorge.it/D25375 --- .../auth/provider/PhabricatorOAuth1AuthProvider.php | 2 +- .../auth/view/PhabricatorAuthAccountView.php | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php b/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php index b1590b9c82..7133cff042 100644 --- a/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php +++ b/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php @@ -67,7 +67,7 @@ public function processLoginRequest( } $denied = $request->getStr('denied'); - if (strlen($denied)) { + if ($denied) { // Twitter indicates that the user cancelled the login attempt by // returning "denied" as a parameter. throw new PhutilAuthUserAbortedException(); diff --git a/src/applications/auth/view/PhabricatorAuthAccountView.php b/src/applications/auth/view/PhabricatorAuthAccountView.php index 9746be7841..f903f45d26 100644 --- a/src/applications/auth/view/PhabricatorAuthAccountView.php +++ b/src/applications/auth/view/PhabricatorAuthAccountView.php @@ -29,13 +29,14 @@ public function render() { $realname = $account->getRealName(); $use_name = null; - if (strlen($dispname)) { + if (phutil_nonempty_string($dispname)) { $use_name = $dispname; - } else if (strlen($username) && strlen($realname)) { + } else if (phutil_nonempty_string($username) && + phutil_nonempty_string($realname)) { $use_name = $username.' ('.$realname.')'; - } else if (strlen($username)) { + } else if (phutil_nonempty_string($username)) { $use_name = $username; - } else if (strlen($realname)) { + } else if (phutil_nonempty_string($realname)) { $use_name = $realname; } From e8ea7a4a3697db4d0d8d36edfce57d0f06c6fd15 Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Sat, 12 Aug 2023 08:37:24 +0100 Subject: [PATCH 272/425] Fix PHP 8.1 issue in DifferentialChangeset getOldStatePathVector() Summary: The DifferentialChangeset getOldStatePathVector() method assumes oldFile and filename are set. This worked under PHP <= 8.0, but fails for PHP >= 8.1 with error messsage ``` strlen(): Passing null to parameter #1 ($string) of type string is deprecated ``` Fixes T15517 Test Plan: Create a diff in which a new file is added. This file will have oldFile NULL and filename a string. View the diff https://my.phorge.site/D1234 Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15517 Differential Revision: https://we.phorge.it/D25323 --- src/__phutil_library_map__.php | 2 ++ .../storage/DifferentialChangeset.php | 10 ++++++---- .../__tests__/DifferentialChangesetTestCase.php | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 src/applications/differential/storage/__tests__/DifferentialChangesetTestCase.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 213b6cf6b1..d223f47515 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -488,6 +488,7 @@ 'DifferentialChangesetRenderer' => 'applications/differential/render/DifferentialChangesetRenderer.php', 'DifferentialChangesetSearchConduitAPIMethod' => 'applications/differential/conduit/DifferentialChangesetSearchConduitAPIMethod.php', 'DifferentialChangesetSearchEngine' => 'applications/differential/query/DifferentialChangesetSearchEngine.php', + 'DifferentialChangesetTestCase' => 'applications/differential/storage/__tests__/DifferentialChangesetTestCase.php', 'DifferentialChangesetTestRenderer' => 'applications/differential/render/DifferentialChangesetTestRenderer.php', 'DifferentialChangesetTwoUpRenderer' => 'applications/differential/render/DifferentialChangesetTwoUpRenderer.php', 'DifferentialChangesetTwoUpTestRenderer' => 'applications/differential/render/DifferentialChangesetTwoUpTestRenderer.php', @@ -6501,6 +6502,7 @@ 'DifferentialChangesetRenderer' => 'Phobject', 'DifferentialChangesetSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'DifferentialChangesetSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'DifferentialChangesetTestCase' => 'PhabricatorTestCase', 'DifferentialChangesetTestRenderer' => 'DifferentialChangesetRenderer', 'DifferentialChangesetTwoUpRenderer' => 'DifferentialChangesetHTMLRenderer', 'DifferentialChangesetTwoUpTestRenderer' => 'DifferentialChangesetTestRenderer', diff --git a/src/applications/differential/storage/DifferentialChangeset.php b/src/applications/differential/storage/DifferentialChangeset.php index 770a49e411..d92b27e574 100644 --- a/src/applications/differential/storage/DifferentialChangeset.php +++ b/src/applications/differential/storage/DifferentialChangeset.php @@ -325,14 +325,16 @@ public function getDiff() { public function getOldStatePathVector() { $path = $this->getOldFile(); - if (!strlen($path)) { + if (!phutil_nonempty_string($path)) { $path = $this->getFilename(); } - $path = trim($path, '/'); - $path = explode('/', $path); + if (!phutil_nonempty_string($path)) { + return null; + } - return $path; + $path = trim($path, '/'); + return explode('/', $path); } public function getNewStatePathVector() { diff --git a/src/applications/differential/storage/__tests__/DifferentialChangesetTestCase.php b/src/applications/differential/storage/__tests__/DifferentialChangesetTestCase.php new file mode 100644 index 0000000000..435b77ee87 --- /dev/null +++ b/src/applications/differential/storage/__tests__/DifferentialChangesetTestCase.php @@ -0,0 +1,16 @@ +getOldStatePathVector(); + $this->assertTrue(true, 'getOldStatePathVector did not throw an error'); + } catch (Throwable $ex) { + $this->assertTrue(false, + 'getOldStatePathVector threw an exception:'.$ex->getMessage()); + } + } + +} From bcfcd9acfc12fdf8ca3bf0e2c89651ba6d159b17 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Mon, 24 Jul 2023 06:20:48 -0700 Subject: [PATCH 273/425] Unify type-checking for `setHref()` type methods Summary: Adopt `PhutilURI::checkHrefType()` to unify type-check of some PHUI objects. Ref T15316. Depends on D25356. Test Plan: In production. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15316 Differential Revision: https://we.phorge.it/D25357 --- src/view/phui/PHUIObjectItemView.php | 38 ++-------------------------- src/view/phui/PHUITagView.php | 13 ++-------- 2 files changed, 4 insertions(+), 47 deletions(-) diff --git a/src/view/phui/PHUIObjectItemView.php b/src/view/phui/PHUIObjectItemView.php index 600db9f3b7..527bd51b38 100644 --- a/src/view/phui/PHUIObjectItemView.php +++ b/src/view/phui/PHUIObjectItemView.php @@ -90,11 +90,7 @@ public function getObject() { * @return self */ public function setHref($href) { - - // We have not a very clear idea about what this method should receive - // So, let's log alien stuff for some time - // https://we.phorge.it/T15316 - self::requireValidHref($href, 'href'); + PhutilURI::checkHrefType($href); $this->href = $href; return $this; @@ -161,11 +157,7 @@ public function setImageURI($image_uri) { * @return self */ public function setImageHref($image_href) { - - // We have not a very clear idea about what this method should receive - // So, let's log alien stuff for some time - // https://we.phorge.it/T15316 - self::requireValidHref($image_href, 'image_href'); + PhutilURI::checkHrefType($image_href); $this->imageHref = $image_href; return $this; @@ -929,30 +921,4 @@ private function renderHandleIcon(PhabricatorObjectHandle $handle, $label) { return javelin_tag('span', $options, ''); } - - /** - * Receive a href attribute and check if it has expected values - * - * TODO: Feel free to remove after 2023, if no more new reports arrive. - * - * https://we.phorge.it/T15316 - * - * @param mixed $href Value to be checked - * @param string $variable_name Human reference - */ - private static function requireValidHref($href, $variable_name) { - - // We have not a very clear idea about what a "href" should be - if (is_object($href) && !($href instanceof PhutilURI)) { - - // We log stuff with a kind stack trace - phlog(new Exception(pht( - 'The variable %s received an unexpected type: %s. '. - 'Please share this stack trace as comment in Task %s', - $variable_name, - get_class($href), - 'https://we.phorge.it/T15316'))); - } - } - } diff --git a/src/view/phui/PHUITagView.php b/src/view/phui/PHUITagView.php index dc837c7156..c36fbab36b 100644 --- a/src/view/phui/PHUITagView.php +++ b/src/view/phui/PHUITagView.php @@ -103,20 +103,11 @@ public function setName($name) { /** * Set the href attribute * - * @param string|null $href + * @param string|PhutilURI|null $href * @return self */ public function setHref($href) { - - // We have not a very clear idea about what this method should receive - // We suspect that PhutilURI should be allowed... but let's log everything! - // https://we.phorge.it/T15316 - if (is_object($href)) { - phlog(sprintf( - 'Received unexpected type for href: %s. '. - 'Please paste this log as comment in https://we.phorge.it/T15316', - get_class($href))); - } + PhutilURI::checkHrefType($href); $this->href = $href; return $this; From 7040bd525764832bc805b595e8505d9b466f7ae5 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 15 Aug 2023 10:15:09 +0200 Subject: [PATCH 274/425] Fix PHP 8.1 "strlen(null)" exception adding "Build with Buildkite" build step in Harbormaster Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=customOAuthUrlencodeNull, ref.master=788098096e11, ref.customOAuthUrlencodeNull=4f0f2043b7e9), phorge(head=master, ref.master=226f3150933d) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php:271] ``` Closes T15594 Test Plan: Add a build step in Harbormaster, select "Interacting with External Build Systems > Build with Buildkite" at `/harbormaster/step/new/1/HarbormasterBuildkiteBuildStepImplementation/`, select the "Create Build Step" button. Page now shows several expected error messages (API Token is required; Organization Name is required; etc) instead of a PHP error. Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15594 Differential Revision: https://we.phorge.it/D25379 --- .../customfield/standard/PhabricatorStandardCustomField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php index 373aa9b66a..0c4ea452a2 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -268,7 +268,7 @@ public function shouldAppearInEditView() { public function readValueFromRequest(AphrontRequest $request) { $value = $request->getStr($this->getFieldKey()); - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { $value = null; } $this->setFieldValue($value); From 3f5fcdf4ddd8e527bf632bc90d3822ec2d824cd3 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 14 Aug 2023 11:05:11 +0200 Subject: [PATCH 275/425] Fix icon background color when using Dark Mode Summary: This CSS fix replaces the hard coded background gradient (white) value of icons when using Dark Mode. Now the "Choose User Icon" popup has visible icons. | Before | After | |---------|---------| |{F331622}|{F331623}| Ref T15056 Test Plan: - Flush all Phorge caches - Sign in - Go to user's Settings > Display Preferences and select the Accessibility (user interface) "Dark Mode". - Go to user's profile, edit profile and click on Choose icon. - Check that now the icons in "Choose User Icon" are visible. - Do these steps for each user interface theme in order to check against regression. Reviewers: O1 Blessed Committers, Matthew, valerio.bozzolan Reviewed By: O1 Blessed Committers, Matthew, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15056 Differential Revision: https://we.phorge.it/D25384 --- resources/celerity/map.php | 4 ++-- webroot/rsrc/css/phui/phui-icon-set-selector.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 3fc6387e77..c78bf06e59 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -160,7 +160,7 @@ 'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', 'rsrc/css/phui/phui-header-view.css' => '36c86a58', 'rsrc/css/phui/phui-hovercard.css' => '39fd2e14', - 'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec', + 'rsrc/css/phui/phui-icon-set-selector.css' => '19e0253b', 'rsrc/css/phui/phui-icon.css' => '084ac612', 'rsrc/css/phui/phui-image-mask.css' => '62c7f4d2', 'rsrc/css/phui/phui-info-view.css' => 'a10a909b', @@ -856,7 +856,7 @@ 'phui-hovercard' => '6199f752', 'phui-hovercard-list' => 'de4b4919', 'phui-hovercard-view-css' => '39fd2e14', - 'phui-icon-set-selector-css' => '7aa5f3ec', + 'phui-icon-set-selector-css' => '19e0253b', 'phui-icon-view-css' => '084ac612', 'phui-image-mask-css' => '62c7f4d2', 'phui-info-view-css' => 'a10a909b', diff --git a/webroot/rsrc/css/phui/phui-icon-set-selector.css b/webroot/rsrc/css/phui/phui-icon-set-selector.css index c66c2a6e55..f3bc1b9685 100644 --- a/webroot/rsrc/css/phui/phui-icon-set-selector.css +++ b/webroot/rsrc/css/phui/phui-icon-set-selector.css @@ -3,8 +3,8 @@ */ button.icon-button { - background-color: #F7F7F9; - background-image: linear-gradient(to bottom, #ffffff, #f1f0f1); + background-image: linear-gradient(to bottom, + {$lightgreybackground}, {$greybackground}); border: 1px solid rgba({$alphablue},.2); color: {$darkgreytext}; position: relative; From 29493f8a5c950548f3b70843c45cb320dad42cde Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 15 Aug 2023 16:18:28 +0200 Subject: [PATCH 276/425] Fix PHP 8.1 "strlen(null)" exception listing >100 task search results Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=df6c315ace5f), phorge(head=master, ref.master=7040bd525764) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/maniphest/query/ManiphestTaskQuery.php:1039] ``` Closes T15604 Test Plan: Have more than 100 tasks, run a broad search with more than 100 results, try to go to next page of results. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15604 Differential Revision: https://we.phorge.it/D25392 --- src/applications/maniphest/query/ManiphestTaskQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php index 9e58728cff..8bbf976564 100644 --- a/src/applications/maniphest/query/ManiphestTaskQuery.php +++ b/src/applications/maniphest/query/ManiphestTaskQuery.php @@ -1036,7 +1036,7 @@ private function parseCursor($cursor) { $parts[] = null; } - if (!strlen($parts[1])) { + if (!phutil_nonempty_string($parts[1])) { $parts[1] = null; } From 9fa9aa30b940e640d847b7603218af4baf48e15d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 16 Aug 2023 13:11:56 +0200 Subject: [PATCH 277/425] Fix PHP 8.1 "strlen(null)" exception on "Link External Account" page at login Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(), phorge() #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/applications/auth/controller/PhabricatorAuthSetExternalController.php:43] ``` Closes T15606 Test Plan: Follow steps in T15606; page `/auth/external/` displays as expected the content: "Link External Account - You can link your Phabricator account to an external account to allow you to log in more easily in the future. To continue, choose an account to link below. If you prefer not to link your account, you can skip this step." and the "Link External Account" button. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15606 Differential Revision: https://we.phorge.it/D25395 --- .../auth/controller/PhabricatorAuthSetExternalController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/controller/PhabricatorAuthSetExternalController.php b/src/applications/auth/controller/PhabricatorAuthSetExternalController.php index 8b0a44b9dc..2a8bbda7df 100644 --- a/src/applications/auth/controller/PhabricatorAuthSetExternalController.php +++ b/src/applications/auth/controller/PhabricatorAuthSetExternalController.php @@ -40,7 +40,7 @@ public function handleRequest(AphrontRequest $request) { $text = PhabricatorAuthMessage::loadMessageText( $viewer, PhabricatorAuthLinkMessageType::MESSAGEKEY); - if (!strlen($text)) { + if (!phutil_nonempty_string($text)) { $text = pht( 'You can link your %s account to an external account to '. 'allow you to log in more easily in the future. To continue, choose '. From a5d8b2d5cfd6e809f82556c9f1462ae0035e2279 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 17 Aug 2023 10:16:24 +0200 Subject: [PATCH 278/425] Fix a PHP 8.1 deprecated use of preg_match with a NULL argument Summary: These calls are preventing users to browse subversion/mercurial repositories in PHP 8.1+. Indeed, a similar bug affecting git repositories was already addressed in another commit (rP6b8ec50148909938850b5acfd11725ae23a8e31b). This commit harmonize both DiffusionSvnRequest and DiffusionMercurialRequest with DiffusionGitRequest Fix T15607 Test Plan: - Sign in - Open a diffusion SVN/Mercurial repository - You should not get a RuntimeException concerning this preg_match call Reviewers: O1 Blessed Committers, Sten, valerio.bozzolan Reviewed By: O1 Blessed Committers, Sten, valerio.bozzolan Subscribers: Sten, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15607 Differential Revision: https://we.phorge.it/D25397 --- .../diffusion/request/DiffusionMercurialRequest.php | 5 +++-- src/applications/diffusion/request/DiffusionSvnRequest.php | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/request/DiffusionMercurialRequest.php b/src/applications/diffusion/request/DiffusionMercurialRequest.php index b626d62750..676dc097f7 100644 --- a/src/applications/diffusion/request/DiffusionMercurialRequest.php +++ b/src/applications/diffusion/request/DiffusionMercurialRequest.php @@ -3,6 +3,9 @@ final class DiffusionMercurialRequest extends DiffusionRequest { protected function isStableCommit($symbol) { + if ($symbol === null) { + return false; + } return preg_match('/^[a-f0-9]{40}\z/', $symbol); } @@ -10,11 +13,9 @@ public function getBranch() { if ($this->branch) { return $this->branch; } - if ($this->repository) { return $this->repository->getDefaultBranch(); } - throw new Exception(pht('Unable to determine branch!')); } diff --git a/src/applications/diffusion/request/DiffusionSvnRequest.php b/src/applications/diffusion/request/DiffusionSvnRequest.php index 5f50366331..33fc3446f9 100644 --- a/src/applications/diffusion/request/DiffusionSvnRequest.php +++ b/src/applications/diffusion/request/DiffusionSvnRequest.php @@ -3,6 +3,9 @@ final class DiffusionSvnRequest extends DiffusionRequest { protected function isStableCommit($symbol) { + if ($symbol === null) { + return false; + } return preg_match('/^[1-9]\d*\z/', $symbol); } From 17befe9bca5b6df2a3f867f58aa9fb5d564ecd9b Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 17 Aug 2023 10:31:33 +0200 Subject: [PATCH 279/425] Fix source code viewer background color when using Dark Mode in Diffusion Summary: This CSS fix replaces the hard coded white background value by the diff.background CSS variable. This is a proposal to address this issue but it may be a good idea to create a different CSS variable (for instance source.background) in order to avoid any potential side effect in the future. | Before | After | |---------|---------| |{F333617}|{F333618}| Fix : T15056 Test Plan: - Flush all Phorge caches - Sign in - Open a diffusion repository - Open any file - Check that viewer background color is consistent with the theme. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25396 --- resources/celerity/map.php | 4 ++-- webroot/rsrc/css/layout/phabricator-source-code-view.css | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index c78bf06e59..a1dd137919 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -121,7 +121,7 @@ 'rsrc/css/fuel/fuel-handle-list.css' => '2c4cbeca', 'rsrc/css/fuel/fuel-map.css' => 'd6e31510', 'rsrc/css/fuel/fuel-menu.css' => '21f5d199', - 'rsrc/css/layout/phabricator-source-code-view.css' => '49656486', + 'rsrc/css/layout/phabricator-source-code-view.css' => 'e382316a', 'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4', 'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa', 'rsrc/css/phui/button/phui-button.css' => 'f9d0f9c8', @@ -805,7 +805,7 @@ 'phabricator-search-results-css' => '9ea70ace', 'phabricator-shaped-request' => '995f5102', 'phabricator-slowvote-css' => '1694baed', - 'phabricator-source-code-view-css' => '49656486', + 'phabricator-source-code-view-css' => 'e382316a', 'phabricator-standard-page-view' => 'e08c7462', 'phabricator-textareautils' => 'f340a484', 'phabricator-title' => '43bc9360', diff --git a/webroot/rsrc/css/layout/phabricator-source-code-view.css b/webroot/rsrc/css/layout/phabricator-source-code-view.css index b16b158004..83cda98df4 100644 --- a/webroot/rsrc/css/layout/phabricator-source-code-view.css +++ b/webroot/rsrc/css/layout/phabricator-source-code-view.css @@ -16,7 +16,7 @@ white-space: pre-wrap; padding: 2px 8px 1px; width: 100%; - background: #ffffff; + background: {$diff.background}; } .phabricator-source-line { From 747d7db241612313fa0ed82eda6d63592988d459 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 17 Aug 2023 10:41:12 +0200 Subject: [PATCH 280/425] Fix a PHP 8.1/8.2 deprecated use of ltrim and rtrim with a NULL argument Summary: These calls were preventing notification servers configuration to be properly initialized. Indeed, PHP 8.X is stricter concerning This behavior is deprecated since PHP 8.1. Phorge adopts phutil_nonempty_string() as a replacement. Fix T15598 Test Plan: Sign in as an administrator, configure the notification server without filling admin path field, you shouldn't get both an RuntimeException and a warning indicating that Phorge is unable to connect to Notification Server but a message indicating that everything is fine. Reviewers: O1 Blessed Committers, valerio.bozzolan, avivey Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey Subscribers: avivey, Cigaryno, Matthew, valerio.bozzolan, tobiaswiese, speck Maniphest Tasks: T15598 Differential Revision: https://we.phorge.it/D25382 --- .../client/PhabricatorNotificationServerRef.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/applications/notification/client/PhabricatorNotificationServerRef.php b/src/applications/notification/client/PhabricatorNotificationServerRef.php index c5b0f2f8aa..03cd5de5d4 100644 --- a/src/applications/notification/client/PhabricatorNotificationServerRef.php +++ b/src/applications/notification/client/PhabricatorNotificationServerRef.php @@ -143,8 +143,10 @@ public function isAdminServer() { return ($this->type == 'admin'); } - public function getURI($to_path = null) { - $full_path = rtrim($this->getPath(), '/').'/'.ltrim($to_path, '/'); + public function getURI($to_path = '') { + $path = coalesce($this->path, ''); + $to_path = coalesce($to_path, ''); + $full_path = rtrim($path, '/').'/'.ltrim($to_path, '/'); $uri = id(new PhutilURI('http://'.$this->getHost())) ->setProtocol($this->getProtocol()) From 7cffe557ac2458022e2799693284c5495c5d5458 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 17 Aug 2023 13:16:50 +0200 Subject: [PATCH 281/425] Fix hovercard background color when using Dark Mode Summary: This CSS fix replaces the disabled hovercard hard coded white background color value by the page.content CSS variable. Fix : T15056 Test Plan: - Flush all Phorge caches - Sign in - Open any page including a reference to a closed manifest (https://we.phorge.it/D25395) - Mouve the mouse over the closed manifest reference - Check that hovercard background color is consistent with the theme. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25402 --- webroot/rsrc/css/phui/phui-hovercard.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webroot/rsrc/css/phui/phui-hovercard.css b/webroot/rsrc/css/phui/phui-hovercard.css index ba8057d08d..43c6f66df9 100644 --- a/webroot/rsrc/css/phui/phui-hovercard.css +++ b/webroot/rsrc/css/phui/phui-hovercard.css @@ -113,7 +113,7 @@ } .hovercard-task-view .phui-oi-disabled.phui-workcard { - background-color: #fff; + background-color: {$page.content}; } .phui-hovercard-object-type { From 3a1f568974f7bddc4235d4bdc8337f4c8713f7a1 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 17 Aug 2023 14:42:02 +0200 Subject: [PATCH 282/425] Fix typo "this this server" Summary: Closes T15611 Test Plan: Carefully read. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15611 Differential Revision: https://we.phorge.it/D25404 --- .../PhabricatorAuthProvidersGuidanceEngineExtension.php | 5 ++--- src/applications/phame/storage/PhameBlog.php | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php b/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php index d4d41f1d83..8d823f82c2 100644 --- a/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php +++ b/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php @@ -53,9 +53,8 @@ public function generateGuidance(PhabricatorGuidanceContext $context) { ->setMessage($message); } else { $message = pht( - 'Anyone who can browse to this this server will be able to '. - 'register an account. To add email domain restrictions, configure '. - '%s.', + 'Anyone who can browse to this server will be able to register '. + 'an account. To add email domain restrictions, configure %s.', $domains_link); $results[] = $this->newGuidance('core.auth.email-domains.off') diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index 587f5daea3..8ab5c2747e 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -154,7 +154,7 @@ public function validateCustomDomain($domain_full_uri) { $href = PhabricatorEnv::getProductionURI( '/config/edit/policy.allow-public/'); return pht( - 'For custom domains to work, this this server must be '. + 'For custom domains to work, this server must be '. 'configured to allow the public access policy. Configure this '. 'setting %s, or ask an administrator to configure this setting. '. 'The domain can be specified later once this setting has been '. From 761134a1a9fe2b2a74ede5705bd41edeb561965b Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 18 Aug 2023 09:53:23 +0800 Subject: [PATCH 283/425] Line counters: do not wrap when over 999 on mobile Summary: | Before | After | |-----------|-----------| | {F335620} | {F335621} | Closes T15615 Test Plan: Create a very long Diff with soooo much lines (e.g. 1000+). Reduce the window and check that line numbers do not wrap anymore. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15615 Differential Revision: https://we.phorge.it/D25407 --- resources/celerity/map.php | 12 ++++++------ .../css/application/differential/changeset-view.css | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index a1dd137919..8f791cbeda 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,7 +12,7 @@ 'core.pkg.css' => '1a5169fe', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', - 'differential.pkg.css' => '525f9a1d', + 'differential.pkg.css' => '2431def2', 'differential.pkg.js' => '46fcb3af', 'diffusion.pkg.css' => '42c75c37', 'diffusion.pkg.js' => '78c9885d', @@ -63,7 +63,7 @@ 'rsrc/css/application/diff/diff-tree-view.css' => 'e2d3e222', 'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d', 'rsrc/css/application/differential/add-comment.css' => '7e5900d9', - 'rsrc/css/application/differential/changeset-view.css' => '8b9caefe', + 'rsrc/css/application/differential/changeset-view.css' => '360630ff', 'rsrc/css/application/differential/core.css' => '7300a73e', 'rsrc/css/application/differential/phui-inline-comment.css' => 'a864426f', 'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d', @@ -560,7 +560,7 @@ 'conpherence-transaction-css' => '3a3f5e7e', 'd3' => '9d068042', 'diff-tree-view-css' => 'e2d3e222', - 'differential-changeset-view-css' => '8b9caefe', + 'differential-changeset-view-css' => '360630ff', 'differential-core-view-css' => '7300a73e', 'differential-revision-add-comment-css' => '7e5900d9', 'differential-revision-comment-css' => '7dbc8d1d', @@ -1228,6 +1228,9 @@ 'aphront-typeahead-control-css', 'phui-tag-view-css', ), + '360630ff' => array( + 'phui-inline-comment-view-css', + ), '3829a3cf' => array( 'javelin-behavior', 'javelin-uri', @@ -1702,9 +1705,6 @@ 'javelin-dom', 'phabricator-busy', ), - '8b9caefe' => array( - 'phui-inline-comment-view-css', - ), '8badee71' => array( 'javelin-install', 'javelin-util', diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 88bbcfa421..a127d14648 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -183,6 +183,7 @@ should always have a boring grey background. */ cursor: pointer; border-right: 1px solid {$thinblueborder}; overflow: hidden; + white-space: nowrap; } .differential-diff td + td.n { From 7f46a252f21e6dc21caba593440db4da13684dab Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 18 Aug 2023 09:19:42 +0200 Subject: [PATCH 284/425] Fix a PHP 8.1/8.2 deprecated use of strlen a NULL argument Summary: This commit harmonizes PhabricatorNotificationServerRef's getURI and getWebsocketURI methods as discussed in D25382. Test Plan: This is hard to say since I didn't know how/when this function might be called. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25401 --- .../notification/client/PhabricatorNotificationServerRef.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/notification/client/PhabricatorNotificationServerRef.php b/src/applications/notification/client/PhabricatorNotificationServerRef.php index 03cd5de5d4..2fe903fdba 100644 --- a/src/applications/notification/client/PhabricatorNotificationServerRef.php +++ b/src/applications/notification/client/PhabricatorNotificationServerRef.php @@ -161,9 +161,9 @@ public function getURI($to_path = '') { return $uri; } - public function getWebsocketURI($to_path = null) { + public function getWebsocketURI($to_path = '') { $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $to_path = $to_path.'~'.$instance.'/'; } From 0ef08baf64527d3c31e5cc2e1da4b4318309c45a Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 18 Aug 2023 09:20:23 +0200 Subject: [PATCH 285/425] Fix a PHP 8.1 deprecated use of strlen with a NULL argument Summary: This strlen() call was preventing a new Phorge instance to be deployed/configured. Indeed, on a fresh instance, configuration's "base-uri" key may not be defined witch lead to a Runtime Exception. Using strlen() to check string validity is deprecated since PHP 8.1, phorge adopts phutil_nonempty_string() as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Fix T15605 Test Plan: - Checkout a fresh Phorge local copy from official 'https://we.phorge.it/source/phorge.git' - Install/Configure local webserver/database - Open http://phorge.domain in you browser - Configure Phorge database (as requested by webpage) - Create Phorge database (as requested by webpage) - You should be able to reach administrator account page instead of getting a RuntimeException Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15605 Differential Revision: https://we.phorge.it/D25394 --- .../config/check/PhabricatorWebServerSetupCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/config/check/PhabricatorWebServerSetupCheck.php b/src/applications/config/check/PhabricatorWebServerSetupCheck.php index cc9660325c..d46f808f58 100644 --- a/src/applications/config/check/PhabricatorWebServerSetupCheck.php +++ b/src/applications/config/check/PhabricatorWebServerSetupCheck.php @@ -23,7 +23,7 @@ protected function executeChecks() { } $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); - if (!strlen($base_uri)) { + if (!$base_uri) { // If `phabricator.base-uri` is not set then we can't really do // anything. return; From d84dbf8e27ba845b1c29e885d600e7f1d2d7e815 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 17 Aug 2023 17:27:21 +0200 Subject: [PATCH 286/425] Fix PHP 8.1 "strlen(null)" exception creating a Paste without content in Conduit paste.create Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=df6c315ace5f), phorge(head=thisThis, ref.master=7cffe557ac24, ref.thisThis=529790613a86) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php:46] ``` Closes T15613 Test Plan: Create a paste in the deprecated API paste.create without content via Conduit. Shows `error_code ERR-NO-PASTE` as expected, and no exception. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15613 Differential Revision: https://we.phorge.it/D25405 --- src/applications/paste/conduit/PasteCreateConduitAPIMethod.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php index c62ecd0494..d55d184874 100644 --- a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php @@ -43,7 +43,7 @@ protected function execute(ConduitAPIRequest $request) { $title = $request->getValue('title'); $language = $request->getValue('language'); - if (!strlen($content)) { + if (!phutil_nonempty_string($content)) { throw new ConduitException('ERR-NO-PASTE'); } From 7c58ea140374fd17cc12d9ab068cde2579781832 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 17 Aug 2023 17:42:24 +0200 Subject: [PATCH 287/425] Fix PHP 8.1 "strlen(null)" exception creating a Phriction doc without slug in Conduit Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=df6c315ace5f), phorge(head=master, ref.master=7cffe557ac24) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php:28] ``` Closes T15614 Test Plan: Create a Phriction document without slug via Conduit. Get an `EXCEPTION: (Exception) No such document.` instead of a `strlen()` exception. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15614 Differential Revision: https://we.phorge.it/D25406 --- .../phriction/conduit/PhrictionCreateConduitAPIMethod.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php index 300dbbfa80..61d3611faf 100644 --- a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php @@ -25,7 +25,7 @@ protected function defineReturnType() { protected function execute(ConduitAPIRequest $request) { $slug = $request->getValue('slug'); - if (!strlen($slug)) { + if (!phutil_nonempty_string($slug)) { throw new Exception(pht('No such document.')); } $doc = id(new PhrictionDocumentQuery()) From ad0052fcd643cb6b88453a6c8a11b5593056e154 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 18 Aug 2023 02:00:06 -0700 Subject: [PATCH 288/425] Rebrand: Add "path" entries to PlatformSymbols Summary: Ref T15006. Change a few places that were mentioning `phabricator` path. Test Plan: Mk1 eyeball on each effected page. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15006 Differential Revision: https://we.phorge.it/D25343 --- .../controller/PhabricatorDaemonLogViewController.php | 3 ++- .../controller/DiffusionRepositoryEditDeleteController.php | 3 ++- src/applications/metamta/PhabricatorMetaMTAWorker.php | 7 ++++--- .../repository/query/PhabricatorRepositoryQuery.php | 4 ++++ .../markup/markuprule/PhutilRemarkupEvalRule.php | 4 ++-- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php index f4c7ba60c9..253a3d5190 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php @@ -171,7 +171,8 @@ private function buildPropertyListView(PhabricatorDaemonLog $daemon) { phutil_tag( 'tt', array(), - "phabricator/ $ ./bin/phd log --id {$id}")); + PlatformSymbols::getPlatformServerPath(). + " $ ./bin/phd log --id {$id}")); return $view; diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php index 93fe70c0fe..72e18d17e8 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php @@ -28,7 +28,8 @@ public function handleRequest(AphrontRequest $request) { 'the command line:')) ->appendCommand( csprintf( - 'phabricator/ $ ./bin/remove destroy %R', + '%s $ ./bin/remove destroy %R', + PlatformSymbols::getPlatformServerPath(), $repository->getMonogram())) ->appendParagraph( pht( diff --git a/src/applications/metamta/PhabricatorMetaMTAWorker.php b/src/applications/metamta/PhabricatorMetaMTAWorker.php index dcc6a8dc5e..7d7c51f8b2 100644 --- a/src/applications/metamta/PhabricatorMetaMTAWorker.php +++ b/src/applications/metamta/PhabricatorMetaMTAWorker.php @@ -45,9 +45,10 @@ private function loadMessage() { public function renderForDisplay(PhabricatorUser $viewer) { return phutil_tag( 'pre', - array( - ), - 'phabricator/ $ ./bin/mail show-outbound --id '.$this->getTaskData()); + array(), + PlatformSymbols::getPlatformServerPath(). + ' $ ./bin/mail show-outbound --id '. + $this->getTaskData()); } } diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 05b011e85a..17a46f7b62 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -62,6 +62,10 @@ public function withIdentifiers(array $identifiers) { $slugs = array(); foreach ($identifiers as $identifier) { + if ($identifier === null) { + continue; + } + if (ctype_digit((string)$identifier)) { $ids[$identifier] = $identifier; continue; diff --git a/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php b/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php index cb67041c62..a525924fff 100644 --- a/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php +++ b/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php @@ -68,11 +68,11 @@ private function evaluateExpression($expression) { 'platform' => array( 'server' => array( 'name' => PlatformSymbols::getPlatformServerName(), - 'path' => pht('phabricator/'), + 'path' => PlatformSymbols::getPlatformServerPath(), ), 'client' => array( 'name' => PlatformSymbols::getPlatformClientName(), - 'path' => pht('arcanist/'), + 'path' => PlatformSymbols::getPlatformClientPath(), ), ), ), From 6ec89e9f08668c950c6a7d80164bd822bd38b472 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 18 Aug 2023 12:48:02 +0200 Subject: [PATCH 289/425] Fix a PHP 8.1 deprecated use of strlen with a NULL argument Summary: With PHP 8.1+ it was not possible to view a commit from subversion repositories. Indeed, if the commit user and/or email is not properly defined, strlen(null) is called, causing a deprecation warning, elevated to exception. Using strlen() to check string validity is deprecated since PHP 8.1. Phorge adopts phutil_nonempty_string() as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Fix T15609 Test Plan: - Apply D25397 and D25398 - Sign in - Open a diffusion SVN repository - Open a commit without user name and or email - You should not see the same Runtime Exception (unfortunately, there is another one as described in T15610) Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15609 Differential Revision: https://we.phorge.it/D25399 --- src/applications/diffusion/data/DiffusionCommitRef.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/applications/diffusion/data/DiffusionCommitRef.php b/src/applications/diffusion/data/DiffusionCommitRef.php index 06dd4d5ffb..76c3371192 100644 --- a/src/applications/diffusion/data/DiffusionCommitRef.php +++ b/src/applications/diffusion/data/DiffusionCommitRef.php @@ -131,6 +131,8 @@ public function getSummary() { } private function formatUser($name, $email) { + $name = coalesce($name, ''); + $email = coalesce($email, ''); if (strlen($name) && strlen($email)) { return "{$name} <{$email}>"; } else if (strlen($email)) { From 8876f75fbb9978f82040122660a7cabbc5358eb9 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 18 Aug 2023 15:49:32 +0200 Subject: [PATCH 290/425] Fix a PHP 8.1 deprecated use of strlen with a NULL argument Summary: This call is preventing users to browse Subversion repositories. Using strlen() to check string validity is deprecated since PHP 8.1, phorge adopts phutil_nonempty_string() as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Fix T15608 Test Plan: - Sign in - Open a Diffusion SVN repository - You should see the repository instead of getting a Runtime Exception Reviewers: O1 Blessed Committers, Sten, avivey Reviewed By: O1 Blessed Committers, Sten, avivey Subscribers: Sten, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15608 Differential Revision: https://we.phorge.it/D25398 --- .../diffusion/controller/DiffusionRepositoryController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index ad3518b81e..3ae237c22d 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -508,7 +508,7 @@ private function buildBrowseTable( $repository_name = $repository->getName(); $branch_name = $drequest->getBranch(); - if (strlen($branch_name)) { + if (phutil_nonempty_string($branch_name)) { $repository_name .= ' ('.$branch_name.')'; } From 3cc5ee6a33dfdf8e1e8bfcc12b655ebd516b3e5b Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 19 Aug 2023 00:02:14 +0800 Subject: [PATCH 291/425] Documentation: fix unexisting path sshd_config.phabricator.example Summary: Since the example file was renamed, this change makes the documentation consistent with it. Also we added exactly one-space somewhere, to create a super-satisfying indentation an example block. You're welcome. Ref T15006 Test Plan: Check that the documentation is at least 0.000001% better now. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15006 Differential Revision: https://we.phorge.it/D25410 --- src/docs/user/userguide/diffusion_hosting.diviner | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docs/user/userguide/diffusion_hosting.diviner b/src/docs/user/userguide/diffusion_hosting.diviner index c8a1c26eed..0d84859082 100644 --- a/src/docs/user/userguide/diffusion_hosting.diviner +++ b/src/docs/user/userguide/diffusion_hosting.diviner @@ -360,13 +360,13 @@ be owned by `root`, and the script must have `755` permissions: ``` $ sudo chown root /path/to/somewhere/ $ sudo chown root /path/to/somewhere/phorge-ssh-hook.sh -$ sudo chmod 755 /path/to/somewhere/phorge-ssh-hook.sh +$ sudo chmod 755 /path/to/somewhere/phorge-ssh-hook.sh ``` If you don't do this, `sshd` will refuse to execute the hook. **Create `sshd_config` for Phorge**: Copy the template in -`phorge/resources/sshd/sshd_config.phabricator.example` to somewhere like +`phorge/resources/sshd/sshd_config.phorge.example` to somewhere like `/etc/ssh/sshd_config.phorge`. Open the file and edit the `AuthorizedKeysCommand`, From ba4b8cb1ae7d209caf6e2a209247e114262d0d15 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 18 Aug 2023 12:28:03 -0700 Subject: [PATCH 292/425] PHP 8 and Diviner Technical Documentation Summary: This also fixes the field Personal Settings > Display Preferences > Monospaced Font, when empty. Close T15621 Test Plan: Generate diviner stuff, navigate to the "Technical Documentation" section (In php 8.1+) Reviewers: O1 Blessed Committers, mturdus, valerio.bozzolan Reviewed By: O1 Blessed Committers, mturdus, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15621 Differential Revision: https://we.phorge.it/D25412 --- src/applications/diviner/storage/DivinerLiveSymbol.php | 2 +- .../settings/setting/PhabricatorMonospacedFontSetting.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/diviner/storage/DivinerLiveSymbol.php b/src/applications/diviner/storage/DivinerLiveSymbol.php index d62e6a1e18..6477e00879 100644 --- a/src/applications/diviner/storage/DivinerLiveSymbol.php +++ b/src/applications/diviner/storage/DivinerLiveSymbol.php @@ -172,7 +172,7 @@ public function save() { public function getTitle() { $title = parent::getTitle(); - if (!strlen($title)) { + if (!phutil_nonempty_string($title)) { $title = $this->getName(); } diff --git a/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php b/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php index 2a96f5d95f..5cfbec1b26 100644 --- a/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php +++ b/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php @@ -25,7 +25,7 @@ protected function getControlInstructions() { } public function validateTransactionValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return; } From 9b105c8e9e54a863a5e92f84c6f27af846647676 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 18 Aug 2023 18:39:59 +0200 Subject: [PATCH 293/425] Fix PHP 8.1 "strlen(null)" exceptions importing ICS file into calendar Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=df6c315ace5f), phorge(head=importICSCalendar, ref.master=3cc5ee6a33df, ref.importICSCalendar=3bd396120123) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/calendar/import/PhabricatorCalendarImportEngine.php:459] ``` ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=df6c315ace5f), phorge(head=importICSCalendar, ref.master=3cc5ee6a33df, ref.importICSCalendar=3bd396120123) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/applications/calendar/import/PhabricatorCalendarImportEngine.php:450] ``` Closes T15620 Test Plan: * Revert rP02a4f8b0c8f1279fc0040ad8077942fd8b0d948b not to run into T15619 * Try to import an ICS file via `/calendar/import/edit/` * See that page `/calendar/import/4/` renders correctly in web browser and shows `Log Messages` and `Imported Events` as expected Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15620 Differential Revision: https://we.phorge.it/D25411 --- .../calendar/import/PhabricatorCalendarImportEngine.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/calendar/import/PhabricatorCalendarImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php index 35e94634fe..626757c61d 100644 --- a/src/applications/calendar/import/PhabricatorCalendarImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php @@ -447,7 +447,7 @@ private function getFullNodeUID(PhutilCalendarEventNode $node) { private function getParentNodeUID(PhutilCalendarEventNode $node) { $recurrence_id = $node->getRecurrenceID(); - if (!strlen($recurrence_id)) { + if (!phutil_nonempty_string($recurrence_id)) { return null; } @@ -456,7 +456,7 @@ private function getParentNodeUID(PhutilCalendarEventNode $node) { private function getNodeInstanceEpoch(PhutilCalendarEventNode $node) { $instance_iso = $node->getRecurrenceID(); - if (strlen($instance_iso)) { + if (phutil_nonempty_string($instance_iso)) { $instance_datetime = PhutilCalendarAbsoluteDateTime::newFromISO8601( $instance_iso); $instance_epoch = $instance_datetime->getEpoch(); From beec08e0191386d73581862b4378a0282166345f Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 22 Aug 2023 11:07:02 +0200 Subject: [PATCH 294/425] Fix RuntimeException: min(): Array must contain at least one element Summary: Make sure that `$epochs[]` is never empty but contains `0` so `min($epochs)` will not complain. Closes T15623 Test Plan: Carefully read the code (as I am unaware of reproduction steps). Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15623 Differential Revision: https://we.phorge.it/D25417 --- .../diffusion/controller/DiffusionBlameController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/applications/diffusion/controller/DiffusionBlameController.php b/src/applications/diffusion/controller/DiffusionBlameController.php index 5de464b335..34078a7b7a 100644 --- a/src/applications/diffusion/controller/DiffusionBlameController.php +++ b/src/applications/diffusion/controller/DiffusionBlameController.php @@ -198,6 +198,10 @@ public function handleRequest(AphrontRequest $request) { $map[$identifier] = $data; } + if (empty($epochs)) { + $epochs[] = 0; + } + $epoch_min = min($epochs); $epoch_max = max($epochs); From 0ec3291ff4a9249c71469f2db09c0a860770e32c Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 28 Aug 2023 10:38:53 +0200 Subject: [PATCH 295/425] Fix answer background color when using Dark Mode in Ponder Summary: This CSS fix replaces the hard coded white background value by the page.content CSS variable. | Before | After | |---------|---------| |{F332921}|{F332922}| Test Plan: - Flush all Phorge caches - Sign in - Go to question with at least one answer. - Check that the answer block background color is consistent with the theme. - Do these steps for each user interface theme in order to check against regression. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25393 --- webroot/rsrc/css/application/ponder/ponder-view.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index 567d7ae99b..675bc205b7 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -77,7 +77,7 @@ } .ponder-answer-view .ponder-answer-content { - background-color: #fff; + background-color: {$page.content}; padding: 16px 16px 0 16px; } From 4c314a733c1da104e4e30c72c7ce74766a382f5d Mon Sep 17 00:00:00 2001 From: Nicolas Aragone Date: Tue, 29 Aug 2023 18:49:23 -0300 Subject: [PATCH 296/425] Clarifying the operating system choices. Test Plan: looked at words, words looked good Reviewers: O1 Blessed Committers, chris Reviewed By: O1 Blessed Committers, chris Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Tags: #documentation Differential Revision: https://we.phorge.it/D25427 --- src/docs/user/installation_guide.diviner | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index ec7aa186d2..135c94adc0 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -54,12 +54,12 @@ Phorge from any operating system with a web browser. However, the server software does not run on Windows. It does run on most other operating systems, so choose one of these instead: - - **Linux**: Most installs use Linux. - - **Mac OS X**: Mac OS X is an acceptable flavor of Linux. - - **FreeBSD**: While FreeBSD is certainly not a flavor of Linux, it is a fine + - **GNU/Linux**: Most installs use Linux. + - **Mac OS X**: Mac OS X is an acceptable non-flavor of Linux. + - **BSD**: While BSD is certainly not a flavor of Linux, it is a fine operating system possessed of many desirable qualities, and Phorge will - install and run properly on FreeBSD. - - **Solaris, etc.**: Other systems which look like Linux and quack like Linux + install and run properly on BSD. + - **Solaris, etc.**: Other systems which look like *nix and quack like *nix will generally work fine, although we may suffer a reduced ability to support and resolve issues on unusual operating systems. From aeab4efe17cfba0dcb2f2b1b487c7175d1d428b8 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Wed, 30 Aug 2023 11:14:14 -0700 Subject: [PATCH 297/425] Generate Diviner book for Javelin Summary: And also fix a bug in the generator script. Test Plan: Run `bin/diviner generate`, wait till the cows come home, see 3 pages about Javelin in the Diviner app. Reviewers: O1 Blessed Committers, chris Reviewed By: O1 Blessed Committers, chris Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25429 --- .gitignore | 1 + .../diviner/workflow/DivinerGenerateWorkflow.php | 3 +-- webroot/rsrc/externals/javelin/docs/javelin.book | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 webroot/rsrc/externals/javelin/docs/javelin.book diff --git a/.gitignore b/.gitignore index d9f44b6bab..4e3a12e5e9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # Diviner /docs/ /.divinercache/ +/webroot/rsrc/externals/javelin/docs/.divinercache/ /src/.cache/ # libphutil diff --git a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php index 866e8b9ee6..0cde772fc3 100644 --- a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php +++ b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php @@ -348,7 +348,7 @@ private function buildAtomizerFutures(array $file_atomizers) { $atomizers[$atomizer][] = $file; } - $root = dirname(phutil_get_library_root('phabricator')); + $root = dirname(phutil_get_library_root('phorge')); $config_root = $this->getConfig('root'); $bar = id(new PhutilConsoleProgressBar()) @@ -363,7 +363,6 @@ private function buildAtomizerFutures(array $file_atomizers) { $this->getBookConfigPath(), $class, $chunk); - $future->setCWD($config_root); $futures[] = $future; diff --git a/webroot/rsrc/externals/javelin/docs/javelin.book b/webroot/rsrc/externals/javelin/docs/javelin.book new file mode 100644 index 0000000000..7c2a0c2274 --- /dev/null +++ b/webroot/rsrc/externals/javelin/docs/javelin.book @@ -0,0 +1,16 @@ +{ + "name" : "javelin", + "title" : "Javelin Documentation", + "short" : "Javelin Docs", + "preface" : "Documentation for developers using Javelin.", + "uri.source": + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", + "rules": { + "(\\.diviner$)": "DivinerArticleAtomizer" + }, + "groups": { + "concepts": { + "name": "Concepts" + } + } +} From 94c0774d80557925480c638a5b917b137ede2269 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Thu, 31 Aug 2023 08:17:01 -0700 Subject: [PATCH 298/425] log deprecation events in Dark Console Summary: `PhutilErrorHandler` already defines "DEPRECATED" error class, but it's not used. I plan to use it to report deprecation warnings. With this change, they will be shown in the Dark Console and in the error logs Ref T15554 This change on its own won't do anything, but is safe to land first. Test Plan: With D25387, pages that have deprecation warnings are now shown, and the deprecation message is available in the dark console (and in log). Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15554 Differential Revision: https://we.phorge.it/D25386 --- .../console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/applications/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php b/src/applications/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php index bc276da0da..fb092ec763 100644 --- a/src/applications/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php +++ b/src/applications/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php @@ -44,6 +44,7 @@ public static function handleErrors($event, $value, $metadata) { 'trace' => $metadata['trace'], ); break; + case PhutilErrorHandler::DEPRECATED: case PhutilErrorHandler::ERROR: // $value is a simple string self::$errors[] = array( From 69c64c1e83e23f11a594686d0abb3f1a74c8d4b1 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Thu, 31 Aug 2023 10:55:13 -0700 Subject: [PATCH 299/425] Teach Commit View about Encoding Summary: If the user specifies a text encoding via the "View Options" dropdown, respect this choice. Ref Q68. Test Plan: Play with the Encoding button in the view Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tinloaf, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25360 --- .../conduit/DiffusionDiffQueryConduitAPIMethod.php | 9 ++++++--- .../diffusion/controller/DiffusionDiffController.php | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php index 694d4bc012..397ccefa86 100644 --- a/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php @@ -23,6 +23,7 @@ protected function defineCustomParamTypes() { return array( 'path' => 'required string', 'commit' => 'optional string', + 'encoding' => 'optional string', ); } @@ -212,18 +213,20 @@ private function getGitOrMercurialResult(ConduitAPIRequest $request) { return $this->getEmptyResult(); } - $parser = $this->getDefaultParser(); + $parser = $this->getDefaultParser($request); $changes = $parser->parseDiff($raw_diff); return $changes; } - private function getDefaultParser() { + private function getDefaultParser(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $parser = new ArcanistDiffParser(); - $try_encoding = $repository->getDetail('encoding'); + $try_encoding = coalesce( + $request->getValue('encoding'), + $repository->getDetail('encoding')); if ($try_encoding) { $parser->setTryEncoding($try_encoding); } diff --git a/src/applications/diffusion/controller/DiffusionDiffController.php b/src/applications/diffusion/controller/DiffusionDiffController.php index 69a473dc91..a844dc23d2 100644 --- a/src/applications/diffusion/controller/DiffusionDiffController.php +++ b/src/applications/diffusion/controller/DiffusionDiffController.php @@ -48,6 +48,7 @@ public function handleRequest(AphrontRequest $request) { array( 'commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), + 'encoding' => $request->getStr('encoding'), )); $drequest->updateSymbolicCommit($data['effectiveCommit']); $raw_changes = ArcanistDiffChange::newFromConduit($data['changes']); From 68c687affdca245bf9ec1bf63575c2631e402e09 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 2 Sep 2023 11:12:11 +0200 Subject: [PATCH 300/425] Fix call to undefined method PhutilJSON::encodeAsObject() Summary: Replace call to undefined method `PhutilJSON::encodeAsObject()` with existing `PhutilJSON::encodeFormatted()` ``` EXCEPTION: (Error) Call to undefined method PhutilJSON::encodeAsObject() at [/src/applications/transactions/xaction/PhabricatorEditEngineDefaultTransaction.php:68] ``` Closes T15603 Test Plan: Grep and read the code in https://we.phorge.it/source/arcanist/browse/master/src/parser/PhutilJSON.php Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15603 Differential Revision: https://we.phorge.it/D25391 --- .../xaction/PhabricatorEditEngineDefaultTransaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/transactions/xaction/PhabricatorEditEngineDefaultTransaction.php b/src/applications/transactions/xaction/PhabricatorEditEngineDefaultTransaction.php index 7b980cdb1a..38011058b6 100644 --- a/src/applications/transactions/xaction/PhabricatorEditEngineDefaultTransaction.php +++ b/src/applications/transactions/xaction/PhabricatorEditEngineDefaultTransaction.php @@ -65,7 +65,7 @@ private function renderDefaultValueAsFallbackText($default_value) { return id(new PhutilJSON())->encodeAsList($default_value); } - return id(new PhutilJSON())->encodeAsObject($default_value); + return id(new PhutilJSON())->encodeFormatted($default_value); } } From cef12d8dc202decc7f4088cb3402f73d74c452f3 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 2 Sep 2023 11:15:14 +0200 Subject: [PATCH 301/425] Fix PHP 8.1 "strlen(null)" exceptions editing a form when custom field of type Date exists Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_scalar()` as a replacement when both string and integers could be passed as a value like here. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_scalar() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Also fix similar warning for `ctype_digit(): Argument of type null will be interpreted as string in the future` by checking for `null` first. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=customOAuthUrlencodeNull, ref.master=788098096e11, ref.customOAuthUrlencodeNull=4f0f2043b7e9), phorge(head=master, ref.master=bcfcd9acfc12) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php:27] ``` ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=customOAuthUrlencodeNull, ref.master=788098096e11, ref.customOAuthUrlencodeNull=4f0f2043b7e9), phorge(head=customFieldDate, ref.master=bcfcd9acfc12, ref.customFieldDate=bcfcd9acfc12) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php:35] ``` ``` EXCEPTION: (RuntimeException) ctype_digit(): Argument of type null will be interpreted as string in the future at [/src/error/PhutilErrorHandler.php:261] arcanist(head=customOAuthUrlencodeNull, ref.master=788098096e11, ref.customOAuthUrlencodeNull=4f0f2043b7e9), phorge(head=customFieldDate, ref.master=bcfcd9acfc12, ref.customFieldDate=bcfcd9acfc12) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> ctype_digit(NULL) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php:77] ``` Closes T15601 Test Plan: After applying these three changes and creating a custom field with `"type": "date"` under `/config/edit/maniphest.custom-field-definitions/`, the website `/transactions/editengine/maniphest.task/view/5/` renders correctly in the browser, showing "This is a preview of the current form configuration." Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15601 Differential Revision: https://we.phorge.it/D25389 --- .../standard/PhabricatorStandardCustomFieldDate.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php index 4aba7543e7..eaaa301160 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php @@ -24,7 +24,7 @@ public function buildOrderIndex() { public function getValueForStorage() { $value = $this->getFieldValue(); - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { return (int)$value; } else { return null; @@ -32,7 +32,7 @@ public function getValueForStorage() { } public function setValueFromStorage($value) { - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $value = (int)$value; } else { $value = null; @@ -74,7 +74,8 @@ private function newDateControl() { // specify as a string. Parse the string into an epoch. $value = $this->getFieldValue(); - if (!ctype_digit($value)) { + if ($value !== null && gettype($value) !== 'integer' && + !ctype_digit($value)) { $value = PhabricatorTime::parseLocalTime($value, $this->getViewer()); } From dc10a7e69ea33fd4b589f0d9e8c9921bc6f94765 Mon Sep 17 00:00:00 2001 From: Benjamin Kausch Date: Sat, 2 Sep 2023 18:52:13 +0200 Subject: [PATCH 302/425] Implement ferret engine in typeahead datasource query for repos Summary: This broadens the typeahead datasource search for repos. Before this patch a repository named "Alligator Simulator" would not be found with the search string "simu...". This is patched with the ferret engine search and indexing features. See T15583 Test Plan: Create repositories with titles with 2 or more words. Search for these repos with the global typeahead search. The search term should begin with the second/third/n-th word of the repo title. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15583 Differential Revision: https://we.phorge.it/D25430 --- .../20230902.repository.01.rebuild-index.php | 6 +++++ .../DiffusionRepositoryDatasource.php | 15 ++++++++----- .../query/PhabricatorRepositoryQuery.php | 22 ------------------- .../PhabricatorRepositoryFulltextEngine.php | 15 +++++++++++-- 4 files changed, 28 insertions(+), 30 deletions(-) create mode 100644 resources/sql/autopatches/20230902.repository.01.rebuild-index.php diff --git a/resources/sql/autopatches/20230902.repository.01.rebuild-index.php b/resources/sql/autopatches/20230902.repository.01.rebuild-index.php new file mode 100644 index 0000000000..aba27ce78c --- /dev/null +++ b/resources/sql/autopatches/20230902.repository.01.rebuild-index.php @@ -0,0 +1,6 @@ +getViewer(); - $raw_query = $this->getRawQuery(); - $query = id(new PhabricatorRepositoryQuery()) - ->setOrder('name') - ->withDatasourceQuery($raw_query); - $repos = $this->executeQuery($query); + ->setViewer($this->getViewer()); + + $this->applyFerretConstraints( + $query, + id(new PhabricatorRepository())->newFerretEngine(), + 'title', + $this->getRawQuery()); + + $repos = $query->execute(); $type_icon = id(new PhabricatorRepositoryRepositoryPHIDType()) ->getTypeIcon(); diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 17a46f7b62..8dead39758 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -9,7 +9,6 @@ final class PhabricatorRepositoryQuery private $types; private $uuids; private $uris; - private $datasourceQuery; private $slugs; private $almanacServicePHIDs; @@ -124,11 +123,6 @@ public function withURIs(array $uris) { return $this; } - public function withDatasourceQuery($query) { - $this->datasourceQuery = $query; - return $this; - } - public function withSlugs(array $slugs) { $this->slugs = $slugs; return $this; @@ -637,22 +631,6 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $this->uuids); } - if (phutil_nonempty_string($this->datasourceQuery)) { - // This handles having "rP" match callsigns starting with "P...". - $query = trim($this->datasourceQuery); - if (preg_match('/^r/', $query)) { - $callsign = substr($query, 1); - } else { - $callsign = $query; - } - $where[] = qsprintf( - $conn, - 'r.name LIKE %> OR r.callsign LIKE %> OR r.repositorySlug LIKE %>', - $query, - $callsign, - $query); - } - if ($this->slugs !== null) { $where[] = qsprintf( $conn, diff --git a/src/applications/repository/search/PhabricatorRepositoryFulltextEngine.php b/src/applications/repository/search/PhabricatorRepositoryFulltextEngine.php index f666af552f..838692c351 100644 --- a/src/applications/repository/search/PhabricatorRepositoryFulltextEngine.php +++ b/src/applications/repository/search/PhabricatorRepositoryFulltextEngine.php @@ -7,10 +7,21 @@ protected function buildAbstractDocument( PhabricatorSearchAbstractDocument $document, $object) { $repo = $object; - $document->setDocumentTitle($repo->getName()); + + $title_fields = array( + $repo->getName(), + $repo->getRepositorySlug(), + ); + $callsign = $repo->getCallsign(); + if ($callsign) { + $title_fields[] = $callsign; + $title_fields[] = 'r'.$callsign; + } + + $document->setDocumentTitle(implode("\n", $title_fields)); $document->addField( PhabricatorSearchDocumentFieldType::FIELD_BODY, - $repo->getRepositorySlug()."\n".$repo->getDetail('description')); + $repo->getDetail('description')); $document->setDocumentCreated($repo->getDateCreated()); $document->setDocumentModified($repo->getDateModified()); From 7868ab3754fad13714640451d664d5bc71b7a02f Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 19 Aug 2023 20:18:45 +0200 Subject: [PATCH 303/425] Make InterpreterBlockRule regex only match on valid interpreter names Summary: With this patch, the underlying exception described in T15372#8537 still remains. However, with this patch, the bug is more contained as it is not triggered when not calling an interpreter (`cowsay`, `figlet`), so Phorge does not crash rendering `noValidInterpreter {{{foo}}} bar` lines but renders them as is (for whatever reasons such lines may exist). See T15372 Test Plan: Enter strings into a comment: * `invalid {{{saysay}}} foo` now renders as plain text instead of crashing * `invalid (invalid) {{{saysay}}} foo` now renders as plain text instead of crashing * `cowsay (invalid) {{{saysay}}} foo` will still crash as before Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25415 --- .../PhutilRemarkupInterpreterBlockRule.php | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupInterpreterBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupInterpreterBlockRule.php index a54e6b8b13..5585ddf541 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupInterpreterBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupInterpreterBlockRule.php @@ -2,13 +2,35 @@ final class PhutilRemarkupInterpreterBlockRule extends PhutilRemarkupBlockRule { - const START_BLOCK_PATTERN = '/^([\w]+)\s*(?:\(([^)]+)\)\s*)?{{{/'; + /** + * Second part of the regex to find stuff like: + * interpreterName {{{ stuff }}} + * interpreterName (options) {{{ stuff }}} + * You have found the kernel of cowsay and figlet. + */ const END_BLOCK_PATTERN = '/}}}\s*$/'; + /** + * Constructs the first part of the regex to find stuff like: + * interpreterName {{{ stuff }}} + * interpreterName (options) {{{ stuff }}} + * The exact regex is constructed from the available interpreters. + * @return string First part of interpreters regex + */ + private function getStartBlockPattern() { + $interpreters = id(new PhutilClassMapQuery()) + ->setAncestorClass('PhutilRemarkupBlockInterpreter') + ->execute(); + $interpreters_regex = mpull($interpreters, 'getInterpreterName'); + $interpreters_regex = array_map('preg_quote', $interpreters_regex); + $interpreters_regex = implode('|', $interpreters_regex); + return "/^($interpreters_regex)\s*(?:\(([^)]+)\)\s*)?{{{/"; + } + public function getMatchingLineCount(array $lines, $cursor) { $num_lines = 0; - if (preg_match(self::START_BLOCK_PATTERN, $lines[$cursor])) { + if (preg_match(self::getStartBlockPattern(), $lines[$cursor])) { $num_lines++; while (isset($lines[$cursor])) { @@ -33,7 +55,7 @@ public function markupText($text, $children) { } $matches = null; - preg_match(self::START_BLOCK_PATTERN, head($lines), $matches); + preg_match(self::getStartBlockPattern(), head($lines), $matches); $argv = array(); if (isset($matches[2])) { @@ -49,7 +71,7 @@ public function markupText($text, $children) { } $lines[$first_key] = preg_replace( - self::START_BLOCK_PATTERN, + self::getStartBlockPattern(), '', $lines[$first_key]); $lines[$last_key] = preg_replace( From 1720209f16c3962ac108c99b9a3dbc00f72a42da Mon Sep 17 00:00:00 2001 From: sten Date: Sat, 29 Jul 2023 20:42:49 +0100 Subject: [PATCH 304/425] Various PHP 8.1 strlen(null) fixes for Dashboard Panels Summary: In the dashboard application (https://my.phorge.site/dashboard/), when creating panels, adding panels to tab panels, and viewing query panels, we get a variety of strlen(null) errors under PHP 8.1. This fixes all the ones seen. Fixes T15574 Test Plan: See T15574 Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15574 Differential Revision: https://we.phorge.it/D25367 --- .../dashboard/PhabricatorDashboardAdjustController.php | 2 +- .../panel/PhabricatorDashboardPanelTabsController.php | 4 ++-- .../engine/PhabricatorDashboardPanelRenderingEngine.php | 1 + .../dashboard/paneltype/PhabricatorDashboardTabsPanelType.php | 2 +- .../PhabricatorFerretSearchEngineExtension.php | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php index fc04b94603..4f3937e3ae 100644 --- a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php +++ b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php @@ -32,7 +32,7 @@ public function handleRequest(AphrontRequest $request) { $panel_ref = null; $panel_key = $request->getStr('panelKey'); - if (strlen($panel_key)) { + if ($panel_key !== null && strlen($panel_key)) { $panel_ref = $ref_list->getPanelRef($panel_key); if (!$panel_ref) { return new Aphront404Response(); diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php index 193e4580d6..7ea0345ceb 100644 --- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php +++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php @@ -41,12 +41,12 @@ public function handleRequest(AphrontRequest $request) { $op = $request->getURIData('op'); $after = $request->getStr('after'); - if (!strlen($after)) { + if ($after === '') { $after = null; } $target = $request->getStr('target'); - if (!strlen($target)) { + if ($target === '') { $target = null; } diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php index cce6ee768f..3b9c3ad192 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php @@ -155,6 +155,7 @@ public function renderPanel() { return $this->renderNormalPanel(); } catch (Exception $ex) { + phlog($ex); return $this->renderErrorPanel( $panel->getName(), pht( diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php index 9bc84d820e..3cf73b539e 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php @@ -100,7 +100,7 @@ public function renderPanelContent( $panel_id = idx($tab_spec, 'panelID'); $subpanel = idx($panels, $panel_id); - $name = idx($tab_spec, 'name'); + $name = coalesce(idx($tab_spec, 'name'), ''); if (!strlen($name)) { if ($subpanel) { $name = $subpanel->getName(); diff --git a/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php b/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php index 55b7c2225a..2544f8b7da 100644 --- a/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php +++ b/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php @@ -27,7 +27,7 @@ public function applyConstraintsToQuery( PhabricatorSavedQuery $saved, array $map) { - if (!strlen($map['query'])) { + if (!(isset($map['query']) && strlen($map['query']))) { return; } From f7d9d95b79f781eea88f37058aa0ffa5622995ff Mon Sep 17 00:00:00 2001 From: sten Date: Tue, 5 Sep 2023 17:02:58 +0100 Subject: [PATCH 305/425] Fix Diviner strlen(null) error when clicking on a link Summary: Fix issue whereby clicking on a link in Diviner (eg https://my.phorge.site/diviner/find/?name=Differential_User_Guide&type=article&jump=1) results in a strlen(null) error under PHP 8.1 Fixes T15635 Test Plan: * Go into the Diviner differential page /book/phorge/article/differential/ * Click on the link to Differential User Guide: Inline Comments Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15635 Differential Revision: https://we.phorge.it/D25433 --- src/applications/diviner/controller/DivinerFindController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diviner/controller/DivinerFindController.php b/src/applications/diviner/controller/DivinerFindController.php index 1bedd65b06..2e3b121706 100644 --- a/src/applications/diviner/controller/DivinerFindController.php +++ b/src/applications/diviner/controller/DivinerFindController.php @@ -32,7 +32,7 @@ public function handleRequest(AphrontRequest $request) { } $context = $request->getStr('context'); - if (strlen($context)) { + if (phutil_nonempty_string($context)) { $query->withContexts(array($context)); } From a65061ddc33da26a203d3a7b73ff0e491a5b9788 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 9 Sep 2023 20:54:44 +0200 Subject: [PATCH 306/425] Fix PHP 8.1 "strlen(null)" exception rendering Task with empty custom date field Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=customOAuthUrlencodeNull, ref.master=df6c315ace5f, ref.customOAuthUrlencodeNull=c69b9749027f), phorge(head=master, ref.master=7868ab3754fa) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php:14] ``` Closes T15632 Test Plan: Have a custom field with type=date defined in maniphest.custom-field-definitions and edit an existing Task changing something else, keeping that field as empty. Reviewers: O1 Blessed Committers, Sten, valerio.bozzolan Reviewed By: O1 Blessed Committers, Sten, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15632 Differential Revision: https://we.phorge.it/D25431 --- .../customfield/standard/PhabricatorStandardCustomFieldDate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php index eaaa301160..ca56816b42 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php @@ -11,7 +11,7 @@ public function buildFieldIndexes() { $indexes = array(); $value = $this->getFieldValue(); - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $indexes[] = $this->newNumericIndex((int)$value); } From a0fb344b94883d54fb958aa78f9a8c715ffae185 Mon Sep 17 00:00:00 2001 From: sten Date: Tue, 5 Sep 2023 15:14:06 +0100 Subject: [PATCH 307/425] Fix PHP 8.1 auth view strlen(null) error Summary: Trying to view a Jira auth provider from https://my.phorge.site/auth/ results in strlen(): Passing null to parameter #1 ($string) of type string is deprecated This change fixes it Fixes T15634 Test Plan: Go to https://my.phorge.site/auth/ and have a Jira already setup. Click on Jira auth provider Confirm the page is displayed and no error is generated. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15634 Differential Revision: https://we.phorge.it/D25432 --- .../auth/storage/PhabricatorAuthProviderConfigTransaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php b/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php index f60ba8c734..d7f974cb15 100644 --- a/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php +++ b/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php @@ -150,7 +150,7 @@ public function getTitle() { $provider = $this->getProvider(); if ($provider) { $title = $provider->renderConfigPropertyTransactionTitle($this); - if (strlen($title)) { + if (phutil_nonempty_string($title)) { return $title; } } From 91faf16cace9308440087d64f17c068e04313c50 Mon Sep 17 00:00:00 2001 From: sten Date: Wed, 6 Sep 2023 07:55:56 +0100 Subject: [PATCH 308/425] Add documentation for cowsay Summary: Cowsay is great, but what if you want to talk like a koala? This diff adds diviner documentation on how to use the additional templates in externals/cowsay/cows/ Test Plan: bin/diviner generate https://my.phorge.site/book/phorge/article/differential_cowsay/ Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25434 --- src/docs/user/userguide/differential.diviner | 1 + .../user/userguide/differential_faq.diviner | 9 ++- src/docs/user/userguide/remarkup.diviner | 4 ++ .../user/userguide/remarkup_cowsay.diviner | 61 +++++++++++++++++++ 4 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/docs/user/userguide/remarkup_cowsay.diviner diff --git a/src/docs/user/userguide/differential.diviner b/src/docs/user/userguide/differential.diviner index d48ff4ceff..fad92dfa4e 100644 --- a/src/docs/user/userguide/differential.diviner +++ b/src/docs/user/userguide/differential.diviner @@ -66,6 +66,7 @@ Continue by: - diving into the details of inline comments in @{article:Differential User Guide: Inline Comments}; or - reading the FAQ at @{article:Differential User Guide: FAQ}; or + - learning how to use markup in comments at @{article:Remarkup Reference}; or - learning about test plans in @{article:Differential User Guide: Test Plans}; or - learning more about Herald in @{article:Herald User Guide}. diff --git a/src/docs/user/userguide/differential_faq.diviner b/src/docs/user/userguide/differential_faq.diviner index 0df1f7b1c9..979daf762a 100644 --- a/src/docs/user/userguide/differential_faq.diviner +++ b/src/docs/user/userguide/differential_faq.diviner @@ -41,8 +41,9 @@ always "request review" of an accepted revision, with a comment like: If authors are being jerks about this (making sweeping changes as soon as they get an accept), solve the problem socially by telling them to stop being jerks. -Unless you've configured additional layers of enforcement, there's nothing -stopping them from silently changing the code before pushing it, anyway. +Unless you've configured additional layers of enforcement (by +using @{article:Herald}), there's nothing stopping them from silently changing +the code before pushing it, anyway. = How can I enable syntax highlighting? = @@ -50,6 +51,10 @@ stopping them from silently changing the code before pushing it, anyway. You need to install and configure **Pygments** to highlight anything else than PHP. See the `pygments.enabled` configuration setting. += What formatting can be used in comments? = + +Phorge implements a markup language similar to other markup languages like +Markdown and Wiki markup. See @{article:Remarkup Reference}. = What do the very light green and red backgrounds mean? = diff --git a/src/docs/user/userguide/remarkup.diviner b/src/docs/user/userguide/remarkup.diviner index 4fb8fc75f8..fa0128127c 100644 --- a/src/docs/user/userguide/remarkup.diviner +++ b/src/docs/user/userguide/remarkup.diviner @@ -732,3 +732,7 @@ to the first anchor with "xyz" as a prefix of the anchor name. Remarkup editors provide a fullscreen composition mode. This can make it easier to edit large blocks of text, or improve focus by removing distractions. You can exit **Fullscreen** mode by clicking the button again or by pressing escape. + +See Also +======== +* @{article:Remarkup Reference: Cowsay} diff --git a/src/docs/user/userguide/remarkup_cowsay.diviner b/src/docs/user/userguide/remarkup_cowsay.diviner new file mode 100644 index 0000000000..696189afb6 --- /dev/null +++ b/src/docs/user/userguide/remarkup_cowsay.diviner @@ -0,0 +1,61 @@ +@title Remarkup Reference: Cowsay +@group userguide + +Overview +-------- + +Cowsay is an application by Tony Monroe which has been ported over to +Phabricator/Phorge to allow your comments to be voiced by +a cow. + +Basic Usage +----------- + +A basic example of using cowsay would be to add a comment + cowsay{{{Great work!}}} +which generates: +``` + _____________ +< Great work! > + ------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +``` + +Other Cowsay Templates +---------------------- +Other templates are available in externals/cowsay/cows/, and you can specify +one by calling cowsay with the 'cow' parameter. eg: + cowsay(cow='tux'){{{Great work!}}} +generates +``` + _____________ +< Great work! > + ------------- + \ + \ + .--. + |o_o | + |:_/ | + // \ \ + (| | ) + /'\_ _/`\ + \___)=(___/ +``` + +Other Parameters +---------------- +* think (set to 1 for thinking bubbles) +* eyes (default 'oo') +* tongue (default ' ', try 'P') + +See Also +======== +* @{article:Remarkup Reference} +* Have you tried figlet: +``` +figlet{{{figlet generates big text!}}} +``` From 903d71e67d84e04337d6dc4d9febcd71eb6f9c53 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 25 Sep 2023 08:06:09 +0200 Subject: [PATCH 309/425] Workboard: Milestone Name easily editable (instead of surfing 3 pages) Summary: After this change, a new input field "Milestone Name" appears in the "Edit" menu of a Milestone: | Before | After | |-----------|-----------| | {F314008} | {F314005} | So you can quickly change the name of your Milestones, from a Workboard. Before this change, from a Workboard, this was the way to rename a Milestone: 1. click on the Milestone name (yes, that is a link) 2. click on Manage 3. click on Edit Details 4. rename 5. Save 6. Manually visit again the Project's Workboard After this change, from a Workboard, you just need to: 1. click on Milestone > Edit 2. click on Edit Column 3. rename 4. Save Example usage: {F314015} This does not change the level of permissions needed: if you have not enough permissions to see or edit a Milestone, you cannot access this feature indeed. In short, this is just a frontend change, keeping current policies as-is. Closes T15143 Test Plan: Create a Project or use an existing editable one. Create a Milestone called "Test Milestone". You can create Milestones visiting the Project's menu {nav icon=sitemap,name=Subprojects > icon=plus,name=Create next milestone} Visit the Project's Workboard. Find the column "Test Milestone". Click the Edit button on a Milestone, and: - try to save another name: it must work - try to save an empty name: nice error message shown - try to save both the score points and the name: it must work - try to save "FOO" as Points: you still see the error message Also: - do the same for the Backlog column: it still works (name still allowed to be empty) - do the same for a "normal" Column (not the Backlog): it still work (name still __not__ allowed to be empty) Reviewers: O1 Blessed Committers, Cigaryno, 20after4, waldyrious Reviewed By: O1 Blessed Committers, Cigaryno, 20after4, waldyrious Subscribers: waldyrious, brennen, aklapper, 20after4, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15143 Differential Revision: https://we.phorge.it/D25066 --- ...PhabricatorProjectColumnEditController.php | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectColumnEditController.php b/src/applications/project/controller/PhabricatorProjectColumnEditController.php index 9ddb2b7d8a..bf579b274f 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnEditController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnEditController.php @@ -45,16 +45,32 @@ public function handleRequest(AphrontRequest $request) { $e_name = null; $e_limit = null; + $e_milestone_name = null; $v_limit = $column->getPointLimit(); $v_name = $column->getName(); + $proxy = $column->getProxy(); + + // Is this a normal Column? Example: when true, this is not a Milestone. + $is_column = !$proxy; + + // Is this a Milestone? Example: when true, this is not a normal Column. + $is_milestone = $proxy && $proxy->isMilestone(); + + // Milestone name, eventually coming from the proxed object. + $v_milestone_name = null; + if ($is_milestone) { + $v_milestone_name = $proxy->getName(); + } + $validation_exception = null; $view_uri = $project->getWorkboardURI(); if ($request->isFormPost()) { $v_name = $request->getStr('name'); $v_limit = $request->getStr('limit'); + $v_milestone_name = $request->getStr('milestone.name'); if ($is_new) { $column->setProjectPHID($project->getPHID()); @@ -74,14 +90,22 @@ public function handleRequest(AphrontRequest $request) { } $xactions = array(); + $xactions_milestone = array(); $type_name = PhabricatorProjectColumnNameTransaction::TRANSACTIONTYPE; $type_limit = PhabricatorProjectColumnLimitTransaction::TRANSACTIONTYPE; + $type_project_name = PhabricatorProjectNameTransaction::TRANSACTIONTYPE; - if (!$column->getProxy()) { + if ($is_column) { + // Transaction for Column name. $xactions[] = id(new PhabricatorProjectColumnTransaction()) ->setTransactionType($type_name) ->setNewValue($v_name); + } else if ($is_milestone) { + // Transaction for Milestone name (that internally is a Project Name). + $xactions_milestone[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType($type_project_name) + ->setNewValue($v_milestone_name); } $xactions[] = id(new PhabricatorProjectColumnTransaction()) @@ -94,24 +118,53 @@ public function handleRequest(AphrontRequest $request) { ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->applyTransactions($column, $xactions); - return id(new AphrontRedirectResponse())->setURI($view_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { + // Error messages related to the Column (like invalid Name, etc.) $e_name = $ex->getShortMessage($type_name); $e_limit = $ex->getShortMessage($type_limit); $validation_exception = $ex; } + + // Save Milestone-related stuff but only if there were no prior problems + // and only if we have changes. + if (!$validation_exception && $xactions_milestone) { + try { + $editor_milestone = id(new PhabricatorProjectTransactionEditor()) + ->setActor($viewer) + ->setContinueOnNoEffect(true) + ->setContentSourceFromRequest($request) + ->applyTransactions($proxy, $xactions_milestone); + } catch (PhabricatorApplicationTransactionValidationException $ex) { + // Error messages related to the Milestone (like invalid Name, etc.) + $e_milestone_name = $ex->getShortMessage($type_project_name); + $validation_exception = $ex; + } + } + + // Refresh the page only if there are no errors to show. + if (!$validation_exception) { + return id(new AphrontRedirectResponse())->setURI($view_uri); + } } $form = id(new AphrontFormView()) ->setUser($request->getUser()); - if (!$column->getProxy()) { + // Show the most appropriate input field for the name. + if ($is_column) { $form->appendChild( id(new AphrontFormTextControl()) ->setValue($v_name) ->setLabel(pht('Name')) ->setName('name') ->setError($e_name)); + } else if ($is_milestone) { + $form->appendChild( + id(new AphrontFormTextControl()) + ->setValue($v_milestone_name) + ->setLabel(pht('Milestone Name')) + ->setName('milestone.name') + ->setError($e_milestone_name)); } $form->appendChild( From 03afb97ff20bd5480ecfa0fa809e3b751bd973e8 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 27 Sep 2023 18:50:08 +0200 Subject: [PATCH 310/425] Correct Transaction text when changing Diffusion URI I/O type Summary: After changing the I/O type of a Diffusion repository URI, the transaction history incorrectly shows a message which talks about changing the URI's Display type instead. Closes T15648 Test Plan: Change the I/O type of a Diffusion repo URI and look at that URIs history. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15648 Differential Revision: https://we.phorge.it/D25443 --- .../repository/storage/PhabricatorRepositoryURITransaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php b/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php index 241a95dad9..49d1a44943 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php +++ b/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php @@ -57,7 +57,7 @@ public function getTitle() { $new_label = idx(idx($map, $new, array()), 'label', $new); return pht( - '%s changed the display type for this URI from "%s" to "%s".', + '%s changed the I/O type for this URI from "%s" to "%s".', $this->renderHandleLink($author_phid), $old_label, $new_label); From 71b273a62281204a7bcce39fea9ebc16157f3ef7 Mon Sep 17 00:00:00 2001 From: Francesco Gazzetta Date: Tue, 3 Oct 2023 13:36:06 +0200 Subject: [PATCH 311/425] Update path in startup error Summary: The error mentioned a "phabricator" directory, but according to the docs it has been migrated to "phorge". Test Plan: Move the "arcanist" directory somewhere else, run something from phorge/bin/. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25445 --- support/startup/PhabricatorStartup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/startup/PhabricatorStartup.php b/support/startup/PhabricatorStartup.php index 58031013ad..b9d659d39b 100644 --- a/support/startup/PhabricatorStartup.php +++ b/support/startup/PhabricatorStartup.php @@ -210,7 +210,7 @@ public static function loadCoreLibraries() { if (!$ok) { self::didFatal( 'Unable to load the "Arcanist" library. Put "arcanist/" next to '. - '"phabricator/" on disk.'); + '"phorge/" on disk.'); } // Load Phabricator itself using the absolute path, so we never end up doing From f727f17bc24a03cf1c6c82be3778e58e4fa282d7 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 10 Oct 2023 07:16:05 +0200 Subject: [PATCH 312/425] Fix regression in DiffusionDiffQueryConduitAPIMethod Summary: Fix the following error you may encounter in production: Too few arguments to function DiffusionDiffQueryConduitAPIMethod::getDefaultParser(), 0 passed in /var/www/phorge/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php on line 156 and exactly 1 expected It was caused by the lack of a new mandatory parameter in a method. Closes T15649 Test Plan: Check with your big eyes that a ConduitAPIRequest is passed to getDefaultParser(). Reviewers: avivey, O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15649 Differential Revision: https://we.phorge.it/D25444 --- .../diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php index 397ccefa86..d97f09cc4e 100644 --- a/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php @@ -153,7 +153,7 @@ protected function getSVNResult(ConduitAPIRequest $request) { $arcanist_changes = DiffusionPathChange::convertToArcanistChanges( $path_changes); - $parser = $this->getDefaultParser(); + $parser = $this->getDefaultParser($request); $parser->setChanges($arcanist_changes); $parser->forcePath($path->getPath()); $changes = $parser->parseDiff($raw_diff); From 94aefaa31d4c781d2553345e2994155067d9f238 Mon Sep 17 00:00:00 2001 From: "roberto.urbani" Date: Fri, 13 Oct 2023 10:46:21 +0200 Subject: [PATCH 313/425] Replacing the deprecated -moz-outline-style with outline-style Summary: Replacing the deprecated -moz-outline-style with outline-style: none. According to "Can I Use", this change is supported in whatever non-ridiculously-old browser web in the universe, like Firefox 2 and Chrome 4 and Internet Explorer 8. https://caniuse.com/outline Ref T15585 Test Plan: The outline in the
tags should look as usual, that is, none. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15585 Differential Revision: https://we.phorge.it/D25416 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/core/core.css | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 8f791cbeda..e40b474c14 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '1a5169fe', + 'core.pkg.css' => '3c4918b0', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '2431def2', @@ -109,7 +109,7 @@ 'rsrc/css/application/slowvote/slowvote.css' => '1694baed', 'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd', 'rsrc/css/application/uiexample/example.css' => 'b4795059', - 'rsrc/css/core/core.css' => 'a708bd25', + 'rsrc/css/core/core.css' => 'b3a5928e', 'rsrc/css/core/remarkup.css' => '3480e1fe', 'rsrc/css/core/syntax.css' => '548567f6', 'rsrc/css/core/z-index.css' => 'ac3bfcd4', @@ -772,7 +772,7 @@ 'phabricator-busy' => '5202e831', 'phabricator-chatlog-css' => 'abdc76ee', 'phabricator-content-source-view-css' => 'cdf0d579', - 'phabricator-core-css' => 'a708bd25', + 'phabricator-core-css' => 'b3a5928e', 'phabricator-countdown-css' => 'bff8012f', 'phabricator-darklog' => '3b869402', 'phabricator-darkmessage' => '26cd4b73', diff --git a/webroot/rsrc/css/core/core.css b/webroot/rsrc/css/core/core.css index ccb3d1697f..4a9d3b394b 100644 --- a/webroot/rsrc/css/core/core.css +++ b/webroot/rsrc/css/core/core.css @@ -84,7 +84,7 @@ h2 { } a { - -moz-outline-style: none; + outline-style: none; text-decoration: none; color: {$anchor}; cursor: pointer; From 2d635fb76e9b20cce53ed68fe1dfd4daa79aa8c2 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 16 Oct 2023 08:40:23 +0200 Subject: [PATCH 314/425] Dashboard Panel: fix first tab sometime not opened anymore Summary: After this change the first Tab ID is always selected, so we don't risk to have no tabs opened by default under some corner cases. This is vaguely better than the original logic that relied on the fact that (0=="0") is true. Besides being a vaguely weird emoticon as well, now that I look at it better. Original logic: https://we.phorge.it/source/phorge/browse/master/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php;c43618a3a8bb021936fad687f04cb1a95faa23e4$289 This fixes a regression that appeared in the cute Wikimedia Phabricator (that now is really Wikimedia Phorge). Their homepage should be gorgeous again, without blank space due to unopened silly tabs. Closes T15651 Test Plan: - Have a Tab Panel (/dashboard/panel/). - Be sure that the first Tab is automatically selected (again?) Reviewers: aklapper, O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15651 Differential Revision: https://we.phorge.it/D25447 --- .../paneltype/PhabricatorDashboardTabsPanelType.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php index 3cf73b539e..6b4f2fe5d6 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php @@ -85,10 +85,12 @@ public function renderPanelContent( $rename_uri = id(new PhutilURI($rename_uri)) ->replaceQueryParam('contextPHID', $context_phid); - $selected = 0; - $key_list = array_keys($config); + // In the future we may persist which panel was selected. + // At the moment we have always selected the first one. + $selected = (string)head($key_list); + $next_keys = array(); $prev_keys = array(); for ($ii = 0; $ii < count($key_list); $ii++) { @@ -111,7 +113,8 @@ public function renderPanelContent( $name = pht('Unnamed Tab'); } - $is_selected = (string)$idx === (string)$selected; + // The $idx can be something like "0", "1" or "asdasd98". + $is_selected = (string)$idx === $selected; $tab_view = id(new PHUIListItemView()) ->setHref('#') @@ -282,7 +285,8 @@ public function renderPanelContent( $panel_content = pht('(Invalid Panel)'); } - $is_selected = (string)$idx === (string)$selected; + // Note that $idx can be something like "0", "1" or "asdasd98". + $is_selected = (string)$idx === $selected; $content_id = celerity_generate_unique_node_id(); From 7b0021a03cb28cf039d05530844874773e8258c2 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 16 Sep 2023 14:13:21 +0200 Subject: [PATCH 315/425] Fix "Undefined index" exception setting Meme text Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) Undefined index: above at [/src/error/PhutilErrorHandler.php:251] arcanist(), phorge() #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer, array) called at [/src/applications/macro/engine/PhabricatorMemeEngine.php:276] ``` Closes T15637 Test Plan: Create a meme called "angrycat" from the /macro/ page, and try a comment like this, expecting no nuclear implosion: {meme, src=angrycat, below=} {meme, src=angrycat, above=} {meme, src=angrycat, below=, above=} {meme, src=angrycat, below= , above= } {meme, src=angrycat, below=asd} {meme, src=angrycat, above=asd} {meme, src=angrycat, above=asd, below=dsa} {meme, src=angrycat, above= asd , below= dsa } Also carefully read the code with your big eyes, keeping in mind that strlen does not accept passing `null` in PHP 8, and looking at what we did in rPb4cfe56f03b44615ac9251aed8d74bf13b085051. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15637 Differential Revision: https://we.phorge.it/D25437 --- src/applications/macro/engine/PhabricatorMemeEngine.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/macro/engine/PhabricatorMemeEngine.php b/src/applications/macro/engine/PhabricatorMemeEngine.php index e1befc20a2..71a45033da 100644 --- a/src/applications/macro/engine/PhabricatorMemeEngine.php +++ b/src/applications/macro/engine/PhabricatorMemeEngine.php @@ -273,7 +273,7 @@ private function newGDAsset(PhabricatorFile $template, $data) { $size = $metrics['size']; $above = $this->getAboveText(); - if (strlen($above)) { + if ($above !== null && phutil_nonempty_string(trim($above))) { $x = (int)floor(($dx - $metrics['text']['above']['width']) / 2); $y = $metrics['text']['above']['height'] + 12; @@ -281,7 +281,7 @@ private function newGDAsset(PhabricatorFile $template, $data) { } $below = $this->getBelowText(); - if (strlen($below)) { + if ($below !== null && phutil_nonempty_string(trim($below))) { $x = (int)floor(($dx - $metrics['text']['below']['width']) / 2); $y = $dy - 12 - $metrics['text']['below']['descend']; From 318d7a61feab3f632febb76e915c4e19dabf53f4 Mon Sep 17 00:00:00 2001 From: sten Date: Wed, 25 Oct 2023 09:40:36 +0100 Subject: [PATCH 316/425] Fix PhabricatorAuthCSRFEngine.php strncmp(null) PHP 8.1 error Summary: Update PhabricatorAuthCSRFEngine.php such that it doesn't fall over when provided with a null CSRF token under PHP 8.1 Fixes T15654 Test Plan: Do a POST request to phorge. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15654 Differential Revision: https://we.phorge.it/D25449 --- src/applications/auth/engine/PhabricatorAuthCSRFEngine.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/applications/auth/engine/PhabricatorAuthCSRFEngine.php b/src/applications/auth/engine/PhabricatorAuthCSRFEngine.php index fcb8c13ab7..856b334039 100644 --- a/src/applications/auth/engine/PhabricatorAuthCSRFEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthCSRFEngine.php @@ -47,7 +47,10 @@ public function isValidToken($token) { // We expect a BREACH-mitigating token. See T3684. $breach_prefix = $this->getBREACHPrefix(); $breach_prelen = strlen($breach_prefix); - if (strncmp($token, $breach_prefix, $breach_prelen) !== 0) { + if ( + $token === null || + strncmp($token, $breach_prefix, $breach_prelen) !== 0 + ) { return false; } From 629fa368cb9ae2331028e1504bb95f55a50d7d4b Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 25 Oct 2023 19:07:44 +0200 Subject: [PATCH 317/425] Calendar: fix creation of ICS Files Summary: It seems that, in an attempt to make ICS URI(s) editable, we broke the ability to manually import ICS files. Whops. The cause is, the URI needs to be put inside its dedicated import engine, and not the general one. Since the intention of T15137 was to be able to edit this field, we have done that in the right way this time. So, you see the field, not just in creation mode. Thanks to the kind aklapper for reporting. Ref T15137 Closes T15619 Rollback 02a4f8b0c8f1279fc0040ad8077942fd8b0d948b Test Plan: - visit /calendar/import/ and: - create/edit an ICS File Import (now works again) - create/edit an ICS URI Import (still work) - try looking at an "ICS Import page" as author (URI still visible) - try looking at an "ICS Import page" without Edit permissions (URI still omitted correctly) Reviewers: aklapper, O1 Blessed Committers, 20after4 Reviewed By: aklapper, O1 Blessed Committers, 20after4 Subscribers: 20after4, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15619, T15137 Differential Revision: https://we.phorge.it/D25448 --- .../PhabricatorCalendarImportEditEngine.php | 15 ------------ .../PhabricatorCalendarICSURIImportEngine.php | 23 +++++++++++-------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php index 9a4a7c0107..7be3969671 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php @@ -83,12 +83,6 @@ protected function buildCustomEditFields($object) { $engine = $object->getEngine(); $can_trigger = $engine->supportsTriggers($object); - // calendar URI import - // note that it can contains a secret token - // if we are here you have enough privileges to edit and see the value - $uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI; - $uri = $object->getParameter($uri_key); - $fields = array( id(new PhabricatorTextEditField()) ->setKey('name') @@ -100,15 +94,6 @@ protected function buildCustomEditFields($object) { ->setConduitTypeDescription(pht('New import name.')) ->setPlaceholder($object->getDisplayName()) ->setValue($object->getName()), - id(new PhabricatorTextEditField()) - ->setKey('uri') - ->setLabel(pht('URI')) - ->setDescription(pht('URI to import.')) - ->setTransactionType( - PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE) - ->setConduitDescription(pht('URI to import.')) - ->setConduitTypeDescription(pht('New URI.')) - ->setValue($uri), id(new PhabricatorBoolEditField()) ->setKey('disabled') ->setOptions(pht('Active'), pht('Disabled')) diff --git a/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php index bd52ec5bc2..6c992e0836 100644 --- a/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php @@ -58,16 +58,19 @@ public function newEditEngineFields( PhabricatorCalendarImport $import) { $fields = array(); - if ($engine->getIsCreate()) { - $fields[] = id(new PhabricatorTextEditField()) - ->setKey('uri') - ->setLabel(pht('URI')) - ->setDescription(pht('URI to import.')) - ->setTransactionType( - PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE) - ->setConduitDescription(pht('URI to import.')) - ->setConduitTypeDescription(pht('New URI.')); - } + // If you are here, you already have the "can edit" capability. + // So you are supposed to be able to edit again your Calendar import URI. + $uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI; + $uri = $import->getParameter($uri_key); + $fields[] = id(new PhabricatorTextEditField()) + ->setKey('uri') + ->setLabel(pht('URI')) + ->setDescription(pht('URI to import.')) + ->setTransactionType( + PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE) + ->setConduitDescription(pht('URI to import.')) + ->setConduitTypeDescription(pht('New URI.')) + ->setValue($uri); return $fields; } From d4b110af260caab56936aab543041b0904e4d02a Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 26 Oct 2023 22:44:48 +0200 Subject: [PATCH 318/425] Remove unused variable $info in DiffusionCloneController.php Summary: Left-over from rPa6b550ba0394284441ee55d11e276a05eb568ad9 Closes T15655 Test Plan: Carefully read the source code file to look out for another appearance; after hours of fruitless searching get slightly disappointed. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15655 Differential Revision: https://we.phorge.it/D25451 --- .../diffusion/controller/DiffusionCloneController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionCloneController.php b/src/applications/diffusion/controller/DiffusionCloneController.php index 8bc4faa1a2..bf118bcf6d 100644 --- a/src/applications/diffusion/controller/DiffusionCloneController.php +++ b/src/applications/diffusion/controller/DiffusionCloneController.php @@ -49,8 +49,6 @@ public function handleRequest(AphrontRequest $request) { ->appendChild(pht('Repository has no URIs set.')); } - $info = null; - // Try to load alternatives. This may fail for repositories which have not // cloned yet. If it does, just ignore it and continue. try { From 222a6fea0a1d9318198fe73a0501cf6444420ce3 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 4 Nov 2023 19:34:00 +0100 Subject: [PATCH 319/425] Fix PHP 8.1 "strlen(null)" exception on Diffusion repo URIs page after repo creation Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` ERROR 8192: strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/var/www/html/phorge/phorge/src/applications/repository/storage/PhabricatorRepository.php:348] ``` Closes T15658 Test Plan: Create an empty new Git repository, go to its URIs management page. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15658 Differential Revision: https://we.phorge.it/D25454 --- src/applications/repository/storage/PhabricatorRepository.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 4a1c923622..b2855c0c06 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -345,12 +345,12 @@ public function getCloneName() { // Make some reasonable effort to produce reasonable default directory // names from repository names. - if (!strlen($name)) { + if (!phutil_nonempty_string($name)) { $name = $this->getName(); $name = phutil_utf8_strtolower($name); $name = preg_replace('@[ -/:->]+@', '-', $name); $name = trim($name, '-'); - if (!strlen($name)) { + if (!phutil_nonempty_string($name)) { $name = $this->getCallsign(); } } From 887e344c19af9ee6f4855227f8a215a491974794 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 28 Oct 2023 20:23:42 +0200 Subject: [PATCH 320/425] Fix project page 404 after rename and removing new name from alias slugs Summary: When renaming a project to a slug already listed under Additional hashtags and explicitly also removing that to-become slug, accessing the project via the URL `/tag/projectname/` returned a 404 until someone added the current project name explicitly under "Additional hashtags" again. In that case, do not remove the alternative hashtag to avoid the 404. Closes T15636 Test Plan: See steps in https://we.phorge.it/T15636 Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15636 Differential Revision: https://we.phorge.it/D25453 --- .../project/editor/PhabricatorProjectTransactionEditor.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index eb57c39b2c..9c6e5cf4cc 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -295,6 +295,12 @@ public function addSlug(PhabricatorProject $project, $slug, $force) { } public function removeSlugs(PhabricatorProject $project, array $slugs) { + // Do not allow removing the project's primary slug which the edit form + // may allow through a series of renames/moves. See T15636 + if (($key = array_search($project->getPrimarySlug(), $slugs)) !== false) { + unset($slugs[$key]); + } + if (!$slugs) { return; } From 37ecdf23369740a740ca8d9f3bf7daef6f7077bd Mon Sep 17 00:00:00 2001 From: "roberto.urbani" Date: Mon, 6 Nov 2023 08:26:37 +0000 Subject: [PATCH 321/425] Improving UX for ignoring timezone conflicts Summary: When there is a new timezone conflict, you will be able to ignore it with a checkbox. Fix T15349 Preview: {F343198} Test Plan: Having a conflicting timezone, click the notification so the usual popup appears. There is a checkbox, leave it checked to ignore the current conflict, uncheck to manually resolve the conflict by selecting one of the available timezones. Reviewers: O1 Blessed Committers, valerio.bozzolan, avivey Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey Subscribers: speck, waldyrious, avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15349 Differential Revision: https://we.phorge.it/D25420 --- .../PhabricatorSettingsTimezoneController.php | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php index 8ae2704161..9cde82fdf0 100644 --- a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php +++ b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php @@ -31,11 +31,12 @@ public function handleRequest(AphrontRequest $request) { $did_calibrate = false; if ($request->isFormPost()) { $timezone = $request->getStr('timezone'); + $ignore_conflict_checkbox = $request->getInt('ignoreConflict'); $pref_ignore = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY; $pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY; - if ($timezone == 'ignore') { + if ($timezone === 'ignore' || $ignore_conflict_checkbox) { $this->writeSettings( array( $pref_ignore => $client_offset, @@ -83,20 +84,29 @@ public function handleRequest(AphrontRequest $request) { $guess = 'ignore'; } - $current_zone = $viewer->getTimezoneIdentifier(); - $current_zone = phutil_tag('strong', array(), $current_zone); + $current_zone_identifier = $viewer->getTimezoneIdentifier(); + $current_zone_formatted = phutil_tag( + 'strong', + array(), + $current_zone_identifier); $form = id(new AphrontFormView()) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Current Setting')) - ->setValue($current_zone)) + ->setValue($current_zone_formatted)) ->appendChild( id(new AphrontFormSelectControl()) ->setName('timezone') ->setLabel(pht('New Setting')) ->setOptions($options) - ->setValue($guess)); + ->setValue($guess)) + ->appendChild(id(new AphrontFormCheckboxControl()) + ->addCheckbox( + 'ignoreConflict', + 1, + pht('Ignore New Setting and Keep %s', $current_zone_identifier))); + return $this->newDialog() ->setTitle(pht('Adjust Timezone')) @@ -110,7 +120,7 @@ public function handleRequest(AphrontRequest $request) { $this->formatOffset($server_offset))) ->appendForm($form) ->addCancelButton(pht('Cancel')) - ->addSubmitButton(pht('Change Timezone')); + ->addSubmitButton(pht('Confirm')); } private function formatOffset($offset) { From ce5e0f3e333e87719261837ad994c00d43507349 Mon Sep 17 00:00:00 2001 From: Zero King Date: Wed, 8 Nov 2023 21:31:39 +0800 Subject: [PATCH 322/425] Fix doc link to Restarting Phorge Summary: See Q81. Link to /diviner/find/ could not find the documentation because it was renamed to "Restarting Phorge". Test Plan: 1. Uninstall optional PHP extension zip and restart Phorge 2. Vist /config/issue/ and click on a missing extension issue 3. Click on the documentation link to Restarting Reviewers: O1 Blessed Committers, valerio.bozzolan, speck Reviewed By: O1 Blessed Committers, valerio.bozzolan, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25459 --- src/applications/config/view/PhabricatorSetupIssueView.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/config/view/PhabricatorSetupIssueView.php b/src/applications/config/view/PhabricatorSetupIssueView.php index bfeae20684..e07e33b3d0 100644 --- a/src/applications/config/view/PhabricatorSetupIssueView.php +++ b/src/applications/config/view/PhabricatorSetupIssueView.php @@ -603,14 +603,14 @@ private function renderRelatedLinks(array $links) { } private function renderRestartLink() { - $doc_href = PhabricatorEnv::getDoclink('Restarting Phabricator'); + $doc_href = PhabricatorEnv::getDoclink('Restarting Phorge'); return phutil_tag( 'a', array( 'href' => $doc_href, 'target' => '_blank', ), - pht('Restarting Phabricator')); + pht('Restarting')); } } From dfa15726ea5afec47e5062a1feaf2ef0cd75c8ea Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 5 Nov 2023 15:36:10 +0100 Subject: [PATCH 323/425] Fix cursor paging issue in Given Token query call Summary: The "Query" class for Given Token is missing a "withIDs()" method. `Call to undefined method PhabricatorTokenGivenQuery::withIDs() at [PhabricatorCursorPagedPolicyAwareQuery.php:120]` Closes T15652 Test Plan: Go to the Token page and pass a URL parameter, such as `/token/given/?after=3` Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15652 Differential Revision: https://we.phorge.it/D25455 --- .../tokens/query/PhabricatorTokenGivenQuery.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php index 0e6ad9eb54..14e8016cf1 100644 --- a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php @@ -3,10 +3,16 @@ final class PhabricatorTokenGivenQuery extends PhabricatorCursorPagedPolicyAwareQuery { + private $ids; private $authorPHIDs; private $objectPHIDs; private $tokenPHIDs; + public function withIDs(array $ids) { + $this->ids = $ids; + return $this; + } + public function withTokenPHIDs(array $token_phids) { $this->tokenPHIDs = $token_phids; return $this; @@ -29,6 +35,13 @@ public function newResultObject() { protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); + if ($this->ids !== null) { + $where[] = qsprintf( + $conn, + 'id IN (%Ld)', + $this->ids); + } + if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, From 8507d3a95072db363add43d8a397dfd502c3db37 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 9 Nov 2023 21:04:06 +0100 Subject: [PATCH 324/425] Fix Exception in Chat room when you are not a Participant Summary: After this change you can lurk in public chats, and receive notifications, without crashing your planet. Fix exception "Undefined index: PHID-USER-..." shown to newcomers when they are receiving messages from a public Chat they are not members of. Closes T15497 Test Plan: - Have the Notification server (Aphlict) enabled and running. https://we.phorge.it/book/phorge/article/notifications/ - Be user Alice and visit a Conpherence Room. Alice must be not a participant. - Be user Bob and send a message in that Room. Now Alice does not see a crash anymore, but the message from Bob. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15497 Differential Revision: https://we.phorge.it/D25408 --- .../controller/ConpherenceUpdateController.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/applications/conpherence/controller/ConpherenceUpdateController.php b/src/applications/conpherence/controller/ConpherenceUpdateController.php index a792a5a4d4..6d838e72bc 100644 --- a/src/applications/conpherence/controller/ConpherenceUpdateController.php +++ b/src/applications/conpherence/controller/ConpherenceUpdateController.php @@ -328,7 +328,12 @@ private function loadAndRenderUpdates( ->executeOne(); $non_update = false; - $participant = $conpherence->getParticipant($user->getPHID()); + + // The User is always available. The Participant may not. See: + // User: it's you, lurking the Chat (maybe it's a public chat). + // Participant: it's you, if you are a Chat Member. + // https://we.phorge.it/T15497 + $participant = $conpherence->getParticipantIfExists($user->getPHID()); if ($need_transactions && $conpherence->getTransactions()) { $data = ConpherenceTransactionRenderer::renderTransactions( @@ -336,7 +341,7 @@ private function loadAndRenderUpdates( $conpherence); $key = PhabricatorConpherenceColumnMinimizeSetting::SETTINGKEY; $minimized = $user->getUserSetting($key); - if (!$minimized) { + if (!$minimized && $participant) { $participant->markUpToDate($conpherence); } } else if ($need_transactions) { From 8092d90c79fc79e8e17b0f50a449e7d2e204acde Mon Sep 17 00:00:00 2001 From: Waldir Pimenta Date: Thu, 9 Nov 2023 23:05:28 +0000 Subject: [PATCH 325/425] Adjust message in timezone conflict form Summary: This applies a suggestion initially proposed in https://we.phorge.it/D25420#12264 Test Plan: - Change your browser/system timezone to differ from your Phorge profile timezone - Click the notice that Phorge shows at the bottom left about the timezone mismatch - Confirm that the form text has been changed as per the diff in this revision Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: aklapper, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25458 --- .../controller/PhabricatorSettingsTimezoneController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php index 9cde82fdf0..0ed286da02 100644 --- a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php +++ b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php @@ -114,8 +114,8 @@ public function handleRequest(AphrontRequest $request) { ->appendParagraph( pht( 'Your browser timezone (%s) differs from your profile timezone '. - '(%s). You can ignore this conflict or adjust your profile setting '. - 'to match your client.', + '(%s). You can adjust your profile setting to match your browser, '. + 'or ignore this conflict to keep your current profile setting.', $this->formatOffset($client_offset), $this->formatOffset($server_offset))) ->appendForm($form) From 0729aa574bfd05bdd510ff48717a92a349f20640 Mon Sep 17 00:00:00 2001 From: Waldir Pimenta Date: Fri, 10 Nov 2023 09:13:09 +0000 Subject: [PATCH 326/425] Remove mention of Phabricator in the Auth setup check Summary: The authentication setup check, available at /config/issue/auth.config-unlocked/, contained a reference to Phabricator in the prompt of the command line hint to resolve the issue. Similar checks only showed the prompt symbol, not the directory, so this one was changed to match. Ref T15006 Test Plan: - Run `./bin/auth unlock` - Visit /config/issue/auth.config-unlocked/ - Notice that, with this patch, "phabricator" no longer appears in the prompt prefix for the suggested fix command at the end of the page. Reviewers: O1 Blessed Committers, valerio.bozzolan, speck Reviewed By: O1 Blessed Committers, valerio.bozzolan, speck Subscribers: avivey, tobiaswiese, Matthew, valerio.bozzolan, Cigaryno Maniphest Tasks: T15006 Differential Revision: https://we.phorge.it/D25425 --- src/applications/config/check/PhabricatorAuthSetupCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/config/check/PhabricatorAuthSetupCheck.php b/src/applications/config/check/PhabricatorAuthSetupCheck.php index e2b076944e..b971404c38 100644 --- a/src/applications/config/check/PhabricatorAuthSetupCheck.php +++ b/src/applications/config/check/PhabricatorAuthSetupCheck.php @@ -74,7 +74,7 @@ protected function executeChecks() { ->addRelatedPhabricatorConfig('auth.lock-config') ->addCommand( hsprintf( - 'phabricator/ $ ./bin/auth lock')); + '$ ./bin/auth lock')); } } } From 87e9c936ad1c98c232dd1632ca649f2d6a227c4d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 9 Nov 2023 20:23:03 +0100 Subject: [PATCH 327/425] Make "git cat-file" exception messages include repository monogram/slug Summary: When throwing an exception related to output provided by `git cat-file`, include the repository monogram to allow potentially debugging in Git. Closes T15661 Test Plan: Unclear. Basically: "have a broken Git repository in Diffusion". (However this patch changes a message only shown in case of an exception, so in the worst case we'd break the exception via an exception.) Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15661 Differential Revision: https://we.phorge.it/D25460 --- .../lowlevel/DiffusionLowLevelResolveRefsQuery.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php index 0bbba1dc18..899e69d49b 100644 --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php @@ -140,8 +140,9 @@ private function resolveGitRefs() { if (count($lines) !== count($unresolved)) { throw new Exception( pht( - 'Unexpected line count from `%s`!', - 'git cat-file')); + 'Unexpected line count from `%s` in %s!', + 'git cat-file', + $repository->getMonogram())); } $hits = array(); @@ -153,8 +154,9 @@ private function resolveGitRefs() { if (count($parts) < 2) { throw new Exception( pht( - 'Failed to parse `%s` output: %s', + 'Failed to parse `%s` output in %s: %s', 'git cat-file', + $repository->getMonogram(), $line)); } list($identifier, $type) = $parts; @@ -177,8 +179,9 @@ private function resolveGitRefs() { default: throw new Exception( pht( - 'Unexpected object type from `%s`: %s', + 'Unexpected object type from `%s` in %s: %s', 'git cat-file', + $repository->getMonogram(), $line)); } From c97a50472c5b0c9f441221c6cec9d9baa5f290b8 Mon Sep 17 00:00:00 2001 From: Zero King Date: Sat, 11 Nov 2023 10:20:35 +0800 Subject: [PATCH 328/425] Correct default database prefix in documentation Summary: Default value of `storage.default-namespace` is still `phabricator`. Correct documentation about this. Test Plan: Compare documentation with src/applications/config/option/PhabricatorMySQLConfigOptions.php. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25463 --- src/docs/contributor/database.diviner | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/contributor/database.diviner b/src/docs/contributor/database.diviner index dc553678a8..6572e42fc1 100644 --- a/src/docs/contributor/database.diviner +++ b/src/docs/contributor/database.diviner @@ -28,7 +28,7 @@ Databases ========= Each Phorge application has its own database. The names are prefixed by -`phorge_` (this is configurable). +`phabricator_` (this is configurable with `storage.default-namespace`). Phorge uses a separate database for each application. To understand why, see @{article:Why does Phorge need so many databases?}. From 90f651d669e447f6263574eea672827503f7b6d3 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 26 Oct 2023 21:26:11 +0200 Subject: [PATCH 329/425] Add Diffusion policy capability "Can Edit and View Identities" Summary: Make it possible not to allow anyone to edit Diffusion identities. Make it possible not to allow anyone to view other users' email addresses. Closes T15443 Test Plan: * As an admin, go to `/applications/view/PhabricatorDiffusionApplication/` and see new policy "Can Edit and View Identities" set to "All Users" (as implicitly before) * As an admin, go to `/applications/view/PhabricatorDiffusionApplication/` and change "Can Edit and View Identities" from "All Users" to "Administrators" * As a non-admin, go to `/diffusion/identity/` and try to select the disabled "Create Identity" button; get an error message clicking it due to lack of permissions * Given there is at least one identity defined, as a non-admin, go directly to `/diffusion/identity/view/1/` and get "You do not have permission to view this object." * Given there is at least one identity defined, as a non-admin, go directly to `/diffusion/identity/edit/1/` and get "You do not have permission to view this object." * As a non-admin, go directly to `/diffusion/identity/edit/form/default/` and get "You do not have permission to edit this object." * As a non-admin, go directly to `/diffusion/identity/` and get "No Identities found." instead of seeing the existing identities listed. * As an admin, go to `/diffusion/identity/` and still see the existing identities listed. * As an admin, go to `/diffusion/identity/`, select "Create Identity" to go to `/diffusion/identity/edit/` and see the "Create Identity" page (though broken; see T15453) * As an admin, go to `/diffusion/identity/view/1/` and still see the existing identity. * As an admin, go to `/diffusion/identity/edit/1/` and successfully edit the existing identity. Reviewers: O1 Blessed Committers, speck, valerio.bozzolan Reviewed By: O1 Blessed Committers, speck, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15443 Differential Revision: https://we.phorge.it/D25450 --- src/__phutil_library_map__.php | 2 ++ .../PhabricatorDiffusionApplication.php | 3 +++ ...catorRepositoryIdentityEditViewCapability.php | 16 ++++++++++++++++ .../PhabricatorRepositoryIdentityEditEngine.php | 3 ++- .../storage/PhabricatorRepositoryIdentity.php | 5 ++++- 5 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/applications/repository/capability/PhabricatorRepositoryIdentityEditViewCapability.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index d223f47515..ad0d8fc937 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4628,6 +4628,7 @@ 'PhabricatorRepositoryIdentityAssignTransaction' => 'applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php', 'PhabricatorRepositoryIdentityChangeWorker' => 'applications/repository/worker/PhabricatorRepositoryIdentityChangeWorker.php', 'PhabricatorRepositoryIdentityEditEngine' => 'applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php', + 'PhabricatorRepositoryIdentityEditViewCapability' => 'applications/repository/capability/PhabricatorRepositoryIdentityEditViewCapability.php', 'PhabricatorRepositoryIdentityFerretEngine' => 'applications/repository/search/PhabricatorRepositoryIdentityFerretEngine.php', 'PhabricatorRepositoryIdentityPHIDType' => 'applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php', 'PhabricatorRepositoryIdentityQuery' => 'applications/repository/query/PhabricatorRepositoryIdentityQuery.php', @@ -11325,6 +11326,7 @@ 'PhabricatorRepositoryIdentityAssignTransaction' => 'PhabricatorRepositoryIdentityTransactionType', 'PhabricatorRepositoryIdentityChangeWorker' => 'PhabricatorWorker', 'PhabricatorRepositoryIdentityEditEngine' => 'PhabricatorEditEngine', + 'PhabricatorRepositoryIdentityEditViewCapability' => 'PhabricatorPolicyCapability', 'PhabricatorRepositoryIdentityFerretEngine' => 'PhabricatorFerretEngine', 'PhabricatorRepositoryIdentityPHIDType' => 'PhabricatorPHIDType', 'PhabricatorRepositoryIdentityQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php index e0e74486da..ccd311fa7e 100644 --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -183,6 +183,9 @@ protected function getCustomCapabilities() { DiffusionCreateRepositoriesCapability::CAPABILITY => array( 'default' => PhabricatorPolicies::POLICY_ADMIN, ), + PhabricatorRepositoryIdentityEditViewCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_USER, + ), ); } diff --git a/src/applications/repository/capability/PhabricatorRepositoryIdentityEditViewCapability.php b/src/applications/repository/capability/PhabricatorRepositoryIdentityEditViewCapability.php new file mode 100644 index 0000000000..9bb9b62ebc --- /dev/null +++ b/src/applications/repository/capability/PhabricatorRepositoryIdentityEditViewCapability.php @@ -0,0 +1,16 @@ +getApplication()->getPolicy( + PhabricatorRepositoryIdentityEditViewCapability::CAPABILITY); } protected function buildCustomEditFields($object) { diff --git a/src/applications/repository/storage/PhabricatorRepositoryIdentity.php b/src/applications/repository/storage/PhabricatorRepositoryIdentity.php index 74fbd06544..df19f302f6 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryIdentity.php +++ b/src/applications/repository/storage/PhabricatorRepositoryIdentity.php @@ -142,7 +142,10 @@ public function getCapabilities() { } public function getPolicy($capability) { - return PhabricatorPolicies::getMostOpenPolicy(); + $app = PhabricatorApplication::getByClass( + 'PhabricatorDiffusionApplication'); + return $app->getPolicy( + PhabricatorRepositoryIdentityEditViewCapability::CAPABILITY); } public function hasAutomaticCapability( From 005fea5a14f44ef44c72658d8b7eee92d50647c7 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Thu, 25 May 2023 15:42:26 -0400 Subject: [PATCH 330/425] Fix view policy inheritance on image transforms Summary: Inherit viewPolicy from original image in image transforms and warn about Profile transform making transformed images public. Details: https://hackerone.com/reports/1984060 https://github.com/mozilla-conduit/phabricator/commit/8358b435a99435a95e0dffbbb92f71dc1961bc7b Closes T15663 Test Plan: 1. Click {nav View Transforms} on an image file with restrictive view policy. 2. See (Image will be Public) warning on Profile transform. 3. Click on Workcard transform. 4. Go back to {nav View Transforms} page and visit the Workcard transformed file. 5. Check if its view policy matches the original file. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15663 Differential Revision: https://we.phorge.it/D25462 Signed-off-by: Zero King --- .../files/transform/PhabricatorFileImageTransform.php | 6 +++++- .../files/transform/PhabricatorFileThumbnailTransform.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/applications/files/transform/PhabricatorFileImageTransform.php b/src/applications/files/transform/PhabricatorFileImageTransform.php index 468eae0e03..98ffcdd706 100644 --- a/src/applications/files/transform/PhabricatorFileImageTransform.php +++ b/src/applications/files/transform/PhabricatorFileImageTransform.php @@ -137,8 +137,12 @@ protected function applyImagemagick(array $argv) { protected function newFileFromData($data) { if ($this->file) { $name = $this->file->getName(); + $inherit_properties = array( + 'viewPolicy' => $this->file->getViewPolicy(), + ); } else { $name = 'default.png'; + $inherit_properties = array(); } $defaults = array( @@ -146,7 +150,7 @@ protected function newFileFromData($data) { 'name' => $this->getTransformKey().'-'.$name, ); - $properties = $this->getFileProperties() + $defaults; + $properties = $this->getFileProperties() + $inherit_properties + $defaults; return PhabricatorFile::newFromFileData($data, $properties); } diff --git a/src/applications/files/transform/PhabricatorFileThumbnailTransform.php b/src/applications/files/transform/PhabricatorFileThumbnailTransform.php index 2c61743078..6cf3914556 100644 --- a/src/applications/files/transform/PhabricatorFileThumbnailTransform.php +++ b/src/applications/files/transform/PhabricatorFileThumbnailTransform.php @@ -58,7 +58,7 @@ protected function getFileProperties() { public function generateTransforms() { return array( id(new PhabricatorFileThumbnailTransform()) - ->setName(pht("Profile (400px \xC3\x97 400px)")) + ->setName(pht("Profile (400px \xC3\x97 400px) (Image will be Public)")) ->setKey(self::TRANSFORM_PROFILE) ->setDimensions(400, 400) ->setScaleUp(true), From 5d80b3fd88d1b98e6780609207bb93cf39cbbb2e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 19 Aug 2023 10:06:34 +0200 Subject: [PATCH 331/425] Fix PHP 8.1 "strlen(null)" exception rendering PHUISegmentBar without a label Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=master, ref.master=df6c315ace5f), phorge(head=master, ref.master=3cc5ee6a33df) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/view/phui/PHUISegmentBarView.php:33] ``` Closes T15622 Test Plan: After applying this change, page with the PHUISegmentBar without label renders as expected without an exception. For example, visit this page: /uiexample/view/PhabricatorAphrontBarUIExample/ Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15622 Differential Revision: https://we.phorge.it/D25414 --- src/view/phui/PHUISegmentBarView.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/phui/PHUISegmentBarView.php b/src/view/phui/PHUISegmentBarView.php index 632c5327eb..8daa41c203 100644 --- a/src/view/phui/PHUISegmentBarView.php +++ b/src/view/phui/PHUISegmentBarView.php @@ -30,7 +30,7 @@ protected function getTagContent() { require_celerity_resource('phui-segment-bar-view-css'); $label = $this->label; - if (strlen($label)) { + if ($label !== null) { $label = phutil_tag( 'div', array( From 16d9cc12af45aeff9973c3c99495f7a45cdaf0c3 Mon Sep 17 00:00:00 2001 From: Zero King Date: Sat, 11 Nov 2023 15:45:21 +0800 Subject: [PATCH 332/425] Enforce viewable MIME types config on PDF documents Summary: Let instance admins decide whether to allow PDFs to be viewable as a Web page. See . MOZILLA: Instead of always allowing PDFs to be viewable in the web UI, [...] This checks that the PDF mimetype is viewable according to the system configuration. Ref Q83. Test Plan: 1. Set `files.viewable-mime-types` to exclude application/pdf. 2. Upload a pdf file. 3. See "No document engine can render the contents of this file." in web UI. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25464 --- .../config/PhabricatorFilesConfigOptions.php | 8 +++++--- .../document/PhabricatorPDFDocumentEngine.php | 16 +++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/applications/files/config/PhabricatorFilesConfigOptions.php b/src/applications/files/config/PhabricatorFilesConfigOptions.php index 7ed96a412b..67d123e2cb 100644 --- a/src/applications/files/config/PhabricatorFilesConfigOptions.php +++ b/src/applications/files/config/PhabricatorFilesConfigOptions.php @@ -134,9 +134,11 @@ public function getOptions() { ->setDescription( pht( "Configure which uploaded file types may be viewed directly ". - "in the browser. Other file types will be downloaded instead ". - "of displayed. This is mainly a usability consideration, since ". - "browsers tend to freak out when viewing very large binary files.". + "in the browser. Other types will be downloaded instead of ". + "displayed. This is a usability and security consideration, ". + "since browsers tend to freak out when viewing very large ". + "binary files, and some types may be vulnerable to XSS attacks ". + "when viewed in a browser.". "\n\n". "The keys in this map are viewable MIME types; the values are ". "the MIME types they are delivered as when they are viewed in ". diff --git a/src/applications/files/document/PhabricatorPDFDocumentEngine.php b/src/applications/files/document/PhabricatorPDFDocumentEngine.php index 1e85bd4ae5..bc6433290a 100644 --- a/src/applications/files/document/PhabricatorPDFDocumentEngine.php +++ b/src/applications/files/document/PhabricatorPDFDocumentEngine.php @@ -14,14 +14,16 @@ protected function getDocumentIconIcon(PhabricatorDocumentRef $ref) { } protected function canRenderDocumentType(PhabricatorDocumentRef $ref) { - // Since we just render a link to the document anyway, we don't need to - // check anything fancy in config to see if the MIME type is actually - // viewable. + $viewable_types = PhabricatorEnv::getEnvConfig('files.viewable-mime-types'); + $viewable_types = array_keys($viewable_types); - return $ref->hasAnyMimeType( - array( - 'application/pdf', - )); + $pdf_types = array( + 'application/pdf', + ); + + return + $ref->hasAnyMimeType($viewable_types) && + $ref->hasAnyMimeType($pdf_types); } protected function newDocumentContent(PhabricatorDocumentRef $ref) { From 296ce3956b81718b1476b0185bc444d9bb82bee5 Mon Sep 17 00:00:00 2001 From: Waldir Pimenta Date: Sun, 12 Nov 2023 21:06:55 +0000 Subject: [PATCH 333/425] Fix loop in contribution docs Summary: The Contributor Introduction lists in the next steps the Contributing Code page; in its turn, the Contributing Code page links back to the Contributor Introduction page. This change fixes that infinite loop by adding an actual path forward, adding a link to the Developer Setup page in the Next Steps section of the Contributing Code page. Test Plan: Open the /book/contrib/article/contributing_code/ URL and confirm that the Developer Setup is included in the Next Steps section. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25465 --- src/docs/contributor/contributing_code.diviner | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/docs/contributor/contributing_code.diviner b/src/docs/contributor/contributing_code.diviner index c7bdec25c9..adc56aca1a 100644 --- a/src/docs/contributor/contributing_code.diviner +++ b/src/docs/contributor/contributing_code.diviner @@ -194,4 +194,6 @@ Next Steps Continue by: - - returning to the @{article:Contributor Introduction}. + - preparing your development environment as described in the + @{article:Developer Setup} + - returning to the @{article:Contributor Introduction} From 4535e8753c8be6e499ad2be9e9de9208fae048b0 Mon Sep 17 00:00:00 2001 From: Zero King Date: Mon, 13 Nov 2023 01:34:00 +0800 Subject: [PATCH 334/425] Fix typo in DiffusionLowLevelGitRefQuery Summary: $refs_types is undefined. Test Plan: None. PhabricatorRepositoryRefCursor::TYPE_REF is not used with DiffusionLowLevelGitRefQuery. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25468 --- .../diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php index d11b658131..21c86e7435 100644 --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php @@ -27,7 +27,7 @@ protected function executeQuery() { $with_branches = isset($ref_types[$type_branch]); $with_tags = isset($ref_types[$type_tag]); - $with_refs = isset($refs_types[$type_ref]); + $with_refs = isset($ref_types[$type_ref]); $repository = $this->getRepository(); From 486fc95b29fd1acbe890f5d04d6daa971033ad4e Mon Sep 17 00:00:00 2001 From: Zero King Date: Mon, 13 Nov 2023 01:55:01 +0800 Subject: [PATCH 335/425] Fix reentrancy guard in setDebugTimeLimit() Summary: $initialized is never initialized and onDebugTick() may be registered multiple times. Test Plan: None. The function is normally only called once. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25469 --- support/startup/PhabricatorStartup.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/support/startup/PhabricatorStartup.php b/support/startup/PhabricatorStartup.php index b9d659d39b..b4a4314ac0 100644 --- a/support/startup/PhabricatorStartup.php +++ b/support/startup/PhabricatorStartup.php @@ -261,10 +261,11 @@ public static function endOutputCapture() { public static function setDebugTimeLimit($limit) { self::$debugTimeLimit = $limit; - static $initialized; + static $initialized = false; if (!$initialized) { declare(ticks=1); register_tick_function(array(__CLASS__, 'onDebugTick')); + $initialized = true; } } From 361fcd0cac368bf4d278907bfd00df46ef28eb29 Mon Sep 17 00:00:00 2001 From: Zero King Date: Mon, 13 Nov 2023 02:15:11 +0800 Subject: [PATCH 336/425] Initialize static variable to make linter happy Summary: Uninitialized variable defaults to 0 in this context. Make it explicit. Test Plan: None. No functional change. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25470 --- src/applications/policy/filter/PhabricatorPolicyFilter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/policy/filter/PhabricatorPolicyFilter.php b/src/applications/policy/filter/PhabricatorPolicyFilter.php index d8f239e51a..3443d17156 100644 --- a/src/applications/policy/filter/PhabricatorPolicyFilter.php +++ b/src/applications/policy/filter/PhabricatorPolicyFilter.php @@ -461,7 +461,7 @@ private function executeExtendedPolicyChecks( // checks make it difficult to create cycles normally, so just do a // simple check here to limit damage. - static $depth; + static $depth = 0; $depth++; From aa8af1d79e8bfeb09e72d5e3b9330780e78b7aeb Mon Sep 17 00:00:00 2001 From: Zero King Date: Sun, 12 Nov 2023 16:21:57 +0800 Subject: [PATCH 337/425] Align logo image and text in site header Summary: Ref T15666. Center wordmark vertically to align with logo in site header. Test Plan: - Check alignment visually. - With browser DevTools, verify that wordmark is 44px tall. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15666 Differential Revision: https://we.phorge.it/D25467 --- resources/celerity/map.php | 12 ++++++------ webroot/rsrc/css/application/base/main-menu-view.css | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index e40b474c14..fca4e57058 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '3c4918b0', + 'core.pkg.css' => 'c0bdb5b4', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '2431def2', @@ -38,7 +38,7 @@ 'rsrc/css/aphront/typeahead.css' => '8779483d', 'rsrc/css/application/almanac/almanac.css' => '2e050f4f', 'rsrc/css/application/auth/auth.css' => 'c2f23d74', - 'rsrc/css/application/base/main-menu-view.css' => '89fc16b6', + 'rsrc/css/application/base/main-menu-view.css' => '5d673247', 'rsrc/css/application/base/notification-menu.css' => '4df1ee30', 'rsrc/css/application/base/phui-theme.css' => '35883b37', 'rsrc/css/application/base/standard-page-view.css' => 'e08c7462', @@ -792,7 +792,7 @@ 'phabricator-flag-css' => '2b77be8d', 'phabricator-keyboard-shortcut' => '1a844c06', 'phabricator-keyboard-shortcut-manager' => '81debc48', - 'phabricator-main-menu-view' => '89fc16b6', + 'phabricator-main-menu-view' => '5d673247', 'phabricator-nav-view-css' => '423f92cc', 'phabricator-notification' => 'a9b91e3f', 'phabricator-notification-css' => '30240bd2', @@ -1501,6 +1501,9 @@ 'javelin-dom', 'phuix-dropdown-menu', ), + '5d673247' => array( + 'phui-theme-css', + ), '5d83623b' => array( 'javelin-dom', ), @@ -1689,9 +1692,6 @@ 'javelin-stratcom', 'javelin-install', ), - '89fc16b6' => array( - 'phui-theme-css', - ), '8ac32fd9' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index 8c5000247a..6aace18b44 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -70,7 +70,8 @@ float: left; color: #fff; font-size: 18px; - margin: 9px 4px 9px 6px; + line-height: 22px; + margin: 11px 4px 11px 6px; padding-right: 8px; max-width: 175px; overflow: hidden; From 282e37aaf68257251712a1bf7533b9cc85d16262 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 13 Nov 2023 14:03:55 +0100 Subject: [PATCH 338/425] Do not expose Contact Numbers settings panel when no SMS support configured Summary: It's useless without SMS support and only exposed to the user themselves. Closes T15486 Test Plan: Before and after applying this patch, * Try to access the list of your contact numbers at `/settings/panel/contact/` * Try to access an existing, previously created contact number at `/auth/contact/1/` * Try to add a contact number at `/auth/contact/edit/` * Go to e.g. `/settings/panel/datetime` and check the "Authentication" section in the left sidebar for {nav icon=hashtag, name=Contact Numbers} Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15486 Differential Revision: https://we.phorge.it/D25452 --- ...atorAuthContactNumberDisableController.php | 23 +++++++++++-------- ...ricatorAuthContactNumberEditController.php | 11 ++++++--- ...atorAuthContactNumberPrimaryController.php | 23 +++++++++++-------- ...ricatorAuthContactNumberTestController.php | 23 +++++++++++-------- ...ricatorAuthContactNumberViewController.php | 13 +++++++---- .../auth/factor/PhabricatorSMSAuthFactor.php | 2 +- ...PhabricatorContactNumbersSettingsPanel.php | 12 ++++++++++ 7 files changed, 68 insertions(+), 39 deletions(-) diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php index a525e7b930..28b7e69593 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php @@ -7,16 +7,19 @@ public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); - $number = id(new PhabricatorAuthContactNumberQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$number) { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + $number = id(new PhabricatorAuthContactNumberQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + } + if (!isset($number) || !$number) { return new Aphront404Response(); } diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php index 95764496da..c012ca3581 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php @@ -4,9 +4,14 @@ final class PhabricatorAuthContactNumberEditController extends PhabricatorAuthContactNumberController { public function handleRequest(AphrontRequest $request) { - return id(new PhabricatorAuthContactNumberEditEngine()) - ->setController($this) - ->buildResponse(); + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + return id(new PhabricatorAuthContactNumberEditEngine()) + ->setController($this) + ->buildResponse(); + } else { + return new Aphront404Response(); + } } } diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberPrimaryController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberPrimaryController.php index cad1bbf3fc..63ba5ad883 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberPrimaryController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberPrimaryController.php @@ -7,16 +7,19 @@ public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); - $number = id(new PhabricatorAuthContactNumberQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$number) { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + $number = id(new PhabricatorAuthContactNumberQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + } + if (!isset($number) || !$number) { return new Aphront404Response(); } diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php index f8c8b013bf..a991c34a6d 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php @@ -7,16 +7,19 @@ public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); - $number = id(new PhabricatorAuthContactNumberQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$number) { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + $number = id(new PhabricatorAuthContactNumberQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + } + if (!isset($number) || !$number) { return new Aphront404Response(); } diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php index 027d288dbc..75e4b8e3b7 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php @@ -6,11 +6,14 @@ final class PhabricatorAuthContactNumberViewController public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); - $number = id(new PhabricatorAuthContactNumberQuery()) - ->setViewer($viewer) - ->withIDs(array($request->getURIData('id'))) - ->executeOne(); - if (!$number) { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + $number = id(new PhabricatorAuthContactNumberQuery()) + ->setViewer($viewer) + ->withIDs(array($request->getURIData('id'))) + ->executeOne(); + } + if (!isset($number) || !$number) { return new Aphront404Response(); } diff --git a/src/applications/auth/factor/PhabricatorSMSAuthFactor.php b/src/applications/auth/factor/PhabricatorSMSAuthFactor.php index 33f640e692..2350855859 100644 --- a/src/applications/auth/factor/PhabricatorSMSAuthFactor.php +++ b/src/applications/auth/factor/PhabricatorSMSAuthFactor.php @@ -334,7 +334,7 @@ private function newSMSChallengeCode() { return $value; } - private function isSMSMailerConfigured() { + public function isSMSMailerConfigured() { $mailers = PhabricatorMetaMTAMail::newMailers( array( 'outbound' => true, diff --git a/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php index 7056fd02de..60a2064546 100644 --- a/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php @@ -19,6 +19,18 @@ public function getPanelGroupKey() { return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY; } + /** + * Whether to display "Contact Numbers" panel in users' Personal + * Settings by checking if global SMS support is configured + */ + public function isUserPanel() { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + return true; + } + return false; + } + public function isMultiFactorEnrollmentPanel() { return true; } From 775d141fe6bf4ce1f7e596dd856962f054f16d1a Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 15 Nov 2023 13:56:04 +0100 Subject: [PATCH 339/425] Audit Feed: less verbose when the author is the committer Summary: If the author and the committer are the same person, do not show them twice. From: UsernameFoo committed : (authored by UsernameFoo). To: UsernameFoo committed : This only affects the feed. | Before | After | |-----------|-----------| | {F342758} | {F342764} | Closes T15528 Test Plan: Do some mixed commits and visit /feed/query/all/. No nuclear implosions. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15528 Differential Revision: https://we.phorge.it/D25421 --- .../audit/storage/PhabricatorAuditTransaction.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/applications/audit/storage/PhabricatorAuditTransaction.php b/src/applications/audit/storage/PhabricatorAuditTransaction.php index e6c1062092..8e49d99520 100644 --- a/src/applications/audit/storage/PhabricatorAuditTransaction.php +++ b/src/applications/audit/storage/PhabricatorAuditTransaction.php @@ -338,7 +338,17 @@ public function getTitleForFeed() { $author = null; } - if ($author) { + // Show both Author and Committer only if they are different. + $show_both = $author && $committer; + if ($show_both) { + if ($new['authorPHID']) { + $show_both = $new['authorPHID'] !== $new['committerPHID']; + } else if (phutil_nonempty_string($new['authorName'])) { + $show_both = $new['authorName'] !== $new['committerName']; + } + } + + if ($show_both) { $title = pht( '%s committed %s (authored by %s).', $committer, From f42dd5819e98c4ff1adc73c22b5f79b42c475538 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 16 Nov 2023 10:41:43 +0100 Subject: [PATCH 340/425] Fix possible array to string conversion renaming Pholio Mockup image Summary: Premising that the `$old` and `$new` variables are 1-element arrays defined as PHID=>title, this can cause `renderValue()` repeatedly fail when passing an array instead of its value. Thus pass `head($old)` instead, to get the first value - that is the only one, even if you rename multiple images (since this Transaction is about a single Mockup image). Closes T15646 Test Plan: * Have `phd` running * Create a Pholio mockup with at least one image Edit the Pholio mockup and: 1. rename the Titles of an image 2. rename a single Image 3. rename no image No nuclear implosions. You still see a lovely Feed mentioning each rename. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15646 Differential Revision: https://we.phorge.it/D25441 --- .../pholio/xaction/PholioImageNameTransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/pholio/xaction/PholioImageNameTransaction.php b/src/applications/pholio/xaction/PholioImageNameTransaction.php index 33b01903ee..f4705a98fc 100644 --- a/src/applications/pholio/xaction/PholioImageNameTransaction.php +++ b/src/applications/pholio/xaction/PholioImageNameTransaction.php @@ -31,8 +31,8 @@ public function getTitle() { '%s renamed an image (%s) from %s to %s.', $this->renderAuthor(), $this->renderHandle(key($new)), - $this->renderValue($old), - $this->renderValue($new)); + $this->renderValue((string)head($old)), + $this->renderValue((string)head($new))); } public function getTitleForFeed() { From 76ed0c7ff7be9d87dd32f14bf9ddb7379261a410 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 10 Nov 2023 12:56:43 +0100 Subject: [PATCH 341/425] Disallow webcrawlers to follow Paste line number anchor links Summary: Paste provides line anchor links in every single line of a paste. If webcrawlers follow these links, they index the very same Paste again. Thus disallow in robots.txt to reduce unneeded traffic and indexing time. Closes T15662 Test Plan: Go to `/robots.txt` in the web browser. Cross fingers that more webcrawlers abide by RFC 9309. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15662 Differential Revision: https://we.phorge.it/D25461 --- .../robots/PhabricatorRobotsPlatformController.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php b/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php index b4a3c4fa37..82028918d7 100644 --- a/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php +++ b/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php @@ -19,6 +19,13 @@ protected function newRobotsRules() { $out[] = 'Disallow: /diffusion/'; $out[] = 'Disallow: /source/'; + // See T15662. Prevent indexing line anchor links in Pastes. Per RFC 9309 + // section 2.2.3, percentage-encode "$" to avoid interpretation as end of + // match pattern. However, crawlers may not abide by it but follow the + // original standard at https://www.robotstxt.org/orig.html with no mention + // how to interpret characters like "$" and thus entirely ignore this rule. + $out[] = 'Disallow: /P*%24*'; + // Add a small crawl delay (number of seconds between requests) for spiders // which respect it. The intent here is to prevent spiders from affecting // performance for users. The possible cost is slower indexing, but that From 05f4d5071fdca02123bd1ff4c0935b847c7f9963 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 17 Nov 2023 08:44:19 +0100 Subject: [PATCH 342/425] Disallow webcrawlers to index Diffusion commits Summary: Phorge already sets `Disallow: /diffusion/` and `Disallow: /source/`. Thus consequently also disallow accessing specific commits via `/r*`. See https://secure.phabricator.com/T4610 for previous discussions. Closes T15670 Test Plan: Go to `/robots.txt` in the web browser. Cross fingers that more webcrawlers abide by RFC 9309. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15670 Differential Revision: https://we.phorge.it/D25474 --- .../controller/robots/PhabricatorRobotsPlatformController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php b/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php index 82028918d7..7151f2e4aa 100644 --- a/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php +++ b/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php @@ -18,6 +18,8 @@ protected function newRobotsRules() { $out[] = 'User-Agent: *'; $out[] = 'Disallow: /diffusion/'; $out[] = 'Disallow: /source/'; + // See T15670. Also prevent directly accessing commits in Diffusion. + $out[] = 'Disallow: /r*'; // See T15662. Prevent indexing line anchor links in Pastes. Per RFC 9309 // section 2.2.3, percentage-encode "$" to avoid interpretation as end of From a741f5d65c103d114b82cf0f70563334d1c3eb4c Mon Sep 17 00:00:00 2001 From: Waldir Pimenta Date: Sat, 18 Nov 2023 22:13:49 +0000 Subject: [PATCH 343/425] Change some instances of "phabricator" to "phorge" Summary: Just a small set of replacements in locations that seem innocuous (user-facing messages, documentation, etc.) Ref T15006 Test Plan: Nothing should change in terms of behavior. The places where these changes were made should now say "phorge". Example tests: - Manage a single User and click on Delete User and see the popup - Run a test email and check the output ./bin/mail send-test --to username - Visit /maniphest/, shift+click on at least 1 Task, click on Bulk Edit Selected, Continue, see the popup - See the mentioned documentation with your big eyes. Eyes do not explode \o/ Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15006 Differential Revision: https://we.phorge.it/D25473 --- conf/aphlict/README | 2 +- scripts/init/lib.php | 2 +- .../config/option/PhabricatorConfigOption.php | 2 +- .../PhabricatorMailManagementReceiveTestWorkflow.php | 3 ++- .../PhabricatorMailManagementSendTestWorkflow.php | 3 ++- .../controller/PhabricatorPeopleDeleteController.php | 9 ++++++--- .../bulk/PhabricatorEditEngineBulkJobType.php | 4 ++-- .../events/PhabricatorAutoEventListener.php | 2 +- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/conf/aphlict/README b/conf/aphlict/README index 2786ea5658..0704bf6068 100644 --- a/conf/aphlict/README +++ b/conf/aphlict/README @@ -8,7 +8,7 @@ be read by default. To specify a path when starting Aphlict, use the `--config` flag: - phabricator/ $ ./bin/aphlict start --config path/to/config.json + phorge/ $ ./bin/aphlict start --config path/to/config.json Specifying a configuration file explicitly overrides default configuration. diff --git a/scripts/init/lib.php b/scripts/init/lib.php index 4c544da9d0..a12c394f3b 100644 --- a/scripts/init/lib.php +++ b/scripts/init/lib.php @@ -13,7 +13,7 @@ function init_phabricator_script(array $options) { if (!$ok) { echo 'FATAL ERROR: Unable to load the "Arcanist" library. '. - 'Put "arcanist/" next to "phabricator/" on disk.'; + 'Put "arcanist/" next to "phorge/" on disk.'; echo "\n"; exit(1); diff --git a/src/applications/config/option/PhabricatorConfigOption.php b/src/applications/config/option/PhabricatorConfigOption.php index 6b38bbb6d6..ee79005d5d 100644 --- a/src/applications/config/option/PhabricatorConfigOption.php +++ b/src/applications/config/option/PhabricatorConfigOption.php @@ -77,7 +77,7 @@ public function getLockedMessage() { 'This configuration is locked and can not be edited from the web '. 'interface. Use %s in %s to edit it.', phutil_tag('tt', array(), './bin/config'), - phutil_tag('tt', array(), 'phabricator/')); + phutil_tag('tt', array(), PlatformSymbols::getPlatformServerPath())); } public function addExample($value, $description) { diff --git a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php index 73917a2d84..a3c556e6ac 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php @@ -181,8 +181,9 @@ public function execute(PhutilArgumentParser $args) { $received->processReceivedMail(); $console->writeErr( - "%s\n\n phabricator/ $ ./bin/mail show-inbound --id %d\n\n", + "%s\n\n %s $ ./bin/mail show-inbound --id %d\n\n", pht('Mail received! You can view details by running this command:'), + PlatformSymbols::getPlatformServerPath(), $received->getID()); } diff --git a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php index 6ea2c9de5a..54c7ff53f6 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php @@ -225,8 +225,9 @@ public function execute(PhutilArgumentParser $args) { $mail->save(); $console->writeErr( - "%s\n\n phabricator/ $ ./bin/mail show-outbound --id %d\n\n", + "%s\n\n %s $ ./bin/mail show-outbound --id %d\n\n", pht('Mail sent! You can view details by running this command:'), + PlatformSymbols::getPlatformServerPath(), $mail->getID()); } diff --git a/src/applications/people/controller/PhabricatorPeopleDeleteController.php b/src/applications/people/controller/PhabricatorPeopleDeleteController.php index 8e6ac91da7..2f45b40d31 100644 --- a/src/applications/people/controller/PhabricatorPeopleDeleteController.php +++ b/src/applications/people/controller/PhabricatorPeopleDeleteController.php @@ -27,9 +27,12 @@ public function handleRequest(AphrontRequest $request) { 'To permanently destroy this user, run this command from the '. 'command line:')) ->appendCommand( - csprintf( - 'phabricator/ $ ./bin/remove destroy %R', - $user->getMonogram())) + hsprintf( + '%s $ %s', + PlatformSymbols::getPlatformServerPath(), + csprintf( + './bin/remove destroy %R', + $user->getMonogram()))) ->appendParagraph( pht( 'Unless you have a very good reason to delete this user, consider '. diff --git a/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php b/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php index 758e7f3439..811b4e1565 100644 --- a/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php +++ b/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php @@ -31,9 +31,9 @@ public function getDescriptionForConfirm(PhabricatorWorkerBulkJob $job) { $parts[] = pht('To silence this edit, run this command:'); $command = csprintf( - 'phabricator/ $ ./bin/bulk make-silent --id %R', + '%s $ ./bin/bulk make-silent --id %R', + PlatformSymbols::getPlatformServerPath(), $job->getID()); - $command = (string)$command; $parts[] = phutil_tag('tt', array(), $command); diff --git a/src/infrastructure/events/PhabricatorAutoEventListener.php b/src/infrastructure/events/PhabricatorAutoEventListener.php index 0ed76b3390..a8419f6865 100644 --- a/src/infrastructure/events/PhabricatorAutoEventListener.php +++ b/src/infrastructure/events/PhabricatorAutoEventListener.php @@ -10,6 +10,6 @@ * * All concrete subclasses of this class are automatically registered at * startup. This allows it to be used with custom one-offs that can be dropped - * into `phabricator/src/extensions/`. + * into `phorge/src/extensions/`. */ abstract class PhabricatorAutoEventListener extends PhabricatorEventListener {} From 1b49165ddd16e7a5c6542748b06ea89729fb07a1 Mon Sep 17 00:00:00 2001 From: Matthew Bowker Date: Tue, 21 Nov 2023 11:17:19 -0700 Subject: [PATCH 344/425] Show more in Application Detail and List view Summary: Update the Application Detail view and List View to show a unified set of Badges (Deprecated, etc.), show PHIDs and Monograms on the Application Detail view, allow Applications to register Monograms. Example of the page /applications/view/PhabricatorDiffusionApplication/: {F393393} T15568 Test Plan: 1. Visit /applications/ and see Deprecated badges etc. 2. Visit various Configure buttons from that list and see Monograms, Badges, PHIDs etc. 3. Enjoy screenshots in the comments of this Diff Reviewers: O1 Blessed Committers, valerio.bozzolan, avivey, speck Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey, speck Subscribers: avivey, speck, tobiaswiese, valerio.bozzolan, Cigaryno Differential Revision: https://we.phorge.it/D25362 --- .../base/PhabricatorApplication.php | 15 +++++++ .../PhabricatorCalendarApplication.php | 4 ++ .../PhabricatorChatLogApplication.php | 8 +++- .../PhabricatorConpherenceApplication.php | 4 ++ .../PhabricatorCountdownApplication.php | 4 ++ .../PhabricatorDashboardApplication.php | 4 ++ .../PhabricatorDifferentialApplication.php | 4 ++ .../PhabricatorDiffusionApplication.php | 5 +++ .../PhabricatorFilesApplication.php | 4 ++ .../PhabricatorFundApplication.php | 4 ++ .../PhabricatorHarbormasterApplication.php | 4 ++ .../PhabricatorHeraldApplication.php | 4 ++ .../PhabricatorLegalpadApplication.php | 4 ++ .../PhabricatorManiphestApplication.php | 4 ++ ...ricatorApplicationDetailViewController.php | 45 +++++++++++++++++++ .../meta/query/PhabricatorAppSearchEngine.php | 18 ++++++-- .../PhabricatorOwnersApplication.php | 4 ++ .../PhabricatorPassphraseApplication.php | 4 ++ .../PhabricatorPasteApplication.php | 4 ++ .../PhabricatorPhameApplication.php | 4 ++ .../phid/type/PhabricatorPHIDType.php | 24 ++++++++++ .../PhabricatorPholioApplication.php | 4 ++ .../PhabricatorPhurlApplication.php | 4 ++ .../PhabricatorPonderApplication.php | 4 ++ .../PhabricatorSlowvoteApplication.php | 4 ++ .../PhabricatorSpacesApplication.php | 4 ++ 26 files changed, 189 insertions(+), 6 deletions(-) diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index 9e1ea7d5ba..67294db263 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -61,6 +61,21 @@ public function getShortDescription() { return pht('%s Application', $this->getName()); } + /** + * Extensions are allowed to register multi-character monograms. + * The name "Monogram" is actually a bit of a misnomer, + * but we're keeping it due to the history. + * + * @return array + */ + public function getMonograms() { + return array(); + } + + public function isDeprecated() { + return false; + } + final public function isInstalled() { if (!$this->canUninstall()) { return true; diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php index fa534280a0..c9ff3ac9d3 100644 --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -28,6 +28,10 @@ public function getTitleGlyph() { return "\xE2\x8C\xA8"; } + public function getMonograms() { + return array('E'); + } + public function getApplicationGroup() { return self::GROUP_UTILITIES; } diff --git a/src/applications/chatlog/application/PhabricatorChatLogApplication.php b/src/applications/chatlog/application/PhabricatorChatLogApplication.php index 912ddd9b6b..ab5eb1960f 100644 --- a/src/applications/chatlog/application/PhabricatorChatLogApplication.php +++ b/src/applications/chatlog/application/PhabricatorChatLogApplication.php @@ -11,7 +11,7 @@ public function getName() { } public function getShortDescription() { - return pht('(Deprecated)'); + return pht('IRC Logs'); } public function getIcon() { @@ -22,6 +22,10 @@ public function isPrototype() { return true; } + public function isDeprecated() { + return true; + } + public function getTitleGlyph() { return "\xE0\xBC\x84"; } @@ -30,7 +34,7 @@ public function getApplicationGroup() { return self::GROUP_UTILITIES; } - public function getRoutes() { + public function getRoutes() { return array( '/chatlog/' => array( '' => 'PhabricatorChatLogChannelListController', diff --git a/src/applications/conpherence/application/PhabricatorConpherenceApplication.php b/src/applications/conpherence/application/PhabricatorConpherenceApplication.php index a0c21bd33a..f3118be9bf 100644 --- a/src/applications/conpherence/application/PhabricatorConpherenceApplication.php +++ b/src/applications/conpherence/application/PhabricatorConpherenceApplication.php @@ -28,6 +28,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('Z'); + } + public function getRoutes() { return array( '/Z(?P[1-9]\d*)' diff --git a/src/applications/countdown/application/PhabricatorCountdownApplication.php b/src/applications/countdown/application/PhabricatorCountdownApplication.php index e205ba64c3..5f06e6ef9f 100644 --- a/src/applications/countdown/application/PhabricatorCountdownApplication.php +++ b/src/applications/countdown/application/PhabricatorCountdownApplication.php @@ -36,6 +36,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('C'); + } + public function getRoutes() { return array( '/C(?P[1-9]\d*)' => 'PhabricatorCountdownViewController', diff --git a/src/applications/dashboard/application/PhabricatorDashboardApplication.php b/src/applications/dashboard/application/PhabricatorDashboardApplication.php index 3cdb932514..aeb2f43195 100644 --- a/src/applications/dashboard/application/PhabricatorDashboardApplication.php +++ b/src/applications/dashboard/application/PhabricatorDashboardApplication.php @@ -30,6 +30,10 @@ public function getApplicationOrder() { return 0.160; } + public function getMonograms() { + return array('W'); + } + public function getRoutes() { $menu_rules = $this->getProfileMenuRouting( 'PhabricatorDashboardPortalViewController'); diff --git a/src/applications/differential/application/PhabricatorDifferentialApplication.php b/src/applications/differential/application/PhabricatorDifferentialApplication.php index 5980f738b0..16b3193dac 100644 --- a/src/applications/differential/application/PhabricatorDifferentialApplication.php +++ b/src/applications/differential/application/PhabricatorDifferentialApplication.php @@ -42,6 +42,10 @@ public function getOverview() { 'engineers to review, discuss and approve changes to software.'); } + public function getMonograms() { + return array('D'); + } + public function getRoutes() { return array( '/D(?P[1-9]\d*)' => array( diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php index ccd311fa7e..a2b9f81aee 100644 --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -44,6 +44,11 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + // This is a special case, as r and R mean different things. + return array('r', 'R'); + } + public function getRoutes() { $repository_routes = array( '/' => array( diff --git a/src/applications/files/application/PhabricatorFilesApplication.php b/src/applications/files/application/PhabricatorFilesApplication.php index e246d4c90c..4014a6ce62 100644 --- a/src/applications/files/application/PhabricatorFilesApplication.php +++ b/src/applications/files/application/PhabricatorFilesApplication.php @@ -67,6 +67,10 @@ protected function getCustomCapabilities() { ); } + public function getMonograms() { + return array('F'); + } + public function getRoutes() { return array( '/F(?P[1-9]\d*)(?:\$(?P\d+(?:-\d+)?))?' diff --git a/src/applications/fund/application/PhabricatorFundApplication.php b/src/applications/fund/application/PhabricatorFundApplication.php index 58ce4f8922..5e2d33c1a4 100644 --- a/src/applications/fund/application/PhabricatorFundApplication.php +++ b/src/applications/fund/application/PhabricatorFundApplication.php @@ -36,6 +36,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('I'); + } + public function getRoutes() { return array( '/I(?P[1-9]\d*)' => 'FundInitiativeViewController', diff --git a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php index 6c499bf63a..7de2a658c2 100644 --- a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php +++ b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php @@ -51,6 +51,10 @@ public function getHelpDocumentationArticles(PhabricatorUser $viewer) { ); } + public function getMonograms() { + return array('B'); + } + public function getRoutes() { return array( '/B(?P[1-9]\d*)' => 'HarbormasterBuildableViewController', diff --git a/src/applications/herald/application/PhabricatorHeraldApplication.php b/src/applications/herald/application/PhabricatorHeraldApplication.php index 0de1c02737..7808820818 100644 --- a/src/applications/herald/application/PhabricatorHeraldApplication.php +++ b/src/applications/herald/application/PhabricatorHeraldApplication.php @@ -49,6 +49,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('H'); + } + public function getRoutes() { return array( '/H(?P[1-9]\d*)' => 'HeraldRuleViewController', diff --git a/src/applications/legalpad/application/PhabricatorLegalpadApplication.php b/src/applications/legalpad/application/PhabricatorLegalpadApplication.php index a3eaff5792..47a8f4ebef 100644 --- a/src/applications/legalpad/application/PhabricatorLegalpadApplication.php +++ b/src/applications/legalpad/application/PhabricatorLegalpadApplication.php @@ -48,6 +48,10 @@ public function getOverview() { 'open source projects keep track of Contributor License Agreements.'); } + public function getMonograms() { + return array('L'); + } + public function getRoutes() { return array( '/L(?P\d+)' => 'LegalpadDocumentSignController', diff --git a/src/applications/maniphest/application/PhabricatorManiphestApplication.php b/src/applications/maniphest/application/PhabricatorManiphestApplication.php index 8ed20416bb..9296060d81 100644 --- a/src/applications/maniphest/application/PhabricatorManiphestApplication.php +++ b/src/applications/maniphest/application/PhabricatorManiphestApplication.php @@ -42,6 +42,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('T'); + } + public function getRoutes() { return array( '/T(?P[1-9]\d*)' => 'ManiphestTaskDetailController', diff --git a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php index 8cceeb7396..1bf6734db3 100644 --- a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php +++ b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php @@ -38,6 +38,30 @@ public function handleRequest(AphrontRequest $request) { $header->setStatus('fa-ban', 'dark', pht('Uninstalled')); } + if (!$selected->isFirstParty()) { + $header->addTag(id(new PHUITagView()) + ->setName('Extension') + ->setIcon('fa-plug') + ->setColor(PHUITagView::COLOR_INDIGO) + ->setType(PHUITagView::TYPE_SHADE)); + } + + if ($selected->isPrototype()) { + $header->addTag(id(new PHUITagView()) + ->setName('Prototype') + ->setIcon('fa-exclamation-circle') + ->setColor(PHUITagView::COLOR_ORANGE) + ->setType(PHUITagView::TYPE_SHADE)); + } + + if ($selected->isDeprecated()) { + $header->addTag(id(new PHUITagView()) + ->setName('Deprecated') + ->setIcon('fa-exclamation-triangle') + ->setColor(PHUITagView::COLOR_RED) + ->setType(PHUITagView::TYPE_SHADE)); + } + $timeline = $this->buildTransactionTimeline( $selected, new PhabricatorApplicationApplicationTransactionQuery()); @@ -95,6 +119,27 @@ private function buildPropertySectionView( phutil_tag('em', array(), $application->getFlavorText())); } + $phids = PhabricatorPHIDType::getAllTypesForApplication( + get_class($application)); + + $user_friendly_phids = array(); + foreach ($phids as $phid => $type) { + $user_friendly_phids[] = "PHID-{$phid} ({$type->getTypeName()})"; + } + + if ($user_friendly_phids) { + $properties->addProperty( + 'PHIDs', + phutil_implode_html(phutil_tag('br'), $user_friendly_phids)); + } + + $monograms = $application->getMonograms(); + if ($monograms) { + $properties->addProperty( + 'Monograms', + phutil_implode_html(', ', $monograms)); + } + if ($application->isPrototype()) { $proto_href = PhabricatorEnv::getDoclink( 'User Guide: Prototype Applications'); diff --git a/src/applications/meta/query/PhabricatorAppSearchEngine.php b/src/applications/meta/query/PhabricatorAppSearchEngine.php index e68570fe1c..08283afc2e 100644 --- a/src/applications/meta/query/PhabricatorAppSearchEngine.php +++ b/src/applications/meta/query/PhabricatorAppSearchEngine.php @@ -231,13 +231,13 @@ protected function renderResultList( ->setSideColumn($configure); if (!$application->isFirstParty()) { - $tag = id(new PHUITagView()) + $extension_tag = id(new PHUITagView()) ->setName(pht('Extension')) - ->setIcon('fa-puzzle-piece') - ->setColor(PHUITagView::COLOR_BLUE) + ->setIcon('fa-plug') + ->setColor(PHUITagView::COLOR_INDIGO) ->setType(PHUITagView::TYPE_SHADE) ->setSlimShady(true); - $item->addAttribute($tag); + $item->addAttribute($extension_tag); } if ($application->isPrototype()) { @@ -250,6 +250,16 @@ protected function renderResultList( $item->addAttribute($prototype_tag); } + if ($application->isDeprecated()) { + $deprecated_tag = id(new PHUITagView()) + ->setName(pht('Deprecated')) + ->setIcon('fa-exclamation-triangle') + ->setColor(PHUITagView::COLOR_RED) + ->setType(PHUITagView::TYPE_SHADE) + ->setSlimShady(true); + $item->addAttribute($deprecated_tag); + } + $item->addAttribute($description); if ($application->getBaseURI() && $application->isInstalled()) { diff --git a/src/applications/owners/application/PhabricatorOwnersApplication.php b/src/applications/owners/application/PhabricatorOwnersApplication.php index 3ef5f974d9..34bbc4b7b5 100644 --- a/src/applications/owners/application/PhabricatorOwnersApplication.php +++ b/src/applications/owners/application/PhabricatorOwnersApplication.php @@ -45,6 +45,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('O'); + } + public function getRoutes() { return array( '/owners/' => array( diff --git a/src/applications/passphrase/application/PhabricatorPassphraseApplication.php b/src/applications/passphrase/application/PhabricatorPassphraseApplication.php index 2ab4f1e33d..ae79e5c8b5 100644 --- a/src/applications/passphrase/application/PhabricatorPassphraseApplication.php +++ b/src/applications/passphrase/application/PhabricatorPassphraseApplication.php @@ -34,6 +34,10 @@ public function canUninstall() { return false; } + public function getMonograms() { + return array('K'); + } + public function getRoutes() { return array( '/K(?P\d+)' => 'PassphraseCredentialViewController', diff --git a/src/applications/paste/application/PhabricatorPasteApplication.php b/src/applications/paste/application/PhabricatorPasteApplication.php index 26e4b1740e..f6b6f7cf65 100644 --- a/src/applications/paste/application/PhabricatorPasteApplication.php +++ b/src/applications/paste/application/PhabricatorPasteApplication.php @@ -32,6 +32,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('P'); + } + public function getRoutes() { return array( '/P(?P[1-9]\d*)(?:\$(?P\d+(?:-\d+)?))?' diff --git a/src/applications/phame/application/PhabricatorPhameApplication.php b/src/applications/phame/application/PhabricatorPhameApplication.php index cd2eb4b487..b030673d1f 100644 --- a/src/applications/phame/application/PhabricatorPhameApplication.php +++ b/src/applications/phame/application/PhabricatorPhameApplication.php @@ -31,6 +31,10 @@ public function getHelpDocumentationArticles(PhabricatorUser $viewer) { ); } + public function getMonograms() { + return array('J'); + } + public function getRoutes() { return array( '/J(?P[1-9]\d*)' => 'PhamePostViewController', diff --git a/src/applications/phid/type/PhabricatorPHIDType.php b/src/applications/phid/type/PhabricatorPHIDType.php index 2f82dcf169..f15df7f886 100644 --- a/src/applications/phid/type/PhabricatorPHIDType.php +++ b/src/applications/phid/type/PhabricatorPHIDType.php @@ -205,4 +205,28 @@ public static function getAllInstalledTypes(PhabricatorUser $viewer) { return $installed_types; } + + /** + * Get all PHID types of applications installed for a given viewer. + * + * @param PhabricatorUser Viewing user. + * @return dict Map of constants to installed + * types. + */ + public static function getAllTypesForApplication( + string $application) { + $all_types = self::getAllTypes(); + + $application_types = array(); + + foreach ($all_types as $key => $type) { + if ($type->getPHIDTypeApplicationClass() != $application) { + continue; + } + + $application_types[$key] = $type; + } + + return $application_types; + } } diff --git a/src/applications/pholio/application/PhabricatorPholioApplication.php b/src/applications/pholio/application/PhabricatorPholioApplication.php index 4d80dd12ae..5ac650cc20 100644 --- a/src/applications/pholio/application/PhabricatorPholioApplication.php +++ b/src/applications/pholio/application/PhabricatorPholioApplication.php @@ -32,6 +32,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('M'); + } + public function getRoutes() { return array( '/M(?P[1-9]\d*)(?:/(?P\d+)/)?' => 'PholioMockViewController', diff --git a/src/applications/phurl/application/PhabricatorPhurlApplication.php b/src/applications/phurl/application/PhabricatorPhurlApplication.php index 14ffda77f1..56290ef5e1 100644 --- a/src/applications/phurl/application/PhabricatorPhurlApplication.php +++ b/src/applications/phurl/application/PhabricatorPhurlApplication.php @@ -37,6 +37,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('U'); + } + public function getRoutes() { return array( '/U(?P[1-9]\d*)/?' => 'PhabricatorPhurlURLViewController', diff --git a/src/applications/ponder/application/PhabricatorPonderApplication.php b/src/applications/ponder/application/PhabricatorPonderApplication.php index 56973447f9..e6f4ef8867 100644 --- a/src/applications/ponder/application/PhabricatorPonderApplication.php +++ b/src/applications/ponder/application/PhabricatorPonderApplication.php @@ -47,6 +47,10 @@ public function getAppEmailBlurb() { pht('Learn More'))); } + public function getMonograms() { + return array('Q'); + } + public function getRoutes() { return array( '/Q(?P[1-9]\d*)' diff --git a/src/applications/slowvote/application/PhabricatorSlowvoteApplication.php b/src/applications/slowvote/application/PhabricatorSlowvoteApplication.php index 1e4bd78419..8fa4edc6f6 100644 --- a/src/applications/slowvote/application/PhabricatorSlowvoteApplication.php +++ b/src/applications/slowvote/application/PhabricatorSlowvoteApplication.php @@ -45,6 +45,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('V'); + } + public function getRoutes() { return array( '/V(?P[1-9]\d*)' => 'PhabricatorSlowvotePollController', diff --git a/src/applications/spaces/application/PhabricatorSpacesApplication.php b/src/applications/spaces/application/PhabricatorSpacesApplication.php index d542551ae4..f40538b69b 100644 --- a/src/applications/spaces/application/PhabricatorSpacesApplication.php +++ b/src/applications/spaces/application/PhabricatorSpacesApplication.php @@ -49,6 +49,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('S'); + } + public function getRoutes() { return array( '/S(?P[1-9]\d*)' => 'PhabricatorSpacesViewController', From 5bd526646144e062f7c68e15ba07d03e6047adb6 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 22 Nov 2023 16:53:55 +0100 Subject: [PATCH 345/425] Fix a PHP 8.1 deprecated use of strlen with a NULL argument Summary: This call prevents users to view a commit in subversion repositories Indeed, if commiter and/or author field is not properly defined strlen is call with a NULL argument. Using strlen to check string validity is deprecated since PHP 8.1, phorge adopts phutil_nonempty_string() as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. Fix T15610 Test Plan: - Sign in (if needed) - Open a diffusion SVN repository - Open a commit without user name and or email - You should be able to view the commit Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15610 Differential Revision: https://we.phorge.it/D25400 --- .../repository/storage/PhabricatorRepositoryCommit.php | 4 ++-- .../repository/storage/PhabricatorRepositoryCommitData.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php index 9e20a36676..ef3657d49f 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php @@ -478,7 +478,7 @@ public function newCommitAuthorView(PhabricatorUser $viewer) { } $author = $this->getRawAuthorStringForDisplay(); - if (strlen($author)) { + if (phutil_nonempty_string($author)) { return DiffusionView::renderName($author); } @@ -493,7 +493,7 @@ public function newCommitCommitterView(PhabricatorUser $viewer) { } $committer = $this->getRawCommitterStringForDisplay(); - if (strlen($committer)) { + if (phutil_nonempty_string($committer)) { return DiffusionView::renderName($committer); } diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommitData.php b/src/applications/repository/storage/PhabricatorRepositoryCommitData.php index c77da64ec2..4726658b7a 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommitData.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommitData.php @@ -131,7 +131,7 @@ public function getCommitterString() { $ref = $this->getCommitRef(); $committer = $ref->getCommitter(); - if (strlen($committer)) { + if (phutil_nonempty_string($committer)) { return $committer; } From acfb44d6679edc87e9f209f011e77ee2594cd8ae Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 22 Nov 2023 16:55:08 +0100 Subject: [PATCH 346/425] Fix a PHP 8.1 deprecated use of strlen with a NULL argument on commit page Summary: With PHP 8.1+ it is not possible to view a commit if the author field is not properly defined Indeed, if the commit author is not properly defined, strlen(null) is called, causing a deprecation warning, elevated to exception. Using strlen() to check string validity is deprecated since PHP 8.1. Phorge adopts phutil_nonempty_string() as a replacement. Fix T15628 Test Plan: - Push a new commit on a subversion repository (since T15629 is not yet addressed) - Visualize the commit - You should not get a RuntimeException Reviewers: O1 Blessed Committers, Sten, valerio.bozzolan Reviewed By: O1 Blessed Committers, Sten, valerio.bozzolan Subscribers: Sten, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15628 Differential Revision: https://we.phorge.it/D25422 --- .../repository/storage/PhabricatorRepositoryCommitData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommitData.php b/src/applications/repository/storage/PhabricatorRepositoryCommitData.php index 4726658b7a..96508a2452 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommitData.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommitData.php @@ -97,7 +97,7 @@ public function getAuthorString() { $ref = $this->getCommitRef(); $author = $ref->getAuthor(); - if (strlen($author)) { + if (phutil_nonempty_string($author)) { return $author; } From cf8d5d60a594f2f172450879327caac0f0e6afc8 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 22 Nov 2023 16:56:01 +0100 Subject: [PATCH 347/425] Fix a PHP 8.1 deprecated use of strlen with a NULL argument in commit parser Summary: With PHP 8.1+ it is not possible to import a commit if the commiter field is not properly defined Indeed, if the committer is not properly defined, strlen(null) is called, causing a deprecation warning, elevated to exception. Using strlen() to check string validity is deprecated since PHP 8.1. Phorge adopts phutil_nonempty_string() as a replacement. Fix T15629 Test Plan: - Push a commit to an observed subversion repository - Import it via the phorge/bin/repository reparse COMMIT_ID --importing - The commit should be properly imported and available in Diffusion Reviewers: O1 Blessed Committers, Sten, valerio.bozzolan Reviewed By: O1 Blessed Committers, Sten, valerio.bozzolan Subscribers: Sten, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15629 Differential Revision: https://we.phorge.it/D25423 --- .../PhabricatorRepositoryCommitMessageParserWorker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php index a0ea2d84b2..fc0bd42477 100644 --- a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php +++ b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php @@ -36,7 +36,7 @@ final protected function updateCommitData( $author = $ref->getAuthor(); $committer = $ref->getCommitter(); - $has_committer = (bool)strlen($committer); + $has_committer = phutil_nonempty_string($committer); $identity_engine = id(new DiffusionRepositoryIdentityEngine()) ->setViewer($viewer) From 4d4712b58d2a4c9856d9d462e4737eac08604f09 Mon Sep 17 00:00:00 2001 From: Matthew Bowker Date: Mon, 27 Nov 2023 11:36:12 -0700 Subject: [PATCH 348/425] Remove Chatlog entirely Summary: This commit removes ChatLog entirely. All of the application files are removed, and the migrations used are stubbed out. I stubbed the migrations as that allows for existing installs to make no changes, but new installs will not create the database. Fixes T15126 Test Plan: Loaded up http://phorge.local/chatlog and confirmed the 404. Loaded up http://phorge.local/applications/view/PhabricatorChatLogApplication and confirmed the 404. Created a new database prefix and ran `bin/storage upgrade` against it, confirmed that the chatlog database was not created. Restored another prefix (an old one) and ran `bin/storage upgrade` and confirmed database was not deleted. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, tobiaswiese, valerio.bozzolan, Cigaryno Maniphest Tasks: T15126 Differential Revision: https://we.phorge.it/D25480 --- resources/celerity/map.php | 2 - .../sql/autopatches/20140722.appname.php | 1 - resources/sql/patches/106.chatlog.sql | 11 +- .../116.utf8-backup-first-expect-wait.sql | 18 - .../sql/patches/20130214.chatlogchannel.sql | 12 +- .../sql/patches/20130214.chatlogchannelid.sql | 3 +- .../sql/patches/20130218.updatechannelid.php | 63 +--- .../sql/patches/20130222.dropchannel.sql | 3 +- resources/sql/quickstart.sql | 39 --- src/__phutil_library_map__.php | 30 -- .../PhabricatorChatLogApplication.php | 47 --- .../conduit/ChatLogConduitAPIMethod.php | 9 - .../conduit/ChatLogQueryConduitAPIMethod.php | 59 ---- .../conduit/ChatLogRecordConduitAPIMethod.php | 72 ---- ...habricatorChatLogChannelListController.php | 41 --- ...PhabricatorChatLogChannelLogController.php | 320 ------------------ .../PhabricatorChatLogController.php | 3 - .../query/PhabricatorChatLogChannelQuery.php | 63 ---- .../chatlog/query/PhabricatorChatLogQuery.php | 84 ----- .../storage/PhabricatorChatLogChannel.php | 51 --- .../chatlog/storage/PhabricatorChatLogDAO.php | 9 - .../storage/PhabricatorChatLogEvent.php | 59 ---- src/docs/book/phorge.book | 4 - .../patch/PhabricatorBuiltinPatchList.php | 1 - .../rsrc/css/application/chatlog/chatlog.css | 81 ----- 25 files changed, 5 insertions(+), 1080 deletions(-) delete mode 100644 src/applications/chatlog/application/PhabricatorChatLogApplication.php delete mode 100644 src/applications/chatlog/conduit/ChatLogConduitAPIMethod.php delete mode 100644 src/applications/chatlog/conduit/ChatLogQueryConduitAPIMethod.php delete mode 100644 src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php delete mode 100644 src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php delete mode 100644 src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php delete mode 100644 src/applications/chatlog/controller/PhabricatorChatLogController.php delete mode 100644 src/applications/chatlog/query/PhabricatorChatLogChannelQuery.php delete mode 100644 src/applications/chatlog/query/PhabricatorChatLogQuery.php delete mode 100644 src/applications/chatlog/storage/PhabricatorChatLogChannel.php delete mode 100644 src/applications/chatlog/storage/PhabricatorChatLogDAO.php delete mode 100644 src/applications/chatlog/storage/PhabricatorChatLogEvent.php delete mode 100644 webroot/rsrc/css/application/chatlog/chatlog.css diff --git a/resources/celerity/map.php b/resources/celerity/map.php index fca4e57058..5fc371bcf0 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -42,7 +42,6 @@ 'rsrc/css/application/base/notification-menu.css' => '4df1ee30', 'rsrc/css/application/base/phui-theme.css' => '35883b37', 'rsrc/css/application/base/standard-page-view.css' => 'e08c7462', - 'rsrc/css/application/chatlog/chatlog.css' => 'abdc76ee', 'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41', 'rsrc/css/application/config/config-options.css' => '16c920ae', 'rsrc/css/application/config/config-template.css' => 'e689dbbd', @@ -770,7 +769,6 @@ 'people-profile-css' => '2ea2daa1', 'phabricator-action-list-view-css' => '1b0085b2', 'phabricator-busy' => '5202e831', - 'phabricator-chatlog-css' => 'abdc76ee', 'phabricator-content-source-view-css' => 'cdf0d579', 'phabricator-core-css' => 'b3a5928e', 'phabricator-countdown-css' => 'bff8012f', diff --git a/resources/sql/autopatches/20140722.appname.php b/resources/sql/autopatches/20140722.appname.php index dd8e929357..2a337c35aa 100644 --- a/resources/sql/autopatches/20140722.appname.php +++ b/resources/sql/autopatches/20140722.appname.php @@ -4,7 +4,6 @@ 'Audit', 'Auth', 'Calendar', - 'ChatLog', 'Conduit', 'Config', 'Conpherence', diff --git a/resources/sql/patches/106.chatlog.sql b/resources/sql/patches/106.chatlog.sql index bbb9be945e..04b0e849bc 100644 --- a/resources/sql/patches/106.chatlog.sql +++ b/resources/sql/patches/106.chatlog.sql @@ -1,10 +1 @@ -CREATE TABLE {$NAMESPACE}_chatlog.chatlog_event ( - id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, - channel VARCHAR(64) BINARY NOT NULL, - epoch INT UNSIGNED NOT NULL, - author VARCHAR(64) BINARY NOT NULL, - type VARCHAR(4) NOT NULL, - message LONGBLOB NOT NULL, - loggedByPHID VARCHAR(64) BINARY NOT NULL, - KEY (channel, epoch) -); +/* This file is intentionally left empty, see T15126 */ diff --git a/resources/sql/patches/116.utf8-backup-first-expect-wait.sql b/resources/sql/patches/116.utf8-backup-first-expect-wait.sql index eda5573641..5f00c03e38 100644 --- a/resources/sql/patches/116.utf8-backup-first-expect-wait.sql +++ b/resources/sql/patches/116.utf8-backup-first-expect-wait.sql @@ -16,24 +16,6 @@ ALTER TABLE `{$NAMESPACE}_audit`.`audit_comment` -ALTER DATABASE `{$NAMESPACE}_chatlog` COLLATE utf8_general_ci; - -ALTER TABLE `{$NAMESPACE}_chatlog`.`chatlog_event` - MODIFY `channel` varchar(64) CHARACTER SET binary, - MODIFY `author` varchar(64) CHARACTER SET binary, - MODIFY `type` varchar(4) CHARACTER SET binary, - MODIFY `message` longtext CHARACTER SET binary, - MODIFY `loggedByPHID` varchar(64) CHARACTER SET binary; -ALTER TABLE `{$NAMESPACE}_chatlog`.`chatlog_event` - COLLATE utf8_general_ci, - MODIFY `channel` varchar(64) COLLATE utf8_bin NOT NULL, - MODIFY `author` varchar(64) COLLATE utf8_bin NOT NULL, - MODIFY `type` varchar(4) COLLATE utf8_general_ci NOT NULL, - MODIFY `message` longtext COLLATE utf8_bin NOT NULL, - MODIFY `loggedByPHID` varchar(64) COLLATE utf8_bin NOT NULL; - - - ALTER DATABASE `{$NAMESPACE}_conduit` COLLATE utf8_general_ci; ALTER TABLE `{$NAMESPACE}_conduit`.`conduit_certificatetoken` diff --git a/resources/sql/patches/20130214.chatlogchannel.sql b/resources/sql/patches/20130214.chatlogchannel.sql index 6bb0a777ac..04b0e849bc 100644 --- a/resources/sql/patches/20130214.chatlogchannel.sql +++ b/resources/sql/patches/20130214.chatlogchannel.sql @@ -1,11 +1 @@ -CREATE TABLE {$NAMESPACE}_chatlog.chatlog_channel ( - id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, - serviceName VARCHAR(64) COLLATE utf8_bin NOT NULL, - serviceType VARCHAR(32) COLLATE utf8_bin NOT NULL, - channelName VARCHAR(64) COLLATE utf8_bin NOT NULL, - viewPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL, - editPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL, - dateCreated INT UNSIGNED NOT NULL, - dateModified INT UNSIGNED NOT NULL, - UNIQUE KEY `key_channel` (channelName, serviceType, serviceName) -)ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* This file is intentionally left empty, see T15126 */ diff --git a/resources/sql/patches/20130214.chatlogchannelid.sql b/resources/sql/patches/20130214.chatlogchannelid.sql index bc522e205c..04b0e849bc 100644 --- a/resources/sql/patches/20130214.chatlogchannelid.sql +++ b/resources/sql/patches/20130214.chatlogchannelid.sql @@ -1,2 +1 @@ -ALTER TABLE `{$NAMESPACE}_chatlog`.`chatlog_event` - ADD `channelID` INT UNSIGNED NOT NULL; +/* This file is intentionally left empty, see T15126 */ diff --git a/resources/sql/patches/20130218.updatechannelid.php b/resources/sql/patches/20130218.updatechannelid.php index cf60544a3b..38ec156d06 100644 --- a/resources/sql/patches/20130218.updatechannelid.php +++ b/resources/sql/patches/20130218.updatechannelid.php @@ -1,64 +1,3 @@ openTransaction(); -$channel_table->openTransaction(); - -$event_table->beginReadLocking(); -$channel_table->beginReadLocking(); - -$events = new LiskMigrationIterator($event_table); -$conn_w = $channel_table->establishConnection('w'); - -foreach ($events as $event) { - if ($event->getChannelID()) { - continue; - } - - $event_row = queryfx_one( - $conn_w, - 'SELECT channel FROM %T WHERE id = %d', - $event->getTableName(), - $event->getID()); - $event_channel = $event_row['channel']; - - $matched = queryfx_one( - $conn_w, - 'SELECT * FROM %T WHERE - channelName = %s AND serviceName = %s AND serviceType = %s', - $channel_table->getTableName(), - $event_channel, - '', - ''); - - if (!$matched) { - $matched = id(new PhabricatorChatLogChannel()) - ->setChannelName($event_channel) - ->setServiceType('') - ->setServiceName('') - ->setViewPolicy(PhabricatorPolicies::POLICY_USER) - ->setEditPolicy(PhabricatorPolicies::POLICY_USER) - ->save(); - $matched_id = $matched->getID(); - } else { - $matched_id = $matched['id']; - } - - queryfx( - $event->establishConnection('w'), - 'UPDATE %T SET channelID = %d WHERE id = %d', - $event->getTableName(), - $matched_id, - $event->getID()); -} - -$event_table->endReadLocking(); -$channel_table->endReadLocking(); - -$event_table->saveTransaction(); -$channel_table->saveTransaction(); - -echo "\n".pht('Done.')."\n"; +/* This file is intentionally left empty, see T15126 */ diff --git a/resources/sql/patches/20130222.dropchannel.sql b/resources/sql/patches/20130222.dropchannel.sql index 00e6a13f18..04b0e849bc 100644 --- a/resources/sql/patches/20130222.dropchannel.sql +++ b/resources/sql/patches/20130222.dropchannel.sql @@ -1,2 +1 @@ -ALTER TABLE `{$NAMESPACE}_chatlog`.`chatlog_event` - DROP channel; +/* This file is intentionally left empty, see T15126 */ diff --git a/resources/sql/quickstart.sql b/resources/sql/quickstart.sql index b2796d31e6..2df0d4f252 100644 --- a/resources/sql/quickstart.sql +++ b/resources/sql/quickstart.sql @@ -1477,45 +1477,6 @@ CREATE TABLE `edgedata` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; -CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_chatlog` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; - -USE `{$NAMESPACE}_chatlog`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `chatlog_channel` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `serviceName` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `serviceType` varchar(32) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `channelName` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `viewPolicy` varbinary(64) NOT NULL, - `editPolicy` varbinary(64) NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_channel` (`channelName`,`serviceType`,`serviceName`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_chatlog`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `chatlog_event` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `epoch` int(10) unsigned NOT NULL, - `author` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `type` varchar(4) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `message` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `loggedByPHID` varbinary(64) NOT NULL, - `channelID` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - KEY `channel` (`epoch`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_conduit` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; USE `{$NAMESPACE}_conduit`; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index ad0d8fc937..f2a39023e4 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -339,9 +339,6 @@ 'CelerityResourcesOnDisk' => 'applications/celerity/resources/CelerityResourcesOnDisk.php', 'CeleritySpriteGenerator' => 'applications/celerity/CeleritySpriteGenerator.php', 'CelerityStaticResourceResponse' => 'applications/celerity/CelerityStaticResourceResponse.php', - 'ChatLogConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogConduitAPIMethod.php', - 'ChatLogQueryConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogQueryConduitAPIMethod.php', - 'ChatLogRecordConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php', 'ConduitAPIDocumentationPage' => 'applications/conduit/data/ConduitAPIDocumentationPage.php', 'ConduitAPIMethod' => 'applications/conduit/method/ConduitAPIMethod.php', 'ConduitAPIMethodTestCase' => 'applications/conduit/method/__tests__/ConduitAPIMethodTestCase.php', @@ -2808,15 +2805,6 @@ 'PhabricatorChartInterval' => 'applications/fact/chart/PhabricatorChartInterval.php', 'PhabricatorChartRenderingEngine' => 'applications/fact/engine/PhabricatorChartRenderingEngine.php', 'PhabricatorChartStackedAreaDataset' => 'applications/fact/chart/PhabricatorChartStackedAreaDataset.php', - 'PhabricatorChatLogApplication' => 'applications/chatlog/application/PhabricatorChatLogApplication.php', - 'PhabricatorChatLogChannel' => 'applications/chatlog/storage/PhabricatorChatLogChannel.php', - 'PhabricatorChatLogChannelListController' => 'applications/chatlog/controller/PhabricatorChatLogChannelListController.php', - 'PhabricatorChatLogChannelLogController' => 'applications/chatlog/controller/PhabricatorChatLogChannelLogController.php', - 'PhabricatorChatLogChannelQuery' => 'applications/chatlog/query/PhabricatorChatLogChannelQuery.php', - 'PhabricatorChatLogController' => 'applications/chatlog/controller/PhabricatorChatLogController.php', - 'PhabricatorChatLogDAO' => 'applications/chatlog/storage/PhabricatorChatLogDAO.php', - 'PhabricatorChatLogEvent' => 'applications/chatlog/storage/PhabricatorChatLogEvent.php', - 'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php', 'PhabricatorCheckboxesEditField' => 'applications/transactions/editfield/PhabricatorCheckboxesEditField.php', 'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php', 'PhabricatorClassConfigType' => 'applications/config/type/PhabricatorClassConfigType.php', @@ -6339,9 +6327,6 @@ 'CelerityResourcesOnDisk' => 'CelerityPhysicalResources', 'CeleritySpriteGenerator' => 'Phobject', 'CelerityStaticResourceResponse' => 'Phobject', - 'ChatLogConduitAPIMethod' => 'ConduitAPIMethod', - 'ChatLogQueryConduitAPIMethod' => 'ChatLogConduitAPIMethod', - 'ChatLogRecordConduitAPIMethod' => 'ChatLogConduitAPIMethod', 'ConduitAPIDocumentationPage' => 'Phobject', 'ConduitAPIMethod' => array( 'Phobject', @@ -9191,21 +9176,6 @@ 'PhabricatorChartInterval' => 'Phobject', 'PhabricatorChartRenderingEngine' => 'Phobject', 'PhabricatorChartStackedAreaDataset' => 'PhabricatorChartDataset', - 'PhabricatorChatLogApplication' => 'PhabricatorApplication', - 'PhabricatorChatLogChannel' => array( - 'PhabricatorChatLogDAO', - 'PhabricatorPolicyInterface', - ), - 'PhabricatorChatLogChannelListController' => 'PhabricatorChatLogController', - 'PhabricatorChatLogChannelLogController' => 'PhabricatorChatLogController', - 'PhabricatorChatLogChannelQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'PhabricatorChatLogController' => 'PhabricatorController', - 'PhabricatorChatLogDAO' => 'PhabricatorLiskDAO', - 'PhabricatorChatLogEvent' => array( - 'PhabricatorChatLogDAO', - 'PhabricatorPolicyInterface', - ), - 'PhabricatorChatLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorCheckboxesEditField' => 'PhabricatorEditField', 'PhabricatorChunkedFileStorageEngine' => 'PhabricatorFileStorageEngine', 'PhabricatorClassConfigType' => 'PhabricatorTextConfigType', diff --git a/src/applications/chatlog/application/PhabricatorChatLogApplication.php b/src/applications/chatlog/application/PhabricatorChatLogApplication.php deleted file mode 100644 index ab5eb1960f..0000000000 --- a/src/applications/chatlog/application/PhabricatorChatLogApplication.php +++ /dev/null @@ -1,47 +0,0 @@ - array( - '' => 'PhabricatorChatLogChannelListController', - 'channel/(?P[^/]+)/' - => 'PhabricatorChatLogChannelLogController', - ), - ); - } - -} diff --git a/src/applications/chatlog/conduit/ChatLogConduitAPIMethod.php b/src/applications/chatlog/conduit/ChatLogConduitAPIMethod.php deleted file mode 100644 index e524ef60da..0000000000 --- a/src/applications/chatlog/conduit/ChatLogConduitAPIMethod.php +++ /dev/null @@ -1,9 +0,0 @@ - 'optional list', - 'limit' => 'optional int (default = 100)', - ); - } - - protected function defineReturnType() { - return 'nonempty list'; - } - - protected function execute(ConduitAPIRequest $request) { - $query = new PhabricatorChatLogQuery(); - - $channel_ids = $request->getValue('channelIDs'); - if ($channel_ids) { - $query->withChannelIDs($channel_ids); - } - - $limit = $request->getValue('limit'); - if (!$limit) { - $limit = 100; - } - $query->setLimit($limit); - - $logs = $query->execute(); - - $results = array(); - foreach ($logs as $log) { - $results[] = array( - 'channelID' => $log->getChannelID(), - 'epoch' => $log->getEpoch(), - 'author' => $log->getAuthor(), - 'type' => $log->getType(), - 'message' => $log->getMessage(), - 'loggedByPHID' => $log->getLoggedByPHID(), - ); - } - - return $results; - } - -} diff --git a/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php b/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php deleted file mode 100644 index fe972222ae..0000000000 --- a/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php +++ /dev/null @@ -1,72 +0,0 @@ - 'required list', - ); - } - - protected function defineReturnType() { - return 'list'; - } - - protected function execute(ConduitAPIRequest $request) { - $logs = $request->getValue('logs'); - if (!is_array($logs)) { - $logs = array(); - } - - $template = new PhabricatorChatLogEvent(); - $template->setLoggedByPHID($request->getUser()->getPHID()); - - $objs = array(); - foreach ($logs as $log) { - $channel_name = idx($log, 'channel'); - $service_name = idx($log, 'serviceName'); - $service_type = idx($log, 'serviceType'); - - $channel = id(new PhabricatorChatLogChannel())->loadOneWhere( - 'channelName = %s AND serviceName = %s AND serviceType = %s', - $channel_name, - $service_name, - $service_type); - - if (!$channel) { - $channel = id(new PhabricatorChatLogChannel()) - ->setChannelName($channel_name) - ->setserviceName($service_name) - ->setServiceType($service_type) - ->setViewPolicy(PhabricatorPolicies::POLICY_USER) - ->setEditPolicy(PhabricatorPolicies::POLICY_USER) - ->save(); - } - - $obj = clone $template; - $obj->setChannelID($channel->getID()); - $obj->setType(idx($log, 'type')); - $obj->setAuthor(idx($log, 'author')); - $obj->setEpoch(idx($log, 'epoch')); - $obj->setMessage(idx($log, 'message')); - $obj->save(); - - $objs[] = $obj; - } - - return array_values(mpull($objs, 'getID')); - } - -} diff --git a/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php b/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php deleted file mode 100644 index 530c26770a..0000000000 --- a/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php +++ /dev/null @@ -1,41 +0,0 @@ -getViewer(); - - $channels = id(new PhabricatorChatLogChannelQuery()) - ->setViewer($viewer) - ->execute(); - - $list = new PHUIObjectItemListView(); - foreach ($channels as $channel) { - $item = id(new PHUIObjectItemView()) - ->setHeader($channel->getChannelName()) - ->setHref('/chatlog/channel/'.$channel->getID().'/') - ->addAttribute($channel->getServiceName()) - ->addAttribute($channel->getServiceType()); - $list->addItem($item); - } - - $crumbs = $this - ->buildApplicationCrumbs() - ->addTextCrumb(pht('Channel List'), $this->getApplicationURI()); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText('Channel List') - ->setObjectList($list); - - return $this->newPage() - ->setTitle(pht('Channel List')) - ->setCrumbs($crumbs) - ->appendChild($box); - - } -} diff --git a/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php b/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php deleted file mode 100644 index b9893f6924..0000000000 --- a/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php +++ /dev/null @@ -1,320 +0,0 @@ -getViewer(); - $id = $request->getURIData('channelID'); - - $uri = new PhutilURI($request->getPath()); - - $pager = new AphrontCursorPagerView(); - $pager->setURI($uri); - $pager->setPageSize(250); - - $query = id(new PhabricatorChatLogQuery()) - ->setViewer($viewer) - ->withChannelIDs(array($id)); - - $channel = id(new PhabricatorChatLogChannelQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - - if (!$channel) { - return new Aphront404Response(); - } - - list($after, $before, $map) = $this->getPagingParameters($request, $query); - - $pager->setAfterID($after); - $pager->setBeforeID($before); - - $logs = $query->executeWithCursorPager($pager); - - // Show chat logs oldest-first. - $logs = array_reverse($logs); - - - // Divide all the logs into blocks, where a block is the same author saying - // several things in a row. A block ends when another user speaks, or when - // two minutes pass without the author speaking. - - $blocks = array(); - $block = null; - - $last_author = null; - $last_epoch = null; - foreach ($logs as $log) { - $this_author = $log->getAuthor(); - $this_epoch = $log->getEpoch(); - - // Decide whether we should start a new block or not. - $new_block = ($this_author !== $last_author) || - ($this_epoch - (60 * 2) > $last_epoch); - - if ($new_block) { - if ($block) { - $blocks[] = $block; - } - $block = array( - 'id' => $log->getID(), - 'epoch' => $this_epoch, - 'author' => $this_author, - 'logs' => array($log), - ); - } else { - $block['logs'][] = $log; - } - - $last_author = $this_author; - $last_epoch = $this_epoch; - } - if ($block) { - $blocks[] = $block; - } - - // Figure out CSS classes for the blocks. We alternate colors between - // lines, and highlight the entire block which contains the target ID or - // date, if applicable. - - foreach ($blocks as $key => $block) { - $classes = array(); - if ($key % 2) { - $classes[] = 'alternate'; - } - $ids = mpull($block['logs'], 'getID', 'getID'); - if (array_intersect_key($ids, $map)) { - $classes[] = 'highlight'; - } - $blocks[$key]['class'] = $classes ? implode(' ', $classes) : null; - } - - - require_celerity_resource('phabricator-chatlog-css'); - - $out = array(); - foreach ($blocks as $block) { - $author = $block['author']; - $author = id(new PhutilUTF8StringTruncator()) - ->setMaximumGlyphs(18) - ->truncateString($author); - $author = phutil_tag('td', array('class' => 'author'), $author); - - $href = $uri->alter('at', $block['id']); - $timestamp = $block['epoch']; - $timestamp = phabricator_datetime($timestamp, $viewer); - $timestamp = phutil_tag( - 'a', - array( - 'href' => $href, - 'class' => 'timestamp', - ), - $timestamp); - - $message = mpull($block['logs'], 'getMessage'); - $message = implode("\n", $message); - $message = phutil_tag( - 'td', - array( - 'class' => 'message', - ), - array( - $timestamp, - $message, - )); - - $out[] = phutil_tag( - 'tr', - array( - 'class' => $block['class'], - ), - array( - $author, - $message, - )); - } - - $links = array(); - - $first_uri = $pager->getFirstPageURI(); - if ($first_uri) { - $links[] = phutil_tag( - 'a', - array( - 'href' => $first_uri, - ), - "\xC2\xAB ".pht('Newest')); - } - - $prev_uri = $pager->getPrevPageURI(); - if ($prev_uri) { - $links[] = phutil_tag( - 'a', - array( - 'href' => $prev_uri, - ), - "\xE2\x80\xB9 ".pht('Newer')); - } - - $next_uri = $pager->getNextPageURI(); - if ($next_uri) { - $links[] = phutil_tag( - 'a', - array( - 'href' => $next_uri, - ), - pht('Older')." \xE2\x80\xBA"); - } - - $pager_bottom = phutil_tag( - 'div', - array('class' => 'phabricator-chat-log-pager-bottom'), - $links); - - $crumbs = $this - ->buildApplicationCrumbs() - ->addTextCrumb($channel->getChannelName(), $uri); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->setMethod('GET') - ->setAction($uri) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Date')) - ->setName('date') - ->setValue($request->getStr('date'))) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Jump'))); - - $table = phutil_tag( - 'table', - array( - 'class' => 'phabricator-chat-log', - ), - $out); - - $log = phutil_tag( - 'div', - array( - 'class' => 'phabricator-chat-log-panel', - ), - $table); - - $jump_link = id(new PHUIButtonView()) - ->setTag('a') - ->setHref('#latest') - ->setText(pht('Jump to Bottom')) - ->setIcon('fa-arrow-circle-down'); - - $jump_target = phutil_tag( - 'div', - array( - 'id' => 'latest', - )); - - $content = phutil_tag( - 'div', - array( - 'class' => 'phabricator-chat-log-wrap', - ), - array( - $log, - $jump_target, - $pager_bottom, - )); - - $header = id(new PHUIHeaderView()) - ->setHeader($channel->getChannelName()) - ->setSubHeader($channel->getServiceName()) - ->addActionLink($jump_link); - - $box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->setCollapsed(true) - ->appendChild($content); - - $box->setShowHide( - pht('Search Dates'), - pht('Hide Dates'), - $form, - '#'); - - return $this->newPage() - ->setTitle(pht('Channel Log')) - ->setCrumbs($crumbs) - ->appendChild($box); - - } - - /** - * From request parameters, figure out where we should jump to in the log. - * We jump to either a date or log ID, but load a few lines of context before - * it so the user can see the nearby conversation. - */ - private function getPagingParameters( - AphrontRequest $request, - PhabricatorChatLogQuery $query) { - - $viewer = $request->getViewer(); - - $at_id = $request->getInt('at'); - $at_date = $request->getStr('date'); - - $context_log = null; - $map = array(); - - $query = clone $query; - $query->setLimit(8); - - if ($at_id) { - // Jump to the log in question, and load a few lines of context before - // it. - $context_logs = $query - ->setAfterID($at_id) - ->execute(); - - $context_log = last($context_logs); - - $map = array( - $at_id => true, - ); - - } else if ($at_date) { - $timestamp = PhabricatorTime::parseLocalTime($at_date, $viewer); - - if ($timestamp) { - $context_logs = $query - ->withMaximumEpoch($timestamp) - ->execute(); - - $context_log = last($context_logs); - - $target_log = head($context_logs); - if ($target_log) { - $map = array( - $target_log->getID() => true, - ); - } - } - } - - if ($context_log) { - $after = null; - $before = $context_log->getID() - 1; - } else { - $after = $request->getInt('after'); - $before = $request->getInt('before'); - } - - return array($after, $before, $map); - } - -} diff --git a/src/applications/chatlog/controller/PhabricatorChatLogController.php b/src/applications/chatlog/controller/PhabricatorChatLogController.php deleted file mode 100644 index 64fcbf20bb..0000000000 --- a/src/applications/chatlog/controller/PhabricatorChatLogController.php +++ /dev/null @@ -1,3 +0,0 @@ -channels = $channels; - return $this; - } - - public function withIDs(array $channel_ids) { - $this->channelIDs = $channel_ids; - return $this; - } - - protected function loadPage() { - $table = new PhabricatorChatLogChannel(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T c %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - $logs = $table->loadAllFromArray($data); - - return $logs; - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - $where[] = $this->buildPagingClause($conn); - - if ($this->channelIDs) { - $where[] = qsprintf( - $conn, - 'id IN (%Ld)', - $this->channelIDs); - - } - - if ($this->channels) { - $where[] = qsprintf( - $conn, - 'channelName IN (%Ls)', - $this->channels); - } - - return $this->formatWhereClause($conn, $where); - } - - public function getQueryApplicationClass() { - return 'PhabricatorChatLogApplication'; - } - -} diff --git a/src/applications/chatlog/query/PhabricatorChatLogQuery.php b/src/applications/chatlog/query/PhabricatorChatLogQuery.php deleted file mode 100644 index 88cf6da7e3..0000000000 --- a/src/applications/chatlog/query/PhabricatorChatLogQuery.php +++ /dev/null @@ -1,84 +0,0 @@ -channelIDs = $channel_ids; - return $this; - } - - public function withMaximumEpoch($epoch) { - $this->maximumEpoch = $epoch; - return $this; - } - - protected function loadPage() { - $table = new PhabricatorChatLogEvent(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T e %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - $logs = $table->loadAllFromArray($data); - - return $logs; - } - - protected function willFilterPage(array $events) { - $channel_ids = mpull($events, 'getChannelID', 'getChannelID'); - - $channels = id(new PhabricatorChatLogChannelQuery()) - ->setViewer($this->getViewer()) - ->withIDs($channel_ids) - ->execute(); - $channels = mpull($channels, null, 'getID'); - - foreach ($events as $key => $event) { - $channel = idx($channels, $event->getChannelID()); - if (!$channel) { - unset($events[$key]); - continue; - } - - $event->attachChannel($channel); - } - - return $events; - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - $where[] = $this->buildPagingClause($conn); - - if ($this->maximumEpoch !== null) { - $where[] = qsprintf( - $conn, - 'epoch <= %d', - $this->maximumEpoch); - } - - if ($this->channelIDs !== null) { - $where[] = qsprintf( - $conn, - 'channelID IN (%Ld)', - $this->channelIDs); - } - - return $this->formatWhereClause($conn, $where); - } - - public function getQueryApplicationClass() { - return 'PhabricatorChatLogApplication'; - } - -} diff --git a/src/applications/chatlog/storage/PhabricatorChatLogChannel.php b/src/applications/chatlog/storage/PhabricatorChatLogChannel.php deleted file mode 100644 index 2e62bcb6cf..0000000000 --- a/src/applications/chatlog/storage/PhabricatorChatLogChannel.php +++ /dev/null @@ -1,51 +0,0 @@ - array( - 'serviceName' => 'text64', - 'serviceType' => 'text32', - 'channelName' => 'text64', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_channel' => array( - 'columns' => array('channelName', 'serviceType', 'serviceName'), - 'unique' => true, - ), - ), - ) + parent::getConfiguration(); - } - - public function getCapabilities() { - return array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - ); - } - - public function getPolicy($capability) { - switch ($capability) { - case PhabricatorPolicyCapability::CAN_VIEW: - return $this->viewPolicy; - break; - case PhabricatorPolicyCapability::CAN_EDIT: - return $this->editPolicy; - break; - } - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return false; - } - -} diff --git a/src/applications/chatlog/storage/PhabricatorChatLogDAO.php b/src/applications/chatlog/storage/PhabricatorChatLogDAO.php deleted file mode 100644 index 8014168814..0000000000 --- a/src/applications/chatlog/storage/PhabricatorChatLogDAO.php +++ /dev/null @@ -1,9 +0,0 @@ - false, - self::CONFIG_COLUMN_SCHEMA => array( - 'author' => 'text64', - 'type' => 'text4', - 'message' => 'text', - ), - self::CONFIG_KEY_SCHEMA => array( - 'channel' => array( - 'columns' => array('epoch'), - ), - ), - ) + parent::getConfiguration(); - } - - public function attachChannel(PhabricatorChatLogChannel $channel) { - $this->channel = $channel; - return $this; - } - - public function getChannel() { - return $this->assertAttached($this->channel); - } - - -/* -( PhabricatorPolicyInterface )----------------------------------------- */ - - - public function getCapabilities() { - return array( - PhabricatorPolicyCapability::CAN_VIEW, - ); - } - - public function getPolicy($capability) { - return $this->getChannel()->getPolicy($capability); - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return $this->getChannel()->hasAutomaticCapability($capability, $viewer); - } - -} diff --git a/src/docs/book/phorge.book b/src/docs/book/phorge.book index 0be0d5526d..b421144f2e 100644 --- a/src/docs/book/phorge.book +++ b/src/docs/book/phorge.book @@ -57,10 +57,6 @@ "name": "Celerity", "include": "(^src/applications/celerity/)" }, - "chatlog": { - "name": "Chatlog", - "include": "(^src/applications/chatlog/)" - }, "conduit": { "name": "Conduit", "include": "(^src/applications/conduit/)" diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index f68635860a..d29e1bcc07 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -50,7 +50,6 @@ public function getOldPatches() { 'after' => array( /* First Patch */ ), ), 'db.calendar' => array(), - 'db.chatlog' => array(), 'db.conduit' => array(), 'db.countdown' => array(), 'db.daemon' => array(), diff --git a/webroot/rsrc/css/application/chatlog/chatlog.css b/webroot/rsrc/css/application/chatlog/chatlog.css deleted file mode 100644 index b0cb05a0e8..0000000000 --- a/webroot/rsrc/css/application/chatlog/chatlog.css +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @provides phabricator-chatlog-css - */ - -.device-phone .phabricator-chat-log-wrap { - padding: 0; -} - -.phabricator-chat-log-pager-bottom { - padding: 8px 4px 16px; - font-weight: bold; - float: right; -} - -.phabricator-chat-log-pager-bottom a { - padding: 2px 3px; -} - -.phabricator-chat-log-panel { - clear: both; -} - -.phabricator-chat-log { - width: 100%; -} - -.phabricator-chat-log td { - padding: 8px; - line-height: 18px; -} - -.phabricator-chat-log tr { - background: #fff; -} - -.phabricator-chat-log tr td.author { - background: {$greybackground}; -} - -.phabricator-chat-log tr.alternate { - border-top: 1px solid {$thinblueborder}; - border-bottom: 1px solid {$thinblueborder}; -} - -.phabricator-chat-log tr.alternate td.author { - background: {$lightgreybackground}; -} - -.phabricator-chat-log tr.highlight td { - background: {$lightyellow}; -} - -.phabricator-chat-log td.timestamp { - white-space: nowrap; - text-align: right; - width: 12em; -} - -.phabricator-chat-log td.message .timestamp { - color: {$bluetext}; - font-size: {$smallestfontsize}; - float: right; - margin-left: 5px; -} - -.phabricator-chat-log td.author { - white-space: nowrap; - text-align: right; - font-weight: bold; - width: 140px; - color: {$darkbluetext}; -} - -.device-phone .phabricator-chat-log td.author { - width: 80px; -} - -.phabricator-chat-log td.message { - white-space: pre-wrap; - word-break: break-word; -} From c3850a3c1530fa2d52c81ad35de4f0a6e12459a9 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 28 Nov 2023 13:46:37 +0100 Subject: [PATCH 349/425] Fix PHP 8.1 "strlen(null)" exception removing custom alt text from image file Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` ERROR 8192: strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/var/www/html/phorge/phorge/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php:59] ``` Closes T15678 Test Plan: Add and remove alt text on an image file; then go to `/feed/query/all/` Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15678 Differential Revision: https://we.phorge.it/D25481 --- .../files/xaction/PhabricatorFileAltTextTransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php b/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php index c3151910e4..063a599170 100644 --- a/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php +++ b/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php @@ -50,13 +50,13 @@ public function getTitleForFeed() { $old_value = $this->getOldValue(); $new_value = $this->getNewValue(); - if (!strlen($old_value)) { + if (!phutil_nonempty_string($old_value)) { return pht( '%s set the alternate text for %s to %s.', $this->renderAuthor(), $this->renderObject(), $this->renderNewValue()); - } else if (!strlen($new_value)) { + } else if (!phutil_nonempty_string($new_value)) { return pht( '%s removed the alternate text for %s (was %s).', $this->renderAuthor(), From 99ee9357eff29f450526607cabec9ec557cfdec3 Mon Sep 17 00:00:00 2001 From: Christopher Speck Date: Thu, 30 Nov 2023 20:15:50 -0500 Subject: [PATCH 350/425] Updates for Mercurial's HTTP protocol Summary: While testing https://secure.phabricator.com/D21864 I ran into some issues getting mercurial HTTP access working. Using wireshark I confirmed that my local mercurial 6.4 was not including command arguments as HTTP headers but in the querystring. I didn't dig too deep into understanding when/why this started happening. The protocol documents this in [[ https://repo.mercurial-scm.org/hg/file/tip/mercurial/helptext/internals/wireprotocol.txt | wireprotocol.txt ]]. >Command arguments can be sent multiple ways. The simplest is part of the URL query string using ``x-www-form-urlencoded`` encoding (see Python's ``urllib.urlencode()``. However, many servers impose length limitations on the URL. So this mechanism is typically only used if the server doesn't support other mechanisms. Based on that either the mercurial on the server is really old (it's 6.1.1 tho) or maybe some other parsing/info passing in Phab's handling of the wire protocol is causing the client to downgrade the wire protocol support. Cherry-picked from: https://secure.phabricator.com/D21867 https://secure.phabricator.com/rP0b6e758978a9691bd5ad25db4aa4c4301640a9a9 Test Plan: Host mercurial repo using HTTP, test push/pull. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25471 --- .../controller/DiffusionServeController.php | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index 93656ea607..d092f9740d 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -878,10 +878,29 @@ private function getMercurialArguments() { } $args_raw[] = $_SERVER[$header]; } - $args_raw = implode('', $args_raw); - return id(new PhutilQueryStringParser()) - ->parseQueryString($args_raw); + if ($args_raw) { + $args_raw = implode('', $args_raw); + return id(new PhutilQueryStringParser()) + ->parseQueryString($args_raw); + } + + // Sometimes arguments come in via the query string. Note that this will + // not handle multi-value entries e.g. "a[]=1,a[]=2" however it's unclear + // whether or how the mercurial protocol should handle this. + $query = idx($_SERVER, 'QUERY_STRING', ''); + $query_pairs = id(new PhutilQueryStringParser()) + ->parseQueryString($query); + foreach ($query_pairs as $key => $value) { + // Filter out private/internal keys as well as the command itself. + if (strncmp($key, '__', 2) && $key != 'cmd') { + $args_raw[$key] = $value; + } + } + + // TODO: Arguments can also come in via request body for POST requests. The + // body would be all arguments, url-encoded. + return $args_raw; } private function formatMercurialArguments($command, array $arguments) { From acedbd022d45e34bbb6abbe303f0b97dd6f36e4d Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 4 Dec 2023 19:31:39 -0800 Subject: [PATCH 351/425] Fix exception in Transaction Log after renaming Pholio Mock image: Call to undefined method PholioImageSequenceTransaction::renderHandleLink() Summary: Replace call to undefined `renderHandleLink()` with `renderHandle()`. Additionally, pass `head_key($new)` instead of `key($new)`. This might not be needed strictly speaking for this very issue but should not harm either as several images in a mock can be renamed at once. ``` EXCEPTION: (Error) Call to undefined method PholioImageSequenceTransaction::renderHandleLink() at [/src/applications/pholio/xaction/PholioImageSequenceTransaction.php:32] ``` Closes T15680 Test Plan: * Rename the title of an image in a Pholio mock. * Go to `/feed/transactions/query/all/` which now renders instead of showing an error, and shows two entries: * `user renamed an image (newname.jpg) from oldname.jpg to newname.jpg.` and `user updated an image's (newname.jpg) sequence.` (However, separate T15679 will still show up in the error log after this change.) Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15680 Differential Revision: https://we.phorge.it/D25482 --- .../pholio/xaction/PholioImageSequenceTransaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/pholio/xaction/PholioImageSequenceTransaction.php b/src/applications/pholio/xaction/PholioImageSequenceTransaction.php index c98c199adf..4d6099ed14 100644 --- a/src/applications/pholio/xaction/PholioImageSequenceTransaction.php +++ b/src/applications/pholio/xaction/PholioImageSequenceTransaction.php @@ -29,7 +29,7 @@ public function getTitle() { return pht( '%s updated an image\'s (%s) sequence.', $this->renderAuthor(), - $this->renderHandleLink(key($new))); + $this->renderHandle(head_key($new))); } public function getTitleForFeed() { From e610e739cb4294dcab92c3145285a5ffa5c3cf61 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 5 Dec 2023 10:23:43 -0800 Subject: [PATCH 352/425] Fix PHP 8.1 exceptions rendering task when custom select field configured Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. In the other case, do not call `json_decode()` when passing null to it. ``` EXCEPTION: (RuntimeException) json_decode(): Passing null to parameter #1 ($json) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> json_decode(NULL, boolean) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php:44] ``` ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php:76] ``` Closes T15683 Test Plan: After configuring a custom `select` field, access a task. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15683 Differential Revision: https://we.phorge.it/D25487 --- .../standard/PhabricatorStandardCustomFieldPHIDs.php | 2 +- .../standard/PhabricatorStandardCustomFieldSelect.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php index a96ebefda1..48d5ea725a 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php @@ -40,7 +40,7 @@ public function setValueFromStorage($value) { // TODO: Clean this up. $result = array(); - if (!is_array($value)) { + if (!is_array($value) && phutil_nonempty_string($value)) { $value = json_decode($value, true); if (is_array($value)) { $result = array_values($value); diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php index 5957afe56a..12e0aa6c3d 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php @@ -73,7 +73,7 @@ public function renderEditControl(array $handles) { } public function renderPropertyViewValue(array $handles) { - if (!strlen($this->getFieldValue())) { + if (!phutil_nonempty_string($this->getFieldValue())) { return null; } return idx($this->getOptions(), $this->getFieldValue()); From e59702569f8e7076952defa683452d293fa5d44a Mon Sep 17 00:00:00 2001 From: Waldir Pimenta Date: Wed, 6 Dec 2023 15:35:05 +0000 Subject: [PATCH 353/425] CSS adjustments to Conpherence Summary: **Fix Conpherence messages overlapping header in mobile mode** Before: {F413544} After: {F413546} **Make Conpherence input box be one line even in desktop mode** Before: {F413541} After: {F413540} This second change, in particular, is motivated by the fact that pressing `Enter` sends the message, whereas the multi-line box gives the impression that the `Enter` key would simply introduce a line break in the message. (That's still possible via `Shift`+`Enter`, btw.) Test Plan: - Visit a Conpherence room with some content in mobile mode; scroll down and notice that the messages no longer slightly overlap the header. - Visit a Conpherence room in desktop mode; verify that the input box now has the height of a single line. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Tags: #conpherence Differential Revision: https://we.phorge.it/D25485 --- resources/celerity/map.php | 6 +++--- .../rsrc/css/application/conpherence/message-pane.css | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 5fc371bcf0..d0bb7523a7 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'conpherence.pkg.css' => '76ed87e3', + 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', 'core.pkg.css' => 'c0bdb5b4', 'core.pkg.js' => '2eeda9e0', @@ -51,7 +51,7 @@ 'rsrc/css/application/conpherence/durable-column.css' => '2d57072b', 'rsrc/css/application/conpherence/header-pane.css' => 'c9a3db8e', 'rsrc/css/application/conpherence/menu.css' => '67f4680d', - 'rsrc/css/application/conpherence/message-pane.css' => 'd244db1e', + 'rsrc/css/application/conpherence/message-pane.css' => '50b1345e', 'rsrc/css/application/conpherence/notification.css' => '85c48def', 'rsrc/css/application/conpherence/participant-pane.css' => '69e0058a', 'rsrc/css/application/conpherence/transaction.css' => '3a3f5e7e', @@ -552,7 +552,7 @@ 'conpherence-durable-column-view' => '2d57072b', 'conpherence-header-pane-css' => 'c9a3db8e', 'conpherence-menu-css' => '67f4680d', - 'conpherence-message-pane-css' => 'd244db1e', + 'conpherence-message-pane-css' => '50b1345e', 'conpherence-notification-css' => '85c48def', 'conpherence-participant-pane-css' => '69e0058a', 'conpherence-thread-manager' => 'aec8e38c', diff --git a/webroot/rsrc/css/application/conpherence/message-pane.css b/webroot/rsrc/css/application/conpherence/message-pane.css index 0dc33789f3..87be9c5897 100644 --- a/webroot/rsrc/css/application/conpherence/message-pane.css +++ b/webroot/rsrc/css/application/conpherence/message-pane.css @@ -9,7 +9,7 @@ position: fixed; left: 240px; right: 240px; - top: 106px; + top: 115px; bottom: 0px; min-width: 300px; width: auto; @@ -55,8 +55,8 @@ position: fixed; left: 240px; right: 240px; - top: 106px; - bottom: 142px; + top: 115px; + bottom: 88px; overflow-x: hidden; overflow-y: auto; -webkit-overflow-scrolling: touch; @@ -95,7 +95,7 @@ .conpherence-message-pane .phui-form-view { border-width: 0; - height: 130px; + height: 76px; padding: 0 20px 12px; position: fixed; bottom: 0; @@ -347,7 +347,7 @@ body .conpherence-message-pane .aphront-form-control { } .conpherence-message-pane .remarkup-assist-textarea { - height: 88px; + height: 34px; padding: 8px; box-sizing: border-box; -moz-box-sizing: border-box; @@ -360,7 +360,6 @@ body .conpherence-message-pane .aphront-form-control { margin: 0; padding: 7px 8px 6px 38px; width: 100%; - height: 34px; resize: none; border-color: {$greyborder}; border-top-left-radius: 3px; From c8a927006067ea87f0ccb968006b162b98c5d0b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Dziewo=C5=84ski?= Date: Wed, 6 Dec 2023 16:50:35 +0100 Subject: [PATCH 354/425] Use user's preferred font for inline code snippets too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Use user's preferred font for inline code snippets, like we already do for block code snippets. Test Plan: Set a non-default font in Settings → Display Preferences → Monospaced Font. Make sure that it applies to both single-tick (inline) and triple-tick (block) code snippets in comments. For example, enter: 42px Tahoma Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15682 Differential Revision: https://we.phorge.it/D25486 --- src/view/page/PhabricatorStandardPageView.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index a81820de3c..005ede23a7 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -413,8 +413,9 @@ protected function getHead() { $font_css = hsprintf( '', $monospaced); } From 5ddca7da55e3f94c489f20f39b50a45d169fe6cb Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 6 Dec 2023 10:29:32 -0800 Subject: [PATCH 355/425] Fix PHP 8.1 "strlen(null)" exception rendering dashboard panel with latest tasks when custom text field configured Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php:33] ``` Closes T15684 Test Plan: After configuring a custom `text` field and a dashboard panel to query and listed the latest created tasks, access the panel. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15684 Differential Revision: https://we.phorge.it/D25488 --- .../standard/PhabricatorStandardCustomFieldText.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php index 56164bb7b5..758c50e817 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php @@ -11,7 +11,7 @@ public function buildFieldIndexes() { $indexes = array(); $value = $this->getFieldValue(); - if (strlen($value)) { + if (phutil_nonempty_string($value)) { $indexes[] = $this->newStringIndex($value); } @@ -30,7 +30,7 @@ public function applyApplicationSearchConstraintToQuery( PhabricatorCursorPagedPolicyAwareQuery $query, $value) { - if (strlen($value)) { + if (phutil_nonempty_string($value)) { $query->withApplicationSearchContainsConstraint( $this->newStringIndex(null), $value); From 6c8329fb661483b5178f66d293f605ffe1ea35fc Mon Sep 17 00:00:00 2001 From: Mukunda Modell Date: Tue, 5 Dec 2023 06:56:11 -0800 Subject: [PATCH 356/425] Include 'published' date of posts in Phame Atom feeds Summary: Metadata of Phame blog posts includes the 'updated' date of a blog post. Make them also include the original 'published' date. Patch written by @20after4 from: https://phabricator.wikimedia.org/rPHAB3de500bfc845759d6da82180df0adfc12f973463 Closes T15686 Test Plan: * Go to the Atom feed of blog 1 at `/phame/blog/feed/1/` and look at this ``'s source * Apply patch and do the same, see additional `` entry just like the `` * Edit the last blog post in blog 1, then check Atom feed and verify that the published and updated date stamp differ as expected Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, 20after4, Cigaryno Maniphest Tasks: T15686 Differential Revision: https://we.phorge.it/D25490 --- .../phame/controller/blog/PhameBlogFeedController.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/applications/phame/controller/blog/PhameBlogFeedController.php b/src/applications/phame/controller/blog/PhameBlogFeedController.php index c0c6e60cc4..1523a8d5e6 100644 --- a/src/applications/phame/controller/blog/PhameBlogFeedController.php +++ b/src/applications/phame/controller/blog/PhameBlogFeedController.php @@ -71,6 +71,11 @@ public function handleRequest(AphrontRequest $request) { '%s', $bloggers[$post->getBloggerPHID()]->getFullName()); + $content[] = phutil_tag( + 'published', + array(), + date('c', $post->getDatePublished())); + $content[] = phutil_tag( 'updated', array(), From c49eeb235ea0276dc34e9f87404b696642495f5a Mon Sep 17 00:00:00 2001 From: Waldir Pimenta Date: Thu, 7 Dec 2023 16:21:39 +0000 Subject: [PATCH 357/425] Improve command line prompts in setup issue pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: This is a follow-up to D25425, where these improvements to the CLI prompt markers were discussed. Changes included in this revision: - Build all prompts the same way - Remove space after the prompt marker (add it via CSS instead) - Add server path prefix - Make the prompt unselectable Test Plan: - Visit any of the setup issue pages, e.g. /config/issue/auth.config-unlocked/ (after ensuring that the corresponding issue is present — in this case, by doing `./bin/auth unlock`) - For example, Deactivate all PHP extensions to trigger each /config/issue/extension.gd/ etc. - For example, update at least up to `dc10a7e69ea3` to see the database upgrade tip etc. - Confirm that the command line prompts now include the path prefix - Confirm that selecting the command via double-click (or click-and-drag) does not select the prompt Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Tags: #ux, #config Differential Revision: https://we.phorge.it/D25466 --- resources/celerity/map.php | 4 ++-- .../config/check/PhabricatorAuthSetupCheck.php | 3 ++- .../config/check/PhabricatorBaseURISetupCheck.php | 7 +++---- .../config/check/PhabricatorDaemonsSetupCheck.php | 10 ++++++++-- .../check/PhabricatorDatabaseSetupCheck.php | 15 +++++++++++---- .../check/PhabricatorElasticsearchSetupCheck.php | 10 ++++++++-- .../check/PhabricatorExtraConfigSetupCheck.php | 14 ++++++++++---- .../config/view/PhabricatorSetupIssueView.php | 9 +++++---- .../examples/PhabricatorSetupIssueUIExample.php | 8 ++++++-- .../rsrc/css/application/config/setup-issue.css | 5 +++++ 10 files changed, 60 insertions(+), 25 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index d0bb7523a7..12ad67b011 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -45,7 +45,7 @@ 'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41', 'rsrc/css/application/config/config-options.css' => '16c920ae', 'rsrc/css/application/config/config-template.css' => 'e689dbbd', - 'rsrc/css/application/config/setup-issue.css' => '5eed85b2', + 'rsrc/css/application/config/setup-issue.css' => '93231115', 'rsrc/css/application/config/unhandled-exception.css' => '9ecfc00d', 'rsrc/css/application/conpherence/color.css' => 'b17746b0', 'rsrc/css/application/conpherence/durable-column.css' => '2d57072b', @@ -903,7 +903,7 @@ 'project-card-view-css' => 'c1200da7', 'project-triggers-css' => 'cd9c8bb9', 'project-view-css' => '2f7caa20', - 'setup-issue-css' => '5eed85b2', + 'setup-issue-css' => '93231115', 'sprite-login-css' => '07052ee0', 'sprite-tokens-css' => 'f1896dc5', 'syntax-default-css' => '055fc231', diff --git a/src/applications/config/check/PhabricatorAuthSetupCheck.php b/src/applications/config/check/PhabricatorAuthSetupCheck.php index b971404c38..8705047263 100644 --- a/src/applications/config/check/PhabricatorAuthSetupCheck.php +++ b/src/applications/config/check/PhabricatorAuthSetupCheck.php @@ -74,7 +74,8 @@ protected function executeChecks() { ->addRelatedPhabricatorConfig('auth.lock-config') ->addCommand( hsprintf( - '$ ./bin/auth lock')); + '%s $./bin/auth lock', + PlatformSymbols::getPlatformServerPath())); } } } diff --git a/src/applications/config/check/PhabricatorBaseURISetupCheck.php b/src/applications/config/check/PhabricatorBaseURISetupCheck.php index 92e46641d7..e73c1455bf 100644 --- a/src/applications/config/check/PhabricatorBaseURISetupCheck.php +++ b/src/applications/config/check/PhabricatorBaseURISetupCheck.php @@ -96,9 +96,8 @@ protected function executeChecks() { ->setMessage($message) ->addCommand( hsprintf( - '$ %s', - csprintf( - './bin/config set phabricator.base-uri %s', - $base_uri_guess))); + '%s $./bin/config set phabricator.base-uri %s', + PlatformSymbols::getPlatformServerPath(), + $base_uri_guess)); } } diff --git a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php index df5821665c..ceedd5137e 100644 --- a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php +++ b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php @@ -49,7 +49,10 @@ protected function executeChecks() { ->setName(pht('Daemons Are Not Running')) ->setSummary($summary) ->setMessage($message) - ->addCommand('$ ./bin/phd start'); + ->addCommand( + hsprintf( + '%s $./bin/phd start', + PlatformSymbols::getPlatformServerPath())); } $expect_user = PhabricatorEnv::getEnvConfig('phd.user'); @@ -90,7 +93,10 @@ protected function executeChecks() { ->setSummary($summary) ->setMessage($message) ->addPhabricatorConfig('phd.user') - ->addCommand('$ ./bin/phd restart'); + ->addCommand( + hsprintf( + '%s $./bin/phd restart', + PlatformSymbols::getPlatformServerPath())); break; } diff --git a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php index d3f6d52f04..99815d85e6 100644 --- a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php +++ b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php @@ -35,11 +35,13 @@ protected function executeChecks() { ->addPhabricatorConfig('mysql.port') ->addCommand( hsprintf( - '$ ./bin/config set mysql.host %s', + '%s $./bin/config set mysql.host %s', + PlatformSymbols::getPlatformServerPath(), $host)) ->addCommand( hsprintf( - '$ ./bin/config set mysql.port %s', + '%s $./bin/config set mysql.port %s', + PlatformSymbols::getPlatformServerPath(), $port)); } @@ -134,7 +136,10 @@ private function executeRefChecks(PhabricatorDatabaseRef $ref) { ->setName(pht('Setup MySQL Schema')) ->setMessage($message) ->setIsFatal(true) - ->addCommand(hsprintf('$ ./bin/storage upgrade')); + ->addCommand( + hsprintf( + '%s $./bin/storage upgrade', + PlatformSymbols::getPlatformServerPath())); return true; } @@ -160,7 +165,9 @@ private function executeRefChecks(PhabricatorDatabaseRef $ref) { ->setIsFatal(true) ->setMessage($message) ->addCommand( - hsprintf('$ ./bin/storage upgrade')); + hsprintf( + '%s $./bin/storage upgrade', + PlatformSymbols::getPlatformServerPath())); return true; } diff --git a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php index 8466c5a6c6..268f55f80b 100644 --- a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php +++ b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php @@ -60,7 +60,10 @@ protected function executeChecks() { $this ->newIssue('elastic.missing-index') ->setName(pht('Elasticsearch Index Not Found')) - ->addCommand('./bin/search init') + ->addCommand( + hsprintf( + '%s $./bin/search init', + PlatformSymbols::getPlatformServerPath())) ->setSummary($summary) ->setMessage($message); @@ -76,7 +79,10 @@ protected function executeChecks() { $this ->newIssue('elastic.broken-index') ->setName(pht('Elasticsearch Index Schema Mismatch')) - ->addCommand('./bin/search init') + ->addCommand( + hsprintf( + '%s $./bin/search init', + PlatformSymbols::getPlatformServerPath())) ->setSummary($summary) ->setMessage($message); } diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php index ae14a0ab0a..070127ccbd 100644 --- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php @@ -76,7 +76,10 @@ protected function executeChecks() { $issue->setMessage($message); if ($found_local) { - $command = csprintf('$ ./bin/config delete %s', $key); + $command = hsprintf( + '%s $./bin/config delete %s', + PlatformSymbols::getPlatformServerPath(), + $key); $issue->addCommand($command); } @@ -166,9 +169,12 @@ protected function executeChecks() { 'target' => '_blank', ), $doc_name)); - $command = csprintf( - '$ ./bin/config delete --database %R', - $key); + $command = hsprintf( + '%s $%s', + PlatformSymbols::getPlatformServerPath(), + csprintf( + './bin/config delete --database %R', + $key)); $this->newIssue('config.locked.'.$key) ->setShortName(pht('Deprecated Config Source')) diff --git a/src/applications/config/view/PhabricatorSetupIssueView.php b/src/applications/config/view/PhabricatorSetupIssueView.php index e07e33b3d0..9aff749bb2 100644 --- a/src/applications/config/view/PhabricatorSetupIssueView.php +++ b/src/applications/config/view/PhabricatorSetupIssueView.php @@ -83,11 +83,11 @@ public function render() { // TODO: We should do a better job of detecting how to install extensions // on the current system. $install_commands = hsprintf( - "\$ sudo apt-get install php-extname ". + "$sudo apt-get install php-extname ". "# Debian / Ubuntu\n". - "\$ sudo dnf install php-extname ". + "$sudo dnf install php-extname ". "# Red Hat / Derivatives\n". - "\$ sudo yum install php-extname ". + "$sudo yum install php-extname ". "# Older Red Hat versions"); $fallback_info = pht( @@ -286,7 +286,8 @@ private function renderPhabricatorConfig(array $configs, $related = false) { $update = array(); foreach ($configs as $key) { $update[] = hsprintf( - '$ ./bin/config set %s value', + '%s $./bin/config set %s value', + PlatformSymbols::getPlatformServerPath(), $key); } $update = phutil_tag('pre', array(), phutil_implode_html("\n", $update)); diff --git a/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php b/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php index d6386e59d4..4343bcb8cb 100644 --- a/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php +++ b/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php @@ -24,8 +24,12 @@ public function renderExample() { ->setSummary(pht('Summary')) ->setMessage(pht('Message')) ->setIssueKey('example.key') - ->addCommand('$ # Add Command') - ->addCommand(hsprintf('$ %s', '$ ls -1 > /dev/null')) + ->addCommand(hsprintf( + '%s $# Add Command', + PlatformSymbols::getPlatformServerPath())) + ->addCommand(hsprintf( + '%s $ls -1 > /dev/null', + PlatformSymbols::getPlatformServerPath())) ->addPHPConfig('php.config.example') ->addPhabricatorConfig('test.value') ->addPHPExtension('libexample'); diff --git a/webroot/rsrc/css/application/config/setup-issue.css b/webroot/rsrc/css/application/config/setup-issue.css index a80b02815c..4af5e9f417 100644 --- a/webroot/rsrc/css/application/config/setup-issue.css +++ b/webroot/rsrc/css/application/config/setup-issue.css @@ -122,6 +122,11 @@ padding: 12px 0; } +.setup-issue-config > pre > tt { + user-select: none; + margin-right: 0.5em; +} + .setup-issue-config + .setup-issue-config { padding-top: 0; } From 2ba2cbaf9bbf8f88e910d3da0a3cd643e4879e8a Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 8 Dec 2023 17:39:39 +0200 Subject: [PATCH 358/425] Show Deprecation Warnings as Setup Warnings Summary: Capture Deprecation Warnings, collect them into cache, and show them as a Setup Issue for admins to see and report back to us. This only captures a sample of the traces, so not to overwhelm users (and RAM. and us) with reports. Requires D25388. Refs T15554. Test Plan: Run some flows that are known to bring up Deprecation Warnings. See them as a Setup Issue! Click little triangles to see details. Reviewers: O1 Blessed Committers, Matthew Reviewed By: O1 Blessed Committers, Matthew Subscribers: revi, Sten, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15554 Differential Revision: https://we.phorge.it/D25440 --- src/__phutil_library_map__.php | 4 + .../check/PhorgeCodeWarningSetupCheck.php | 81 +++++++++++++++++++ .../PhabricatorSystemApplication.php | 1 + ...PhorgeSystemDeprecationWarningListener.php | 58 +++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 src/applications/config/check/PhorgeCodeWarningSetupCheck.php create mode 100644 src/applications/system/events/PhorgeSystemDeprecationWarningListener.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f2a39023e4..d22dc78970 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -5387,6 +5387,8 @@ 'PholioTransactionType' => 'applications/pholio/xaction/PholioTransactionType.php', 'PholioTransactionView' => 'applications/pholio/view/PholioTransactionView.php', 'PholioUploadedImageView' => 'applications/pholio/view/PholioUploadedImageView.php', + 'PhorgeCodeWarningSetupCheck' => 'applications/config/check/PhorgeCodeWarningSetupCheck.php', + 'PhorgeSystemDeprecationWarningListener' => 'applications/system/events/PhorgeSystemDeprecationWarningListener.php', 'PhortuneAccount' => 'applications/phortune/storage/PhortuneAccount.php', 'PhortuneAccountAddManagerController' => 'applications/phortune/controller/account/PhortuneAccountAddManagerController.php', 'PhortuneAccountBillingAddressTransaction' => 'applications/phortune/xaction/PhortuneAccountBillingAddressTransaction.php', @@ -12208,6 +12210,8 @@ 'PholioTransactionType' => 'PhabricatorModularTransactionType', 'PholioTransactionView' => 'PhabricatorApplicationTransactionView', 'PholioUploadedImageView' => 'AphrontView', + 'PhorgeCodeWarningSetupCheck' => 'PhabricatorSetupCheck', + 'PhorgeSystemDeprecationWarningListener' => 'PhabricatorEventListener', 'PhortuneAccount' => array( 'PhortuneDAO', 'PhabricatorApplicationTransactionInterface', diff --git a/src/applications/config/check/PhorgeCodeWarningSetupCheck.php b/src/applications/config/check/PhorgeCodeWarningSetupCheck.php new file mode 100644 index 0000000000..2aaf33ac25 --- /dev/null +++ b/src/applications/config/check/PhorgeCodeWarningSetupCheck.php @@ -0,0 +1,81 @@ +getWarnings(); + if (!$warnings) { + return; + } + + $link = phutil_tag( + 'a', + array('href' => 'https://we.phorge.it/w/docs/report-warnings/'), + pht('%s\'s home page', PlatformSymbols::getPlatformServerName())); + + $message = pht( + 'There is some deprecated code found in the %s code-base.'. + "\n\n". + "This isn't a problem yet, but it means that %s might stop working if ". + 'you upgrade PHP version.'. + "\n\n". + 'This page records a sample of the cases since last server restart. '. + "\n\n". + 'To solve this issue, either:'. + "\n\n". + '- Visit %s, file bug report with the information below, or'. + "\n". + '- Ignore this issue using the `Ignore` button below.'. + "\n\n", + PlatformSymbols::getPlatformServerName(), + PlatformSymbols::getPlatformServerName(), + $link); + $message = array($message); + + $message[] = pht('PHP version: %s', phpversion()); + $message[] = "\n\n"; + + $message[] = pht('Recorded items (sample):'); + $list = array(); + $warnings = array_reverse(isort($warnings, 'counter')); + foreach ($warnings as $key => $data) { + $summary = pht( + '%s, occurrences: %s', + $key, + $data['counter']); + + $trace = phutil_tag('tt', array(), + array($data['message'] , "\n", $data['trace'])); + + $list[] = phutil_tag( + 'li', + array(), + phutil_tag( + 'details', + array(), + array( + phutil_tag('summary', array(), $summary), + $trace, + ))); + } + $message[] = phutil_tag('ul', array(), $list); + + + $this->newIssue('deprecations') + ->setName(pht('Deprecated Code')) + ->setMessage($message) + ->setSummary(pht('There is some deprecated code found in the code-base.')) + ->addLink( + 'https://we.phorge.it/w/docs/report-warnings/', + 'More Details on the website'); + } + +} diff --git a/src/applications/system/application/PhabricatorSystemApplication.php b/src/applications/system/application/PhabricatorSystemApplication.php index 6184c0de24..f3abc54d14 100644 --- a/src/applications/system/application/PhabricatorSystemApplication.php +++ b/src/applications/system/application/PhabricatorSystemApplication.php @@ -17,6 +17,7 @@ public function isUnlisted() { public function getEventListeners() { return array( new PhabricatorSystemDebugUIEventListener(), + new PhorgeSystemDeprecationWarningListener(), ); } diff --git a/src/applications/system/events/PhorgeSystemDeprecationWarningListener.php b/src/applications/system/events/PhorgeSystemDeprecationWarningListener.php new file mode 100644 index 0000000000..147e84a584 --- /dev/null +++ b/src/applications/system/events/PhorgeSystemDeprecationWarningListener.php @@ -0,0 +1,58 @@ +getKey(self::CACHE_KEY); + + if (!$cache_entry) { + $cache_entry = array(); + } + + $trace_entry = idx($cache_entry, $trace_key); + + if ($trace_entry) { + $trace_entry['counter']++; + } else { + $trace_entry = array( + 'counter' => 1, + 'message' => $value, + 'trace' => PhutilErrorHandler::formatStacktrace($metadata['trace']), + ); + } + $cache_entry[$trace_key] = $trace_entry; + + $cache->setKey(self::CACHE_KEY , $cache_entry); + } + + public function getWarnings() { + $cache = PhabricatorCaches::getRuntimeCache(); + return $cache->getKey(self::CACHE_KEY); + } + +} From 2295bcda14e71948516752f8fbada6601b9f0bde Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sun, 10 Dec 2023 16:55:02 +0100 Subject: [PATCH 359/425] Curtain: add Hovercard support Summary: Before this change, the right navigation panel (Curtain) had info without Hovercard support. After this change, all fields, like Subscribers, Tags, Referenced Files etc. have Hovercards. If you meet an UX problem with a specific field with Hovercard, you can disable that specific Hovercard, calling PHUICurtainObjectRefView::setHovercarded(false). This is an example, now with Hovercard support: {F342785} Closes T15577 Test Plan: No nuclear implosions when, on desktop and with a mouse: Browse Maniphest Task and mouse hover Authored By, Assigned To, Subscribers, Referenced Files, ... Browse Phriction document and mouse Tags, Referenced Files, Subscribers, ... Browse single commit and mouse hover Referenced Files, Subscribers, ... Browse Ponder question and mouse hover Tags, Referenced Files, Subscribers, ... Browse Differential revision and mouse hover Tags, Referenced Files, Subscribers, ... Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15577 Differential Revision: https://we.phorge.it/D25424 --- src/view/phui/PHUICurtainObjectRefView.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/view/phui/PHUICurtainObjectRefView.php b/src/view/phui/PHUICurtainObjectRefView.php index e7ead114c7..855aa855c6 100644 --- a/src/view/phui/PHUICurtainObjectRefView.php +++ b/src/view/phui/PHUICurtainObjectRefView.php @@ -7,6 +7,7 @@ final class PHUICurtainObjectRefView private $epoch; private $highlighted; private $exiled; + private $hovercarded = true; private $exileNote = false; public function setHandle(PhabricatorObjectHandle $handle) { @@ -30,6 +31,11 @@ public function setExiled($is_exiled, $note = false) { return $this; } + public function setHovercarded($hovercarded) { + $this->hovercarded = $hovercarded; + return $this; + } + protected function getTagAttributes() { $classes = array(); $classes[] = 'phui-curtain-object-ref-view'; @@ -155,7 +161,11 @@ private function newTitle() { $handle = $this->handle; if ($handle) { - $title_view = $handle->renderLink(); + if ($this->hovercarded) { + $title_view = $handle->renderHovercardLink(); + } else { + $title_view = $handle->renderLink(); + } } return $title_view; From fe1122bd4d6d0587d7f4350cf35a62242785ea2e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 17 Dec 2023 11:41:42 +0100 Subject: [PATCH 360/425] Catch RuntimeException: mb_convert_encoding(): Illegal character encoding specified at PhabricatorTextDocumentEngine.php:73 Summary: When given `$encoding` is invalid, catch the exception to show a proper error message and make the server logs provide more hints. ``` EXCEPTION: (RuntimeException) mb_convert_encoding(): Illegal character encoding specified at [/src/error/PhutilErrorHandler.php:261] #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer, array) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> mb_convert_encoding(string, string, string) called at [/src/applications/files/document/PhabricatorTextDocumentEngine.php:73] ``` Closes T15624 Test Plan: Open a URL which passes a bogus encoding value as parameter, like `/source/somerepository/browse/master/README.md?as=source&encode=TROLOLOL` Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: Sten, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15624 Differential Revision: https://we.phorge.it/D25418 --- .../document/PhabricatorTextDocumentEngine.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/applications/files/document/PhabricatorTextDocumentEngine.php b/src/applications/files/document/PhabricatorTextDocumentEngine.php index 2fce98baf1..8f04a56751 100644 --- a/src/applications/files/document/PhabricatorTextDocumentEngine.php +++ b/src/applications/files/document/PhabricatorTextDocumentEngine.php @@ -70,10 +70,16 @@ protected function loadTextData(PhabricatorDocumentRef $ref) { $encoding = $this->getEncodingConfiguration(); if ($encoding !== null) { if (function_exists('mb_convert_encoding')) { - $content = mb_convert_encoding($content, 'UTF-8', $encoding); - $this->encodingMessage = pht( - 'This document was converted from %s to UTF8 for display.', - $encoding); + try { + $content = mb_convert_encoding($content, 'UTF-8', $encoding); + $this->encodingMessage = pht( + 'This document was converted from %s to UTF8 for display.', + $encoding); + } catch (Throwable $ex) { + $this->encodingMessage = pht( + 'Unable to convert from requested encoding %s to UTF8.', + $encoding); + } } else { $this->encodingMessage = pht( 'Unable to perform text encoding conversion: mbstring extension '. From be763d90cec3a283771375828ddd617135dc51b1 Mon Sep 17 00:00:00 2001 From: Kuba Orlik Date: Sat, 16 Dec 2023 16:02:19 +0100 Subject: [PATCH 361/425] Fix typo Summary: This typo appeared in the docs and I thought I'd fix it Test Plan: See that the typo at src/applications/config/option/PhabricatorCoreConfigOptions.php is gone Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25496 --- src/applications/config/option/PhabricatorCoreConfigOptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/config/option/PhabricatorCoreConfigOptions.php b/src/applications/config/option/PhabricatorCoreConfigOptions.php index b09c12e269..46001f2363 100644 --- a/src/applications/config/option/PhabricatorCoreConfigOptions.php +++ b/src/applications/config/option/PhabricatorCoreConfigOptions.php @@ -200,7 +200,7 @@ public function getOptions() { '$PATH')) ->setDescription( pht( - "Thhi software sometimes executes other binaries on the ". + "This software sometimes executes other binaries on the ". "server. An example of this is the `%s` command, used to ". "syntax-highlight code written in languages other than PHP. By ". "default, it is assumed that these binaries are in the %s of the ". From bd44ec833f439f5d7931e933584f11d7694662ac Mon Sep 17 00:00:00 2001 From: Kuba Orlik Date: Mon, 18 Dec 2023 10:54:51 +0100 Subject: [PATCH 362/425] Add example systemd unit for sshd in Diffusion docs Summary: The docs don't specify how to make the second sshd daemon start on every reboot. I've added a unit that I use in my setup to the docs. Ref T15467 Test Plan: Check that the docs look alright. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15467 Differential Revision: https://we.phorge.it/D25497 --- .../user/userguide/diffusion_hosting.diviner | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/docs/user/userguide/diffusion_hosting.diviner b/src/docs/user/userguide/diffusion_hosting.diviner index 0d84859082..0618ff97a5 100644 --- a/src/docs/user/userguide/diffusion_hosting.diviner +++ b/src/docs/user/userguide/diffusion_hosting.diviner @@ -585,6 +585,32 @@ Very carefully, restart `sshd`. Verify that you can connect on the new port: Now you can move the Phorge `sshd` to port 22, then adjust the value for `diffusion.ssh-port` in your Phorge configuration. +You can set up and enable this systemd unit to start the second sshd +daemon on every reboot: + +``` +name=/etc/systemd/system/phorge-ssh.service,lang=ini +[Unit] +Description=Phorge sshd +Documentation=https://we.phorge.it/book/phorge/article/diffusion_hosting/#sshd-setup +After=network.target auditd.service + +[Service] +ExecStartPre=/usr/sbin/sshd -t -f /path/to/config_file.edited +ExecStart=/usr/sbin/sshd -f /path/to/config_file.edited +ExecReload=/usr/sbin/sshd -t -f /path/to/config_file.edited +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +Restart=on-failure +RestartPreventExitStatus=255 +Type=notify +RuntimeDirectory=sshd +RuntimeDirectoryMode=0755 + +[Install] +WantedBy=multi-user.target +Alias=phorge-sshd.service +``` No Direct Pushes ================ From 2d997c3f107efe78707404e6e7787f9eb1df54f4 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 18 Dec 2023 12:45:13 +0100 Subject: [PATCH 363/425] Avoid "Undefined index" error in project.search API when project_customfieldstorage table has additional fields not requested Summary: Do not trigger an "Undefined index" error in PhabricatorCustomFieldStorage when calling the `project.search` Conduit API without passing search parameters when the local project_customfieldstorage table has additional fields configured not requested in the API call. Closes T15688 Test Plan: See above; `curl -k -s -X GET http://phorge.localhost/api/project.search?api.token=api-xxxxxxxxxxxx | jq -r` for comparison Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: 20after4, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15688 Differential Revision: https://we.phorge.it/D25493 --- .../customfield/storage/PhabricatorCustomFieldStorage.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php b/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php index 0f60d77602..cf0140a38a 100644 --- a/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php +++ b/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php @@ -80,6 +80,10 @@ final public function loadStorageSourceData(array $fields) { $object_phid = $row['objectPHID']; $value = $row['fieldValue']; + if (!isset($map[$index]) || !isset($map[$index][$object_phid])) { + continue; + } + $key = $map[$index][$object_phid]; $result[$key] = $value; } From 428f9686c4171912ee186ebd919640a7427da768 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 27 Dec 2023 17:38:40 +0100 Subject: [PATCH 364/425] Composing a custom project picture: Remove misaligned empty icon Summary: Do not assume that all items under `/resources/builtin/projects/` are image files (some can be subdirectories). Only add PNG files to the map. Could be nicer to use `is_file()` or `is_dir()` but we are dealing only with string representations of files and folders. Closes T15696 Test Plan: Go to `/project/picture/1/` and click the `Choose Icon and Color...` button; check last icon shown under "Choose Icon". Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15696 Differential Revision: https://we.phorge.it/D25499 --- .../PhabricatorFilesComposeIconBuiltinFile.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php b/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php index 0bb56490cc..ac16ba7f6b 100644 --- a/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php +++ b/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php @@ -51,12 +51,14 @@ public static function getAllIcons() { $map = array(); $list = Filesystem::listDirectory($root, $include_hidden = false); foreach ($list as $file) { - $short = preg_replace('/\.png$/', '', $file); - - $map[$short] = array( - 'path' => $root.$file, - 'quip' => idx($quips, $short, $short), - ); + $count = 0; + $short = preg_replace('/\.png$/', '', $file, -1, $count); + if ($count === 1) { + $map[$short] = array( + 'path' => $root.$file, + 'quip' => idx($quips, $short, $short), + ); + } } return $map; From d92ed9ce0c0c00c9164cfadb6c024e7ea67fd084 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 4 Jan 2024 12:59:10 +0100 Subject: [PATCH 365/425] Hide "Packages" field in Diffusion Commits query form when application uninstalled Summary: Fixes T15698 Test Plan: 1. Uninstall Packages via `/applications/view/PhabricatorPackagesApplication/` 2. Go to `/diffusion/commit/`, expand the Query form, and see that `Packages` is not shown anymore Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15698 Differential Revision: https://we.phorge.it/D25506 --- src/applications/audit/query/PhabricatorCommitSearchEngine.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index 8b7751c795..7251857c7f 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -70,6 +70,8 @@ protected function buildQueryFromParameters(array $map) { } protected function buildCustomSearchFields() { + $show_packages = PhabricatorApplication::isClassInstalled( + 'PhabricatorPackagesApplication'); return array( id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Responsible Users')) @@ -118,6 +120,7 @@ protected function buildCustomSearchFields() { ->setConduitKey('packages') ->setAliases(array('package', 'packages', 'packagePHID')) ->setDatasource(new PhabricatorOwnersPackageDatasource()) + ->setIsHidden(!$show_packages) ->setDescription( pht('Find commits which affect given packages.')), id(new PhabricatorSearchThreeStateField()) From 6fe942a8f9f6b34fdac4cec274cc3ace2d0cee2c Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 8 Jan 2024 19:07:34 +0100 Subject: [PATCH 366/425] Add support for FIDO2-backed SSH keys Summary: U2F/FIDO are open standards for inexpensive two-factor authentication hardware that are widely used for website authentication. These keys are available since OpenSSH 8.2 (2020-02-14). https://www.openssh.com/txt/release-8.2 https://security.stackexchange.com/q/240991/260234 Ref Q96 Test Plan: Check the added keys. They exactly match the output of the command 'ssh -Q key'. Reviewers: O1 Blessed Committers, antonia, avivey Reviewed By: O1 Blessed Committers, antonia, avivey Subscribers: avivey, tobiaswiese, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25508 --- src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php b/src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php index d8660b1956..682cde0df6 100644 --- a/src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php +++ b/src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php @@ -55,11 +55,15 @@ public static function newFromRawKey($entire_key) { list($type, $body, $comment) = $parts; + // The only goal is to prevent user error by nonsense input. + // This is just a meaningful subset from 'ssh -Q key'. $recognized_keys = array( 'ssh-dsa', 'ssh-dss', 'ssh-rsa', 'ssh-ed25519', + 'sk-ssh-ed25519@openssh.com', + 'sk-ecdsa-sha2-nistp256@openssh.com', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', From 49c3fe619342c0fae4781f05f822d91ea621ac39 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 11 Jan 2024 11:57:54 +0100 Subject: [PATCH 367/425] Fix invalid CSS property value Summary: `x` is not a valid unit. Test Plan: Open a Maniphest task in the web browser; inspect the CSS of the label of any items in the "Add Action" dropdown. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25510 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/phui/phui-comment-form.css | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 12ad67b011..3c16116f4c 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'c0bdb5b4', + 'core.pkg.css' => '9f620ef6', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '2431def2', @@ -143,7 +143,7 @@ 'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30', 'rsrc/css/phui/phui-chart.css' => 'fe8f87a7', 'rsrc/css/phui/phui-cms.css' => '8c05c41e', - 'rsrc/css/phui/phui-comment-form.css' => '68a2d99a', + 'rsrc/css/phui/phui-comment-form.css' => '9feec837', 'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0', 'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf', 'rsrc/css/phui/phui-curtain-object-ref-view.css' => '51d93266', @@ -835,7 +835,7 @@ 'phui-calendar-month-css' => 'cb758c42', 'phui-chart-css' => 'fe8f87a7', 'phui-cms-css' => '8c05c41e', - 'phui-comment-form-css' => '68a2d99a', + 'phui-comment-form-css' => '9feec837', 'phui-comment-panel-css' => 'ec4e31c0', 'phui-crumbs-view-css' => '614f43cf', 'phui-curtain-object-ref-view-css' => '51d93266', diff --git a/webroot/rsrc/css/phui/phui-comment-form.css b/webroot/rsrc/css/phui/phui-comment-form.css index dcf45edb76..38c444ae06 100644 --- a/webroot/rsrc/css/phui/phui-comment-form.css +++ b/webroot/rsrc/css/phui/phui-comment-form.css @@ -155,7 +155,7 @@ body.device .phui-box.phui-object-box.phui-comment-form-view { .phui-comment-form-view .phui-form-view label.aphront-form-label, .phui-comment-form-view .phui-form-view .aphront-form-error { - height: 28x; + height: 28px; line-height: 28px; padding: 0; } From 40d10fbe1ae6cc5c37f78f5a9ad84a02b628f2d6 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 19 Dec 2023 00:12:45 +0100 Subject: [PATCH 368/425] Fix PHP 8.1 "strlen(null)" exception when custom select field configured Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` ERROR 8192: strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/var/www/html/phorge/phorge/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php:484] ``` Closes T15687 Test Plan: Unknown. Definitely requires having custom fields defined, then playing with creating tasks using forms which expose these fields and going to `/maniphest/query/all/`. See also D25487. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15687 Differential Revision: https://we.phorge.it/D25492 --- .../customfield/standard/PhabricatorStandardCustomField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php index 0c4ea452a2..87cde00801 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -481,7 +481,7 @@ public function updateAbstractDocument( } $field_value = $this->getFieldValue(); - if (strlen($field_value)) { + if (($field_value !== null) && (strlen($field_value))) { $document->addField($field_key, $field_value); } } From 8e09dbfee3436cfa77dd304d060b017cc9e399ea Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 19 Dec 2023 00:22:09 +0100 Subject: [PATCH 369/425] Fix PHP 8.1 exception creating task when custom select field configured Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` EXCEPTION: (RuntimeException) strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php:14] ``` Closes T15691 Test Plan: After configuring a custom `select` field, try to create a task via a Maniphest form using that custom field. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15691 Differential Revision: https://we.phorge.it/D25494 --- .../standard/PhabricatorStandardCustomFieldSelect.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php index 12e0aa6c3d..958d65d7e7 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php @@ -11,7 +11,7 @@ public function buildFieldIndexes() { $indexes = array(); $value = $this->getFieldValue(); - if (strlen($value)) { + if (($value !== null) && (strlen($value))) { $indexes[] = $this->newStringIndex($value); } From 549a26d0879c514c410c3322f8dd0355126bf153 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 12 Jan 2024 22:32:59 +0100 Subject: [PATCH 370/425] Auth SSH Details: show Public Key (not just Type) Summary: At the moment the "SSH Key Details" page lists only two minimal information: - Key Type (Example: 'ssh-rsa') - Created (Example: 'Wed, Dec 13, 5:13 PM') After this change it shows also the original Public Key. It becomes something like: - Public Key (Example: 'ssh-rsa AAAB3....... mariana@localhost') - Created (Example: 'Wed, Dec 13, 5:13 PM') This is just a minimal UX change. Note that you can already see the Public Key by clicking "Edit". So, nothing new is shown. Context: Premising that Phorge encourages to Revoke your decommissioned / deprecated / compromised SSH keys with a very lovely "Revoke" Workflow... Root problem: If you Revoke one of your keys, and if you then want to double-check that SSH Public Key again (if you want to see its original body), you then discover that you cannot see it anymore. In fact, the only way is the Edit button, but Editing is disabled for any Revoked Key. In this kind of situation, it's not nice to have to involve your favorite database administrator, to inspect the 'phabricator_auth.auth_sshkey' database table, to just be able to see your SSH Public Key again. After this change, you can easily self-audit your Revoked SSH Public Keys, and your beloved DBA has extra free time. Win-win situation. Closes T15692 Test Plan: - visit your SSH Keys, open one, and check the new Details with your big eyes - do the dame while juggling two ping pong balls (is anybody reading this?) - do the same on a Revoked key Reviewers: O1 Blessed Committers, aklapper Reviewed By: O1 Blessed Committers, aklapper Subscribers: aklapper, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15692 Differential Revision: https://we.phorge.it/D25495 --- .../auth/controller/PhabricatorAuthSSHKeyViewController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/controller/PhabricatorAuthSSHKeyViewController.php b/src/applications/auth/controller/PhabricatorAuthSSHKeyViewController.php index 54c6e3861b..591b835d62 100644 --- a/src/applications/auth/controller/PhabricatorAuthSSHKeyViewController.php +++ b/src/applications/auth/controller/PhabricatorAuthSSHKeyViewController.php @@ -110,7 +110,7 @@ private function buildPropertySection( $properties = id(new PHUIPropertyListView()) ->setUser($viewer); - $properties->addProperty(pht('SSH Key Type'), $ssh_key->getKeyType()); + $properties->addProperty(pht('Public Key'), $ssh_key->getEntireKey()); $properties->addProperty( pht('Created'), phabricator_datetime($ssh_key->getDateCreated(), $viewer)); From cc964550f945b08c64970c01a34b55314e8ef0aa Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 11 Jan 2024 19:16:31 +0100 Subject: [PATCH 371/425] Ellipsize long labels in Add Action area to avoid text overlap Summary: Add CSS to ellipsize long labels and disable text wrapping Closes T15707 Test Plan: Set a long custom "action" label in `/config/edit/maniphest.points/`; go to a Maniphest task; open all items in the "Add Action..." dropdown. Example config: ``` { "enabled": true, "label": "Points", "action": "Set Points Yeah Points Points Much Points" } ``` Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15707 Differential Revision: https://we.phorge.it/D25511 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/phui/phui-comment-form.css | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 3c16116f4c..18806d504b 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '9f620ef6', + 'core.pkg.css' => '5e7dbeb9', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '2431def2', @@ -143,7 +143,7 @@ 'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30', 'rsrc/css/phui/phui-chart.css' => 'fe8f87a7', 'rsrc/css/phui/phui-cms.css' => '8c05c41e', - 'rsrc/css/phui/phui-comment-form.css' => '9feec837', + 'rsrc/css/phui/phui-comment-form.css' => '3c6679a3', 'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0', 'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf', 'rsrc/css/phui/phui-curtain-object-ref-view.css' => '51d93266', @@ -835,7 +835,7 @@ 'phui-calendar-month-css' => 'cb758c42', 'phui-chart-css' => 'fe8f87a7', 'phui-cms-css' => '8c05c41e', - 'phui-comment-form-css' => '9feec837', + 'phui-comment-form-css' => '3c6679a3', 'phui-comment-panel-css' => 'ec4e31c0', 'phui-crumbs-view-css' => '614f43cf', 'phui-curtain-object-ref-view-css' => '51d93266', diff --git a/webroot/rsrc/css/phui/phui-comment-form.css b/webroot/rsrc/css/phui/phui-comment-form.css index 38c444ae06..4ace4759c3 100644 --- a/webroot/rsrc/css/phui/phui-comment-form.css +++ b/webroot/rsrc/css/phui/phui-comment-form.css @@ -158,6 +158,9 @@ body.device .phui-box.phui-object-box.phui-comment-form-view { height: 28px; line-height: 28px; padding: 0; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; } .phui-comment-form-view .aphront-form-error .phui-icon-view { From 5de10185ce5b7611d6c27b0c0d16ff92f8a5cff9 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sun, 14 Jan 2024 15:39:07 +0100 Subject: [PATCH 372/425] Config User Extra Fields: link to the doc (like Maniphest) Summary: Add a documentation link in a specific Config page, that is: Config > Setting > user.custom-field-definitions Test Plan: Visit the page and click on the new link: - /config/edit/user.custom-field-definitions/ Visit this page that was the inspirational page: - /config/edit/maniphest.custom-field-definitions/ No nuclear implosions. Reviewers: O1 Blessed Committers, aklapper Reviewed By: O1 Blessed Committers, aklapper Subscribers: aklapper, tobiaswiese, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25507 --- .../people/config/PhabricatorUserConfigOptions.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/applications/people/config/PhabricatorUserConfigOptions.php b/src/applications/people/config/PhabricatorUserConfigOptions.php index 3ef736c8ca..66b85a6507 100644 --- a/src/applications/people/config/PhabricatorUserConfigOptions.php +++ b/src/applications/people/config/PhabricatorUserConfigOptions.php @@ -39,12 +39,22 @@ public function getOptions() { $custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType'; + $fields_description = $this->deformat(pht(<<newOption('user.fields', $custom_field_type, $default) ->setCustomData(id(new PhabricatorUser())->getCustomFieldBaseClass()) ->setDescription(pht('Select and reorder user profile fields.')), $this->newOption('user.custom-field-definitions', 'wild', array()) - ->setDescription(pht('Add new simple fields to user profiles.')), + ->setDescription($fields_description), $this->newOption('user.require-real-name', 'bool', true) ->setDescription(pht('Always require real name for user profiles.')) ->setBoolOptions( From 7702772b2b86d833414a884d459ecd90ff9d2f05 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 17 Jan 2024 16:13:11 +0100 Subject: [PATCH 373/425] preg_match() null exception setting custom user profile image with empty files.viewable-mime-types Summary: When `files.viewable-mime-types` is not set, `getViewableMimeType()` passes `null` to `preg_match()` which is deprecated behavior since PHP 8.1. Only call `preg_match()` when there are some MIME types to compare. ``` ERROR 8192: preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated at [/var/www/html/phorge/phorge/src/applications/files/storage/PhabricatorFile.php:974] ``` Closes T15710 Test Plan: Go to a user profile and try to upload a custom profile picture in BMP format. Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15710 Differential Revision: https://we.phorge.it/D25516 --- src/applications/files/storage/PhabricatorFile.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index 6782a5a90e..53af38a33b 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -971,10 +971,13 @@ public function isTransformableImage() { // warns you if you don't have complete support. $matches = null; - $ok = preg_match( - '@^image/(gif|png|jpe?g)@', - $this->getViewableMimeType(), - $matches); + $ok = false; + if ($this->getViewableMimeType() !== null) { + $ok = preg_match( + '@^image/(gif|png|jpe?g)@', + $this->getViewableMimeType(), + $matches); + } if (!$ok) { return false; } From 2f1e271c5fd7017168ec65f86bbef46075ec7558 Mon Sep 17 00:00:00 2001 From: Benjamin Kausch Date: Mon, 22 Jan 2024 09:14:47 +0100 Subject: [PATCH 374/425] Remove bottom margin from embedded remarkup images Summary: {T15473} created a very small but annoying regression. Every embedded images has now an margin at the bottom: {F718091} I don't think this is intentional. If you click on the margin the image itself is opened not the lightbox. This revision removes the margin. Test Plan: Look at tasks with embedded images and see if there is still an margin. Reviewers: O1 Blessed Committers, valerio.bozzolan, aklapper Reviewed By: O1 Blessed Committers, valerio.bozzolan, aklapper Subscribers: aklapper, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25522 --- resources/celerity/map.php | 10 +++++----- webroot/rsrc/css/core/remarkup.css | 8 ++++++++ webroot/rsrc/css/phui/phui-property-list-view.css | 7 ++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 18806d504b..43b5f64503 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '5e7dbeb9', + 'core.pkg.css' => '3b2df0d0', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '2431def2', @@ -109,7 +109,7 @@ 'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd', 'rsrc/css/application/uiexample/example.css' => 'b4795059', 'rsrc/css/core/core.css' => 'b3a5928e', - 'rsrc/css/core/remarkup.css' => '3480e1fe', + 'rsrc/css/core/remarkup.css' => 'b251d2e7', 'rsrc/css/core/syntax.css' => '548567f6', 'rsrc/css/core/z-index.css' => 'ac3bfcd4', 'rsrc/css/diviner/diviner-shared.css' => '4bd263b0', @@ -171,7 +171,7 @@ 'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', 'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64', - 'rsrc/css/phui/phui-property-list-view.css' => '118db252', + 'rsrc/css/phui/phui-property-list-view.css' => '9a155095', 'rsrc/css/phui/phui-remarkup-preview.css' => '91767007', 'rsrc/css/phui/phui-segment-bar-view.css' => '5166b370', 'rsrc/css/phui/phui-spacing.css' => 'b05cadc3', @@ -798,7 +798,7 @@ 'phabricator-object-selector-css' => 'ee77366f', 'phabricator-phtize' => '2f1db1ed', 'phabricator-prefab' => '5793d835', - 'phabricator-remarkup-css' => '3480e1fe', + 'phabricator-remarkup-css' => 'b251d2e7', 'phabricator-remarkup-metadata' => 'e40c4991', 'phabricator-search-results-css' => '9ea70ace', 'phabricator-shaped-request' => '995f5102', @@ -873,7 +873,7 @@ 'phui-pager-css' => 'd022c7ad', 'phui-pinboard-view-css' => '1f08f5d8', 'phui-policy-section-view-css' => '139fdc64', - 'phui-property-list-view-css' => '118db252', + 'phui-property-list-view-css' => '9a155095', 'phui-remarkup-preview-css' => '91767007', 'phui-segment-bar-view-css' => '5166b370', 'phui-spacing-css' => 'b05cadc3', diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css index 7f1c51cac9..76554b56b4 100644 --- a/webroot/rsrc/css/core/remarkup.css +++ b/webroot/rsrc/css/core/remarkup.css @@ -474,6 +474,14 @@ video.phabricator-media { box-shadow: 1px 1px 2px rgba({$alphablack}, 0.20); } +.phabricator-remarkup-embed-image > img { + background: url('/rsrc/image/checker_light.png'); +} + +.device-desktop .phabricator-remarkup-embed-image > img:hover { + background: url('/rsrc/image/checker_dark.png'); +} + .phabricator-remarkup-embed-image-full, .phabricator-remarkup-embed-image-wide { display: inline-block; diff --git a/webroot/rsrc/css/phui/phui-property-list-view.css b/webroot/rsrc/css/phui/phui-property-list-view.css index 93388c3d7c..7a5512e6f2 100644 --- a/webroot/rsrc/css/phui/phui-property-list-view.css +++ b/webroot/rsrc/css/phui/phui-property-list-view.css @@ -203,16 +203,13 @@ div.phui-property-list-stacked .phui-property-list-properties border-bottom: 1px solid {$blueborder}; } - -.document-engine-image img, -.phabricator-remarkup-embed-image img { +.document-engine-image img { margin: 20px auto; background: url('/rsrc/image/checker_light.png'); max-width: 100%; } -.device-desktop .document-engine-image img:hover, -.device-desktop .phabricator-remarkup-embed-image img:hover { +.device-desktop .document-engine-image img:hover { background: url('/rsrc/image/checker_dark.png'); } From b035743ce38fbcf8e1a23508197cde150283bb6f Mon Sep 17 00:00:00 2001 From: Benjamin Kausch Date: Mon, 22 Jan 2024 17:33:49 +0100 Subject: [PATCH 375/425] Show title entirely and cut of secondary information gracefully in typeahead results of main search Summary: This revision increases the usability of the typeahead results of the main search (on the right in the top bar). The result texts wont't overflow in a reasonable way. | Before | After | |-----------|-----------| | {F715296} | {F715385} | The result title (red box) will now break so the user can read and understand it. The result type (blue box) will be cut off inside the padding with an ellipsis. See T15715 Test Plan: Check it out and use the main search in the top bar to create typeahead result with very long titles ;) Nothing changes for short titles. Reviewers: O1 Blessed Committers, valerio.bozzolan, avivey Reviewed By: O1 Blessed Committers, valerio.bozzolan, avivey Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15715 Differential Revision: https://we.phorge.it/D25520 --- resources/celerity/map.php | 12 ++++++------ webroot/rsrc/css/application/base/main-menu-view.css | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 43b5f64503..62ca4bb6a7 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '3b2df0d0', + 'core.pkg.css' => '4e0e22fb', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '2431def2', @@ -38,7 +38,7 @@ 'rsrc/css/aphront/typeahead.css' => '8779483d', 'rsrc/css/application/almanac/almanac.css' => '2e050f4f', 'rsrc/css/application/auth/auth.css' => 'c2f23d74', - 'rsrc/css/application/base/main-menu-view.css' => '5d673247', + 'rsrc/css/application/base/main-menu-view.css' => '33820efe', 'rsrc/css/application/base/notification-menu.css' => '4df1ee30', 'rsrc/css/application/base/phui-theme.css' => '35883b37', 'rsrc/css/application/base/standard-page-view.css' => 'e08c7462', @@ -790,7 +790,7 @@ 'phabricator-flag-css' => '2b77be8d', 'phabricator-keyboard-shortcut' => '1a844c06', 'phabricator-keyboard-shortcut-manager' => '81debc48', - 'phabricator-main-menu-view' => '5d673247', + 'phabricator-main-menu-view' => '33820efe', 'phabricator-nav-view-css' => '423f92cc', 'phabricator-notification' => 'a9b91e3f', 'phabricator-notification-css' => '30240bd2', @@ -1211,6 +1211,9 @@ 'javelin-dom', 'phabricator-notification', ), + '33820efe' => array( + 'phui-theme-css', + ), 34450586 => array( 'javelin-color', 'javelin-install', @@ -1499,9 +1502,6 @@ 'javelin-dom', 'phuix-dropdown-menu', ), - '5d673247' => array( - 'phui-theme-css', - ), '5d83623b' => array( 'javelin-dom', ), diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index 6aace18b44..c3e8e4267d 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -302,6 +302,8 @@ a.phabricator-core-user-menu .caret:before { background-size: 30px 30px; background-repeat: no-repeat; position: relative; + overflow: hidden; + text-overflow: ellipsis; } .phabricator-main-search-typeahead-result .result-name { @@ -309,6 +311,7 @@ a.phabricator-core-user-menu .caret:before { font-size: {$normalfontsize}; font-weight: bold; color: {$darkgreytext}; + white-space: normal; } .phabricator-main-search-typeahead-result.result-closed { From 7a86040f8a46e9f04466186db8a2c9fed792edc3 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 13 Jan 2024 21:54:05 +0100 Subject: [PATCH 376/425] Avoid exception setting project profile image when GD not installed Summary: When trying to set a custom project profile image while the PHP GD extension is not installed, use the same logic which already exists in `PhabricatorFilesComposeAvatarBuiltinFile.php` to set the default project image. This stills display an unhelpful error message `This server only supports these image formats: .` but avoids an exception trying to call GD's `imagecreatefromstring()`. ``` EXCEPTION: (Error) Call to undefined function imagecreatefromstring() at [/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php:131] ``` Closes T15326 Test Plan: 1. Remove the php-gd (and potentially gd) packages on your system; restart httpd 2. Go to http://phorge.localhost/project/manage/1/ 3. Select `Edit Picture` in the sidebar on the right to go to the `Edit Project Picture` at http://phorge.localhost/project/picture/1/ 4. Set a custom icon and color and click the `Save Image` button 5. Get `This server only supports these image formats: .` but no exception anymore Reviewers: O1 Blessed Committers, avivey, valerio.bozzolan Reviewed By: O1 Blessed Committers, avivey, valerio.bozzolan Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15326 Differential Revision: https://we.phorge.it/D25515 --- .../builtin/PhabricatorFilesComposeIconBuiltinFile.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php b/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php index ac16ba7f6b..7fd01ba810 100644 --- a/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php +++ b/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php @@ -98,6 +98,14 @@ public static function getAllColors() { } private function composeImage($color, $icon) { + // If we don't have the GD extension installed, just return a static + // default project image rather than trying to compose one. + if (!function_exists('imagecreatefromstring')) { + $root = dirname(phutil_get_library_root('phabricator')); + $default_path = $root.'/resources/builtin/profile.png'; + return Filesystem::readFile($default_path); + } + $color_map = self::getAllColors(); $color = idx($color_map, $color); if (!$color) { From c0191a039293b6585bd6a2db0a8b47e82a7a9bc2 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 26 Jan 2024 19:12:21 +0100 Subject: [PATCH 377/425] Fix PHP 8.1 "strlen(null)" exception creating a Merchant in Phortune Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` ERROR 8192: strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/var/www/html/phorge/phorge/src/applications/phortune/provider/PhortunePaymentProvider.php:99] ``` Closes T15725 Test Plan: See steps in T15725 Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15725 Differential Revision: https://we.phorge.it/D25528 --- src/applications/phortune/provider/PhortunePaymentProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/phortune/provider/PhortunePaymentProvider.php b/src/applications/phortune/provider/PhortunePaymentProvider.php index 57b2956ecb..eb6d2ff2e3 100644 --- a/src/applications/phortune/provider/PhortunePaymentProvider.php +++ b/src/applications/phortune/provider/PhortunePaymentProvider.php @@ -96,7 +96,7 @@ abstract public function extendEditForm( array $issues); protected function renderConfigurationSecret($value) { - if (strlen($value)) { + if (phutil_nonempty_string($value)) { return str_repeat('*', strlen($value)); } return ''; From 6a30dc039ea57c883f37640ce0fefed91712e94a Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 27 Jan 2024 09:33:09 +0100 Subject: [PATCH 378/425] Update external timezone information Summary: Update the list of timezones from 2016 by pulling from CLDR upstream (revision `57c1f4c` from 2024-01-11) located at https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml Closes T15727 Test Plan: * As an admin, go to http://phorge.localhost/config/edit/phabricator.timezone/ and change the default value. * As a user, go to http://phorge.localhost/settings/panel/datetime/ and change the value in the `Timezone` dropdown. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15727 Differential Revision: https://we.phorge.it/D25529 --- externals/cldr/cldr_windows_timezones.xml | 135 +++++++++++++--------- 1 file changed, 82 insertions(+), 53 deletions(-) diff --git a/externals/cldr/cldr_windows_timezones.xml b/externals/cldr/cldr_windows_timezones.xml index 47b689d8af..2fae6c3c18 100644 --- a/externals/cldr/cldr_windows_timezones.xml +++ b/externals/cldr/cldr_windows_timezones.xml @@ -9,7 +9,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -30,7 +30,6 @@ For terms of use, see http://www.unicode.org/copyright.html - @@ -49,7 +48,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -58,28 +57,32 @@ For terms of use, see http://www.unicode.org/copyright.html - + - + - - + + - - + + + + + + @@ -93,8 +96,8 @@ For terms of use, see http://www.unicode.org/copyright.html - - + + @@ -104,7 +107,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -129,7 +132,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -145,6 +148,10 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + @@ -195,13 +202,8 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - - @@ -216,7 +218,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -235,6 +237,10 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + @@ -260,14 +266,8 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - - - - - + + @@ -285,6 +285,7 @@ For terms of use, see http://www.unicode.org/copyright.html + @@ -295,9 +296,17 @@ For terms of use, see http://www.unicode.org/copyright.html - + + + + + + + + + @@ -358,17 +367,13 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - - + @@ -415,28 +420,40 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - - + + + + + + + + + + + + + + + + + @@ -449,9 +466,9 @@ For terms of use, see http://www.unicode.org/copyright.html - + - + @@ -463,9 +480,7 @@ For terms of use, see http://www.unicode.org/copyright.html - - @@ -499,10 +514,18 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + + + + + @@ -514,7 +537,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -530,6 +553,10 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + @@ -548,7 +575,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -627,10 +654,6 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - @@ -647,6 +670,10 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + @@ -682,7 +709,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -715,7 +742,6 @@ For terms of use, see http://www.unicode.org/copyright.html - @@ -749,12 +775,15 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + + + - - - From f8db94f2bfbe271ca1c9d47bf0d85411bdb82e0e Mon Sep 17 00:00:00 2001 From: Benjamin Kausch Date: Mon, 22 Jan 2024 18:04:39 +0100 Subject: [PATCH 379/425] Add flexible line height to remarkup headers Summary: The text in remarkup has a fixed line height. The headers will have the same fixed line height, even if their font size can be bigger than this line height. Especially on mobile this results in ugly sticked together lines which are less readable. This revision will give the header a flexible line height of `1.3`. Unitless means relative to the font size. | Before | After | |-----------|-----------| | {F717418} | {F717419} | (I have moved the same margin attribute for every `h{2-6}` into the new declaration.) Close T15716. Test Plan: Just checkout and look in wiki articles with headers to see if everything looks nice and neat. Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: 20after4, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15716 Differential Revision: https://we.phorge.it/D25523 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/core/remarkup.css | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 62ca4bb6a7..4008d8876e 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '4e0e22fb', + 'core.pkg.css' => 'c753df75', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '2431def2', @@ -109,7 +109,7 @@ 'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd', 'rsrc/css/application/uiexample/example.css' => 'b4795059', 'rsrc/css/core/core.css' => 'b3a5928e', - 'rsrc/css/core/remarkup.css' => 'b251d2e7', + 'rsrc/css/core/remarkup.css' => 'd91c2ee8', 'rsrc/css/core/syntax.css' => '548567f6', 'rsrc/css/core/z-index.css' => 'ac3bfcd4', 'rsrc/css/diviner/diviner-shared.css' => '4bd263b0', @@ -798,7 +798,7 @@ 'phabricator-object-selector-css' => 'ee77366f', 'phabricator-phtize' => '2f1db1ed', 'phabricator-prefab' => '5793d835', - 'phabricator-remarkup-css' => 'b251d2e7', + 'phabricator-remarkup-css' => 'd91c2ee8', 'phabricator-remarkup-metadata' => 'e40c4991', 'phabricator-search-results-css' => '9ea70ace', 'phabricator-shaped-request' => '995f5102', diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css index 76554b56b4..be3de293b9 100644 --- a/webroot/rsrc/css/core/remarkup.css +++ b/webroot/rsrc/css/core/remarkup.css @@ -174,29 +174,29 @@ list-style-type: none; } +.phabricator-remarkup .remarkup-header { + line-height: 1.4; + margin: 1em 0 0.75em; +} + .phabricator-remarkup h2.remarkup-header { font-size: 28px; - margin: 1em 0 0.75em; } .phabricator-remarkup h3.remarkup-header { font-size: 24px; - margin: 1em 0 0.75em; } .phabricator-remarkup h4.remarkup-header { font-size: 22px; - margin: 1em 0 0.75em; } .phabricator-remarkup h5.remarkup-header { font-size: 18px; - margin: 1em 0 0.75em; } .phabricator-remarkup h6.remarkup-header { font-size: 16px; - margin: 1em 0 0.75em; } .phabricator-remarkup blockquote { From 87db48289705382b381a8619c896452924f5ede0 Mon Sep 17 00:00:00 2001 From: Benjamin Kausch Date: Fri, 19 Jan 2024 11:04:54 +0100 Subject: [PATCH 380/425] Adds "Locate File" input to every browse directory view in Diffusion Summary: Ref T15645 The very helpful "Locate File" input in Diffusion was so far only visible in the homepage route of any repository (`/repository`). With this revision you can now locate a file from every browsed directory and in any selected commit. The finder was already "directory sensitive" meaning: if you are trying to locate a file from within a browsed directory, only the children of this path will be searched. For the searching in a specified commit (for example: https://we.phorge.it/source/phorge/browse/master/src/;05f4d5071fdca02123bd1ff4c0935b847c7f9963), I had to do a little JS magic adding the commit to the URI on the client side. Test Plan: Checkout, browse through your repos with Diffusion trying to find files. (I tested only with Git repos.) Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15645 Differential Revision: https://we.phorge.it/D25521 --- resources/celerity/map.php | 18 ++++---- .../controller/DiffusionBrowseController.php | 2 + .../controller/DiffusionController.php | 43 +++++++++++++++++++ .../DiffusionRepositoryController.php | 37 ---------------- .../diffusion/behavior-locate-file.js | 6 ++- 5 files changed, 59 insertions(+), 47 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 4008d8876e..4d48b64336 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -391,7 +391,7 @@ 'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'b7b73831', 'rsrc/js/application/diffusion/behavior-commit-branches.js' => '4b671572', 'rsrc/js/application/diffusion/behavior-commit-graph.js' => 'ac10c917', - 'rsrc/js/application/diffusion/behavior-locate-file.js' => '87428eb2', + 'rsrc/js/application/diffusion/behavior-locate-file.js' => '4c77f259', 'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123', 'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a', 'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b', @@ -619,7 +619,7 @@ 'javelin-behavior-differential-populate' => 'b86ef6c2', 'javelin-behavior-diffusion-commit-branches' => '4b671572', 'javelin-behavior-diffusion-commit-graph' => 'ac10c917', - 'javelin-behavior-diffusion-locate-file' => '87428eb2', + 'javelin-behavior-diffusion-locate-file' => '4c77f259', 'javelin-behavior-diffusion-pull-lastmodified' => 'c715c123', 'javelin-behavior-document-engine' => '243d6c22', 'javelin-behavior-doorkeeper-tag' => '6a85bc5a', @@ -1375,6 +1375,13 @@ 'javelin-install', 'javelin-dom', ), + '4c77f259' => array( + 'javelin-behavior', + 'javelin-diffusion-locate-file-source', + 'javelin-dom', + 'javelin-typeahead', + 'javelin-uri', + ), '4dffaeb2' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1672,13 +1679,6 @@ '84f82dad' => array( 'javelin-install', ), - '87428eb2' => array( - 'javelin-behavior', - 'javelin-diffusion-locate-file-source', - 'javelin-dom', - 'javelin-typeahead', - 'javelin-uri', - ), '876506b6' => array( 'javelin-view', 'javelin-install', diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index b81c42db8c..8cc0ae31c3 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -326,9 +326,11 @@ public function browseDirectory( )); $crumbs->setBorder(true); + $locate_file = $this->buildLocateFile(); $tabs = $this->buildTabsView('code'); $owners_list = $this->buildOwnersList($drequest); $bar = id(new PHUILeftRightView()) + ->setLeft($locate_file) ->setRight($this->corpusButtons) ->addClass('diffusion-action-bar'); diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index 618ee1a631..dc4c8e8699 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -554,4 +554,47 @@ protected function buildTabsView($key) { } + /** + * @return PHUIBoxView|null + */ + protected function buildLocateFile() { + $request = $this->getRequest(); + $viewer = $request->getUser(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + + $form_box = null; + if ($repository->canUsePathTree()) { + Javelin::initBehavior( + 'diffusion-locate-file', + array( + 'controlID' => 'locate-control', + 'inputID' => 'locate-input', + 'symbolicCommit' => $drequest->getSymbolicCommit(), + 'browseBaseURI' => (string)$drequest->generateURI( + array( + 'action' => 'browse', + 'commit' => '', + 'path' => '', + )), + 'uri' => (string)$drequest->generateURI( + array( + 'action' => 'pathtree', + )), + )); + + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->appendChild( + id(new AphrontFormTypeaheadControl()) + ->setHardpointID('locate-control') + ->setID('locate-input') + ->setPlaceholder(pht('Locate File'))); + $form_box = id(new PHUIBoxView()) + ->appendChild($form->buildLayoutView()) + ->addClass('diffusion-profile-locate'); + } + return $form_box; + } + } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index 3ae237c22d..d111d52c4e 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -433,43 +433,6 @@ private function buildBranchList(DiffusionRequest $drequest) { return $button; } - private function buildLocateFile() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - $drequest = $this->getDiffusionRequest(); - $repository = $drequest->getRepository(); - - $form_box = null; - if ($repository->canUsePathTree()) { - Javelin::initBehavior( - 'diffusion-locate-file', - array( - 'controlID' => 'locate-control', - 'inputID' => 'locate-input', - 'browseBaseURI' => (string)$drequest->generateURI( - array( - 'action' => 'browse', - )), - 'uri' => (string)$drequest->generateURI( - array( - 'action' => 'pathtree', - )), - )); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->appendChild( - id(new AphrontFormTypeaheadControl()) - ->setHardpointID('locate-control') - ->setID('locate-input') - ->setPlaceholder(pht('Locate File'))); - $form_box = id(new PHUIBoxView()) - ->appendChild($form->buildLayoutView()) - ->addClass('diffusion-profile-locate'); - } - return $form_box; - } - private function buildBrowseTable( $browse_results, $browse_paths, diff --git a/webroot/rsrc/js/application/diffusion/behavior-locate-file.js b/webroot/rsrc/js/application/diffusion/behavior-locate-file.js index c8817428e6..c44a1c7922 100644 --- a/webroot/rsrc/js/application/diffusion/behavior-locate-file.js +++ b/webroot/rsrc/js/application/diffusion/behavior-locate-file.js @@ -17,7 +17,11 @@ JX.behavior('diffusion-locate-file', function(config) { typeahead.setDatasource(datasource); typeahead.listen('choose', function(r) { - JX.$U(config.browseBaseURI + r.ref).go(); + var browseURI = config.browseBaseURI + r.ref; + if (config.symbolicCommit) { + browseURI += ';' + config.symbolicCommit; + } + JX.$U(browseURI).go(); }); var started = false; From 064424c1b75517cce9ede8f79bad501ceee5f668 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 25 Dec 2023 21:28:39 +0100 Subject: [PATCH 381/425] Add getQueryApplicationClass() to *TransactionQuery.php classes Summary: Add `getQueryApplicationClass()` to all `*TransactionQuery.php` classes similar to other `*Query.php` classes having the same function, and make the parent function in `PhabricatorApplicationTransactionQuery.php` abstract. In the future, this will enable excluding transaction query results based on their underlying application (for example if an application has been uninstalled) to mitigate the problem of overheated search results. See https://we.phorge.it/T15642 for context. The only callers of `getQueryApplicationClass()` are in `src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php` and `src/applications/policy/__tests__/PhabricatorPolicyTestCase.php`. See T15642 Test Plan: Patch changes only one existing code place, thus check if related pages still work as expected: * Go to http://phorge.localhost/feed/ * Go to http://phorge.localhost/feed/transactions/ * On http://phorge.localhost/feed/transactions/ , click `Edit Query` and set `Object Types` to `Application` etc. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15642 Differential Revision: https://we.phorge.it/D25500 --- .../almanac/query/AlmanacBindingTransactionQuery.php | 4 ++++ .../almanac/query/AlmanacDeviceTransactionQuery.php | 4 ++++ .../almanac/query/AlmanacInterfaceTransactionQuery.php | 4 ++++ .../almanac/query/AlmanacNamespaceTransactionQuery.php | 4 ++++ .../almanac/query/AlmanacNetworkTransactionQuery.php | 4 ++++ .../almanac/query/AlmanacServiceTransactionQuery.php | 4 ++++ .../audit/query/PhabricatorAuditTransactionQuery.php | 4 ++++ .../PhabricatorAuthContactNumberTransactionQuery.php | 4 ++++ .../PhabricatorAuthFactorProviderTransactionQuery.php | 4 ++++ .../auth/query/PhabricatorAuthMessageTransactionQuery.php | 4 ++++ .../query/PhabricatorAuthPasswordTransactionQuery.php | 4 ++++ .../PhabricatorAuthProviderConfigTransactionQuery.php | 4 ++++ .../auth/query/PhabricatorAuthSSHKeyTransactionQuery.php | 4 ++++ .../badges/query/PhabricatorBadgesTransactionQuery.php | 4 ++++ .../query/PhabricatorCalendarEventTransactionQuery.php | 4 ++++ .../query/PhabricatorCalendarExportTransactionQuery.php | 4 ++++ .../query/PhabricatorCalendarImportTransactionQuery.php | 4 ++++ .../config/query/PhabricatorConfigTransactionQuery.php | 4 ++++ .../conpherence/query/ConpherenceTransactionQuery.php | 4 ++++ .../query/PhabricatorCountdownTransactionQuery.php | 4 ++++ .../query/PhabricatorDashboardPanelTransactionQuery.php | 4 ++++ .../query/PhabricatorDashboardPortalTransactionQuery.php | 4 ++++ .../query/PhabricatorDashboardTransactionQuery.php | 4 ++++ .../query/DifferentialDiffTransactionQuery.php | 4 ++++ .../differential/query/DifferentialTransactionQuery.php | 4 ++++ .../diviner/query/DivinerLiveBookTransactionQuery.php | 4 ++++ .../drydock/query/DrydockBlueprintTransactionQuery.php | 4 ++++ .../files/query/PhabricatorFileTransactionQuery.php | 4 ++++ .../fund/query/FundBackerTransactionQuery.php | 4 ++++ .../fund/query/FundInitiativeTransactionQuery.php | 4 ++++ .../query/HarbormasterBuildPlanTransactionQuery.php | 4 ++++ .../query/HarbormasterBuildStepTransactionQuery.php | 4 ++++ .../query/HarbormasterBuildTransactionQuery.php | 4 ++++ .../query/HarbormasterBuildableTransactionQuery.php | 4 ++++ src/applications/herald/query/HeraldTransactionQuery.php | 4 ++++ .../herald/query/HeraldWebhookTransactionQuery.php | 4 ++++ .../legalpad/query/LegalpadTransactionQuery.php | 4 ++++ .../macro/query/PhabricatorMacroTransactionQuery.php | 4 ++++ .../maniphest/query/ManiphestTransactionQuery.php | 4 ++++ .../PhabricatorApplicationApplicationTransactionQuery.php | 7 +++++++ ...PhabricatorMetaMTAApplicationEmailTransactionQuery.php | 4 ++++ .../nuance/query/NuanceItemTransactionQuery.php | 4 ++++ .../nuance/query/NuanceQueueTransactionQuery.php | 4 ++++ .../nuance/query/NuanceSourceTransactionQuery.php | 4 ++++ .../query/PhabricatorOAuthServerTransactionQuery.php | 4 ++++ .../query/PhabricatorOwnersPackageTransactionQuery.php | 4 ++++ .../query/PhabricatorPackagesPackageTransactionQuery.php | 4 ++++ .../PhabricatorPackagesPublisherTransactionQuery.php | 4 ++++ .../query/PhabricatorPackagesVersionTransactionQuery.php | 4 ++++ .../query/PassphraseCredentialTransactionQuery.php | 4 ++++ .../paste/query/PhabricatorPasteTransactionQuery.php | 4 ++++ .../people/query/PhabricatorPeopleTransactionQuery.php | 4 ++++ .../phame/query/PhameBlogTransactionQuery.php | 4 ++++ .../phame/query/PhamePostTransactionQuery.php | 4 ++++ src/applications/phlux/query/PhluxTransactionQuery.php | 4 ++++ src/applications/pholio/query/PholioTransactionQuery.php | 4 ++++ .../query/PhortuneAccountEmailTransactionQuery.php | 4 ++++ .../phortune/query/PhortuneAccountTransactionQuery.php | 4 ++++ .../phortune/query/PhortuneCartTransactionQuery.php | 4 ++++ .../phortune/query/PhortuneMerchantTransactionQuery.php | 4 ++++ .../query/PhortunePaymentMethodTransactionQuery.php | 4 ++++ .../PhortunePaymentProviderConfigTransactionQuery.php | 4 ++++ .../query/PhortuneSubscriptionTransactionQuery.php | 4 ++++ .../phriction/query/PhrictionTransactionQuery.php | 4 ++++ .../phurl/query/PhabricatorPhurlURLTransactionQuery.php | 4 ++++ .../ponder/query/PonderAnswerTransactionQuery.php | 4 ++++ .../ponder/query/PonderQuestionTransactionQuery.php | 4 ++++ .../query/PhabricatorProjectColumnTransactionQuery.php | 4 ++++ .../project/query/PhabricatorProjectTransactionQuery.php | 4 ++++ .../query/PhabricatorProjectTriggerTransactionQuery.php | 4 ++++ .../PhabricatorRepositoryIdentityTransactionQuery.php | 4 ++++ .../query/PhabricatorRepositoryTransactionQuery.php | 4 ++++ .../query/PhabricatorRepositoryURITransactionQuery.php | 4 ++++ ...icatorProfileMenuItemConfigurationTransactionQuery.php | 4 ++++ .../query/PhabricatorUserPreferencesTransactionQuery.php | 4 ++++ .../query/PhabricatorSlowvoteTransactionQuery.php | 4 ++++ .../query/PhabricatorSpacesNamespaceTransactionQuery.php | 4 ++++ .../query/PhabricatorApplicationTransactionQuery.php | 8 +++++++- ...PhabricatorEditEngineConfigurationTransactionQuery.php | 4 ++++ .../query/PhabricatorWorkerBulkJobTransactionQuery.php | 4 ++++ 80 files changed, 326 insertions(+), 1 deletion(-) diff --git a/src/applications/almanac/query/AlmanacBindingTransactionQuery.php b/src/applications/almanac/query/AlmanacBindingTransactionQuery.php index 2b002e4f4a..55682cf7bc 100644 --- a/src/applications/almanac/query/AlmanacBindingTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacBindingTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacBindingTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacDeviceTransactionQuery.php b/src/applications/almanac/query/AlmanacDeviceTransactionQuery.php index 8b50fa85e5..8d12b526c9 100644 --- a/src/applications/almanac/query/AlmanacDeviceTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacDeviceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacDeviceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php b/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php index ef8d3961d2..0a0925e0a1 100644 --- a/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacInterfaceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php b/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php index 64ce639f45..f261a474de 100644 --- a/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacNamespaceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacNetworkTransactionQuery.php b/src/applications/almanac/query/AlmanacNetworkTransactionQuery.php index 7be51efec0..d49408b63d 100644 --- a/src/applications/almanac/query/AlmanacNetworkTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacNetworkTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacNetworkTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacServiceTransactionQuery.php b/src/applications/almanac/query/AlmanacServiceTransactionQuery.php index 96bf93377b..4ca6b3caa7 100644 --- a/src/applications/almanac/query/AlmanacServiceTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacServiceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacServiceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/audit/query/PhabricatorAuditTransactionQuery.php b/src/applications/audit/query/PhabricatorAuditTransactionQuery.php index 5966f1cc45..05741466f0 100644 --- a/src/applications/audit/query/PhabricatorAuditTransactionQuery.php +++ b/src/applications/audit/query/PhabricatorAuditTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuditTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuditApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php index a443cbab42..6cadf8f055 100644 --- a/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthContactNumberTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthFactorProviderTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthFactorProviderTransactionQuery.php index 5add1345c4..e0402ec557 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorProviderTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorProviderTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthFactorProviderTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php index 0b2ce79db3..25a2b81b82 100644 --- a/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthMessageTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthPasswordTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthPasswordTransactionQuery.php index 519b6aa77b..2ad7bb70ff 100644 --- a/src/applications/auth/query/PhabricatorAuthPasswordTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthPasswordTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthPasswordTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php index 68c14e71fa..b517c4f57e 100644 --- a/src/applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthProviderConfigTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php index 397a03f2b0..ea2602dc9f 100644 --- a/src/applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthSSHKeyTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php b/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php index bbe86fe3fa..7ad887cc97 100644 --- a/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorBadgesTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorBadgesApplication::class; + } + } diff --git a/src/applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php index cc465752fb..989e812ffc 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorCalendarEventTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorCalendarApplication::class; + } + } diff --git a/src/applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php b/src/applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php index 32b9d71b65..5858a20f58 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorCalendarExportTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorCalendarApplication::class; + } + } diff --git a/src/applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php index 123ec9b3c4..bad8217b66 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorCalendarImportTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorCalendarApplication::class; + } + } diff --git a/src/applications/config/query/PhabricatorConfigTransactionQuery.php b/src/applications/config/query/PhabricatorConfigTransactionQuery.php index 16ab2b47d6..c34f730410 100644 --- a/src/applications/config/query/PhabricatorConfigTransactionQuery.php +++ b/src/applications/config/query/PhabricatorConfigTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorConfigTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorConfigApplication::class; + } + } diff --git a/src/applications/conpherence/query/ConpherenceTransactionQuery.php b/src/applications/conpherence/query/ConpherenceTransactionQuery.php index d227217cb4..4e570d72ee 100644 --- a/src/applications/conpherence/query/ConpherenceTransactionQuery.php +++ b/src/applications/conpherence/query/ConpherenceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new ConpherenceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorConpherenceApplication::class; + } + } diff --git a/src/applications/countdown/query/PhabricatorCountdownTransactionQuery.php b/src/applications/countdown/query/PhabricatorCountdownTransactionQuery.php index 11c10a72eb..7a840ac4ab 100644 --- a/src/applications/countdown/query/PhabricatorCountdownTransactionQuery.php +++ b/src/applications/countdown/query/PhabricatorCountdownTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorCountdownTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorCountdownApplication::class; + } + } diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelTransactionQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelTransactionQuery.php index 9a192485c1..ad77844189 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelTransactionQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorDashboardPanelTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDashboardApplication::class; + } + } diff --git a/src/applications/dashboard/query/PhabricatorDashboardPortalTransactionQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPortalTransactionQuery.php index f4dff94088..6daf2f8869 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPortalTransactionQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPortalTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorDashboardPortalTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDashboardApplication::class; + } + } diff --git a/src/applications/dashboard/query/PhabricatorDashboardTransactionQuery.php b/src/applications/dashboard/query/PhabricatorDashboardTransactionQuery.php index e445f4a13e..0917ae0f9e 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardTransactionQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorDashboardTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDashboardApplication::class; + } + } diff --git a/src/applications/differential/query/DifferentialDiffTransactionQuery.php b/src/applications/differential/query/DifferentialDiffTransactionQuery.php index c9da0bab61..e7a245f4f2 100644 --- a/src/applications/differential/query/DifferentialDiffTransactionQuery.php +++ b/src/applications/differential/query/DifferentialDiffTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new DifferentialDiffTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDifferentialApplication::class; + } + } diff --git a/src/applications/differential/query/DifferentialTransactionQuery.php b/src/applications/differential/query/DifferentialTransactionQuery.php index d413782301..b7cf395f95 100644 --- a/src/applications/differential/query/DifferentialTransactionQuery.php +++ b/src/applications/differential/query/DifferentialTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new DifferentialTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDifferentialApplication::class; + } + } diff --git a/src/applications/diviner/query/DivinerLiveBookTransactionQuery.php b/src/applications/diviner/query/DivinerLiveBookTransactionQuery.php index e3ce6d41d7..b552417430 100644 --- a/src/applications/diviner/query/DivinerLiveBookTransactionQuery.php +++ b/src/applications/diviner/query/DivinerLiveBookTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new DivinerLiveBookTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDivinerApplication::class; + } + } diff --git a/src/applications/drydock/query/DrydockBlueprintTransactionQuery.php b/src/applications/drydock/query/DrydockBlueprintTransactionQuery.php index f2d70a692e..d6652412b8 100644 --- a/src/applications/drydock/query/DrydockBlueprintTransactionQuery.php +++ b/src/applications/drydock/query/DrydockBlueprintTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new DrydockBlueprintTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDrydockApplication::class; + } + } diff --git a/src/applications/files/query/PhabricatorFileTransactionQuery.php b/src/applications/files/query/PhabricatorFileTransactionQuery.php index 72bca16d60..33360b04b5 100644 --- a/src/applications/files/query/PhabricatorFileTransactionQuery.php +++ b/src/applications/files/query/PhabricatorFileTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorFileTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorFilesApplication::class; + } + } diff --git a/src/applications/fund/query/FundBackerTransactionQuery.php b/src/applications/fund/query/FundBackerTransactionQuery.php index 9ba1506add..15b493ab39 100644 --- a/src/applications/fund/query/FundBackerTransactionQuery.php +++ b/src/applications/fund/query/FundBackerTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new FundBackerTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorFundApplication::class; + } + } diff --git a/src/applications/fund/query/FundInitiativeTransactionQuery.php b/src/applications/fund/query/FundInitiativeTransactionQuery.php index 3c178ec645..a35b7d92c1 100644 --- a/src/applications/fund/query/FundInitiativeTransactionQuery.php +++ b/src/applications/fund/query/FundInitiativeTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new FundInitiativeTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorFundApplication::class; + } + } diff --git a/src/applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php b/src/applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php index a69365e01f..d67220fcb2 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new HarbormasterBuildPlanTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorHarbormasterApplication::class; + } + } diff --git a/src/applications/harbormaster/query/HarbormasterBuildStepTransactionQuery.php b/src/applications/harbormaster/query/HarbormasterBuildStepTransactionQuery.php index b61f94dd6c..6be73c5b6e 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildStepTransactionQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildStepTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new HarbormasterBuildStepTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorHarbormasterApplication::class; + } + } diff --git a/src/applications/harbormaster/query/HarbormasterBuildTransactionQuery.php b/src/applications/harbormaster/query/HarbormasterBuildTransactionQuery.php index 2d697f1ad2..bfed10f110 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildTransactionQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new HarbormasterBuildTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorHarbormasterApplication::class; + } + } diff --git a/src/applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php b/src/applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php index c6aeccc198..cccc794529 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new HarbormasterBuildableTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorHarbormasterApplication::class; + } + } diff --git a/src/applications/herald/query/HeraldTransactionQuery.php b/src/applications/herald/query/HeraldTransactionQuery.php index ac33da2070..97fff6407d 100644 --- a/src/applications/herald/query/HeraldTransactionQuery.php +++ b/src/applications/herald/query/HeraldTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new HeraldRuleTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorHeraldApplication::class; + } + } diff --git a/src/applications/herald/query/HeraldWebhookTransactionQuery.php b/src/applications/herald/query/HeraldWebhookTransactionQuery.php index b812305e56..2ca5f0ad5d 100644 --- a/src/applications/herald/query/HeraldWebhookTransactionQuery.php +++ b/src/applications/herald/query/HeraldWebhookTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new HeraldWebhookTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorHeraldApplication::class; + } + } diff --git a/src/applications/legalpad/query/LegalpadTransactionQuery.php b/src/applications/legalpad/query/LegalpadTransactionQuery.php index e4234d16a7..e0555fcd28 100644 --- a/src/applications/legalpad/query/LegalpadTransactionQuery.php +++ b/src/applications/legalpad/query/LegalpadTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new LegalpadTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorLegalpadApplication::class; + } + } diff --git a/src/applications/macro/query/PhabricatorMacroTransactionQuery.php b/src/applications/macro/query/PhabricatorMacroTransactionQuery.php index 5ff9348c12..4c1baf640d 100644 --- a/src/applications/macro/query/PhabricatorMacroTransactionQuery.php +++ b/src/applications/macro/query/PhabricatorMacroTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorMacroTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorMacroApplication::class; + } + } diff --git a/src/applications/maniphest/query/ManiphestTransactionQuery.php b/src/applications/maniphest/query/ManiphestTransactionQuery.php index 9826107b77..b29c0652a4 100644 --- a/src/applications/maniphest/query/ManiphestTransactionQuery.php +++ b/src/applications/maniphest/query/ManiphestTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new ManiphestTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorManiphestApplication::class; + } + } diff --git a/src/applications/meta/query/PhabricatorApplicationApplicationTransactionQuery.php b/src/applications/meta/query/PhabricatorApplicationApplicationTransactionQuery.php index 77843f713d..85bbf1162e 100644 --- a/src/applications/meta/query/PhabricatorApplicationApplicationTransactionQuery.php +++ b/src/applications/meta/query/PhabricatorApplicationApplicationTransactionQuery.php @@ -7,4 +7,11 @@ public function getTemplateApplicationTransaction() { return new PhabricatorApplicationApplicationTransaction(); } + // NOTE: Although this belongs to the "Applications" application, trying + // to filter its results just leaves us recursing indefinitely. Users + // always have access to applications regardless of other policy settings + // anyway. + public function getQueryApplicationClass() { + return null; + } } diff --git a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailTransactionQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailTransactionQuery.php index 4f4f6d11de..311e0af833 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailTransactionQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorMetaMTAApplicationEmailTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorMetaMTAApplication::class; + } + } diff --git a/src/applications/nuance/query/NuanceItemTransactionQuery.php b/src/applications/nuance/query/NuanceItemTransactionQuery.php index a2a29fc218..9b78d21a73 100644 --- a/src/applications/nuance/query/NuanceItemTransactionQuery.php +++ b/src/applications/nuance/query/NuanceItemTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new NuanceItemTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorNuanceApplication::class; + } + } diff --git a/src/applications/nuance/query/NuanceQueueTransactionQuery.php b/src/applications/nuance/query/NuanceQueueTransactionQuery.php index 6179e135e9..d8e209215c 100644 --- a/src/applications/nuance/query/NuanceQueueTransactionQuery.php +++ b/src/applications/nuance/query/NuanceQueueTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new NuanceQueueTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorNuanceApplication::class; + } + } diff --git a/src/applications/nuance/query/NuanceSourceTransactionQuery.php b/src/applications/nuance/query/NuanceSourceTransactionQuery.php index b87d06c815..937d1154ad 100644 --- a/src/applications/nuance/query/NuanceSourceTransactionQuery.php +++ b/src/applications/nuance/query/NuanceSourceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new NuanceSourceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorNuanceApplication::class; + } + } diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php index 4dd21e2609..b11139f3ee 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorOAuthServerTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorOAuthServerApplication::class; + } + } diff --git a/src/applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php index e0e7c2792f..06989ab52f 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorOwnersPackageTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorOwnersApplication::class; + } + } diff --git a/src/applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php b/src/applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php index 146f138119..fee7b2d4b8 100644 --- a/src/applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPackagesPackageTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPackagesApplication::class; + } + } diff --git a/src/applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php b/src/applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php index e7af82cfa8..111af3c5c6 100644 --- a/src/applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPackagesPublisherTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPackagesApplication::class; + } + } diff --git a/src/applications/packages/query/PhabricatorPackagesVersionTransactionQuery.php b/src/applications/packages/query/PhabricatorPackagesVersionTransactionQuery.php index 7ab10e2490..a414d0ba04 100644 --- a/src/applications/packages/query/PhabricatorPackagesVersionTransactionQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesVersionTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPackagesVersionTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPackagesApplication::class; + } + } diff --git a/src/applications/passphrase/query/PassphraseCredentialTransactionQuery.php b/src/applications/passphrase/query/PassphraseCredentialTransactionQuery.php index ebc5237091..a76789e7ca 100644 --- a/src/applications/passphrase/query/PassphraseCredentialTransactionQuery.php +++ b/src/applications/passphrase/query/PassphraseCredentialTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PassphraseCredentialTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPassphraseApplication::class; + } + } diff --git a/src/applications/paste/query/PhabricatorPasteTransactionQuery.php b/src/applications/paste/query/PhabricatorPasteTransactionQuery.php index b0e9b5bc47..00c70084b0 100644 --- a/src/applications/paste/query/PhabricatorPasteTransactionQuery.php +++ b/src/applications/paste/query/PhabricatorPasteTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPasteTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPasteApplication::class; + } + } diff --git a/src/applications/people/query/PhabricatorPeopleTransactionQuery.php b/src/applications/people/query/PhabricatorPeopleTransactionQuery.php index 898bc9ee05..564cd96df8 100644 --- a/src/applications/people/query/PhabricatorPeopleTransactionQuery.php +++ b/src/applications/people/query/PhabricatorPeopleTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorUserTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPeopleApplication::class; + } + } diff --git a/src/applications/phame/query/PhameBlogTransactionQuery.php b/src/applications/phame/query/PhameBlogTransactionQuery.php index 77a056b682..4c4c3be379 100644 --- a/src/applications/phame/query/PhameBlogTransactionQuery.php +++ b/src/applications/phame/query/PhameBlogTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhameBlogTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhameApplication::class; + } + } diff --git a/src/applications/phame/query/PhamePostTransactionQuery.php b/src/applications/phame/query/PhamePostTransactionQuery.php index 879b6363e3..9b93f4a808 100644 --- a/src/applications/phame/query/PhamePostTransactionQuery.php +++ b/src/applications/phame/query/PhamePostTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhamePostTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhameApplication::class; + } + } diff --git a/src/applications/phlux/query/PhluxTransactionQuery.php b/src/applications/phlux/query/PhluxTransactionQuery.php index a6969ef557..44d542fcab 100644 --- a/src/applications/phlux/query/PhluxTransactionQuery.php +++ b/src/applications/phlux/query/PhluxTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhluxTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhluxApplication::class; + } + } diff --git a/src/applications/pholio/query/PholioTransactionQuery.php b/src/applications/pholio/query/PholioTransactionQuery.php index 4c6d8ba0f5..00decb9236 100644 --- a/src/applications/pholio/query/PholioTransactionQuery.php +++ b/src/applications/pholio/query/PholioTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PholioTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPholioApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php b/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php index 2aa9d8418e..578153cdb6 100644 --- a/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneAccountEmailTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortuneAccountTransactionQuery.php b/src/applications/phortune/query/PhortuneAccountTransactionQuery.php index f49dc822c4..d1540edc87 100644 --- a/src/applications/phortune/query/PhortuneAccountTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneAccountTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneAccountTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortuneCartTransactionQuery.php b/src/applications/phortune/query/PhortuneCartTransactionQuery.php index a72b74814f..4a26bc877d 100644 --- a/src/applications/phortune/query/PhortuneCartTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneCartTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneCartTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortuneMerchantTransactionQuery.php b/src/applications/phortune/query/PhortuneMerchantTransactionQuery.php index e71128ce75..b8b7d32f28 100644 --- a/src/applications/phortune/query/PhortuneMerchantTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneMerchantTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneMerchantTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortunePaymentMethodTransactionQuery.php b/src/applications/phortune/query/PhortunePaymentMethodTransactionQuery.php index 2067e1e360..a0da99b600 100644 --- a/src/applications/phortune/query/PhortunePaymentMethodTransactionQuery.php +++ b/src/applications/phortune/query/PhortunePaymentMethodTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortunePaymentMethodTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php b/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php index 34797ba657..f346f5cafc 100644 --- a/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php +++ b/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortunePaymentProviderConfigTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortuneSubscriptionTransactionQuery.php b/src/applications/phortune/query/PhortuneSubscriptionTransactionQuery.php index db97925b39..b6d12cc368 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneSubscriptionTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneSubscriptionTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phriction/query/PhrictionTransactionQuery.php b/src/applications/phriction/query/PhrictionTransactionQuery.php index c43006364b..6b924f0985 100644 --- a/src/applications/phriction/query/PhrictionTransactionQuery.php +++ b/src/applications/phriction/query/PhrictionTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhrictionTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhrictionApplication::class; + } + } diff --git a/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php b/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php index 6c4e7d212d..11743281be 100644 --- a/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php +++ b/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPhurlURLTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhurlApplication::class; + } + } diff --git a/src/applications/ponder/query/PonderAnswerTransactionQuery.php b/src/applications/ponder/query/PonderAnswerTransactionQuery.php index 47d8a042a4..e22bd59d97 100644 --- a/src/applications/ponder/query/PonderAnswerTransactionQuery.php +++ b/src/applications/ponder/query/PonderAnswerTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PonderAnswerTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPonderApplication::class; + } + } diff --git a/src/applications/ponder/query/PonderQuestionTransactionQuery.php b/src/applications/ponder/query/PonderQuestionTransactionQuery.php index 788e6b5cbe..992dbb2681 100644 --- a/src/applications/ponder/query/PonderQuestionTransactionQuery.php +++ b/src/applications/ponder/query/PonderQuestionTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PonderQuestionTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPonderApplication::class; + } + } diff --git a/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php b/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php index f536aad05e..2a18104f76 100644 --- a/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorProjectColumnTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorProjectApplication::class; + } + } diff --git a/src/applications/project/query/PhabricatorProjectTransactionQuery.php b/src/applications/project/query/PhabricatorProjectTransactionQuery.php index 0211f6c5bc..e0a82666f9 100644 --- a/src/applications/project/query/PhabricatorProjectTransactionQuery.php +++ b/src/applications/project/query/PhabricatorProjectTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorProjectTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorProjectApplication::class; + } + } diff --git a/src/applications/project/query/PhabricatorProjectTriggerTransactionQuery.php b/src/applications/project/query/PhabricatorProjectTriggerTransactionQuery.php index 9ec4d4a53b..7a6c331947 100644 --- a/src/applications/project/query/PhabricatorProjectTriggerTransactionQuery.php +++ b/src/applications/project/query/PhabricatorProjectTriggerTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorProjectTriggerTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorProjectApplication::class; + } + } diff --git a/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php b/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php index f62a8610ff..c85ff3f5ad 100644 --- a/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorRepositoryIdentityTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDiffusionApplication::class; + } + } diff --git a/src/applications/repository/query/PhabricatorRepositoryTransactionQuery.php b/src/applications/repository/query/PhabricatorRepositoryTransactionQuery.php index 27410709c3..0656e80ef7 100644 --- a/src/applications/repository/query/PhabricatorRepositoryTransactionQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorRepositoryTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDiffusionApplication::class; + } + } diff --git a/src/applications/repository/query/PhabricatorRepositoryURITransactionQuery.php b/src/applications/repository/query/PhabricatorRepositoryURITransactionQuery.php index 28ae9d9d62..271cba67bd 100644 --- a/src/applications/repository/query/PhabricatorRepositoryURITransactionQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryURITransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorRepositoryURITransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDiffusionApplication::class; + } + } diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php index 0023660512..721bac781e 100644 --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorProfileMenuItemConfigurationTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorSearchApplication::class; + } + } diff --git a/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php b/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php index 7955830340..9b398e9d82 100644 --- a/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php +++ b/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorUserPreferencesTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorSettingsApplication::class; + } + } diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php b/src/applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php index d09096d50b..b1a8b8a2d5 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorSlowvoteTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorSlowvoteApplication::class; + } + } diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php index 34d7e43570..c55fedd8d0 100644 --- a/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php +++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorSpacesNamespaceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorSpacesApplication::class; + } + } diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php index 195de601f0..64e94947ac 100644 --- a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php +++ b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php @@ -292,8 +292,14 @@ protected function shouldGroupQueryResultRows() { return parent::shouldGroupQueryResultRows(); } + /** + * Get name of class of application queried by the query. + * + * To be overwritten by child classes when applicable. + * + * @return string|null Application class name + */ public function getQueryApplicationClass() { - // TODO: Sort this out? return null; } diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php index 2a4677944e..429ed8db5e 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorEditEngineConfigurationTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorTransactionsApplication::class; + } + } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobTransactionQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobTransactionQuery.php index 350277a888..f3eddb8020 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobTransactionQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorWorkerBulkJobTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDaemonsApplication::class; + } + } From d9919ddb0667a31b1f13fcfa60ca2cd483147309 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Sat, 27 Jan 2024 12:24:57 +0200 Subject: [PATCH 382/425] Revert "Added cross-platform default fonts" Summary: Revert rPf8ffa393c4427f8b9026075e6dc490990a12df12 (D25021). This Commit caused more grief then fun. Ref T15630 Test Plan: Site should still be readable on most platforms Reviewers: O1 Blessed Committers, aklapper Reviewed By: O1 Blessed Committers, aklapper Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15630 Differential Revision: https://we.phorge.it/D25530 --- resources/celerity/map.php | 59 +++++++++++-------- .../CelerityDarkModePostprocessor.php | 12 ++-- .../CelerityDefaultPostprocessor.php | 12 ++-- .../CelerityLargeFontPostprocessor.php | 6 +- .../application/config/config-template.css | 3 +- .../application/harbormaster/harbormaster.css | 3 +- 6 files changed, 51 insertions(+), 44 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 4d48b64336..db4047fc00 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,10 +9,14 @@ 'names' => array( 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', +<<<<<<< HEAD 'core.pkg.css' => 'c753df75', +======= + 'core.pkg.css' => '77d64402', +>>>>>>> 67b4f957d1 (Revert "Added cross-platform default fonts") 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', - 'differential.pkg.css' => '2431def2', + 'differential.pkg.css' => '6d3700f0', 'differential.pkg.js' => '46fcb3af', 'diffusion.pkg.css' => '42c75c37', 'diffusion.pkg.js' => '78c9885d', @@ -44,7 +48,7 @@ 'rsrc/css/application/base/standard-page-view.css' => 'e08c7462', 'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41', 'rsrc/css/application/config/config-options.css' => '16c920ae', - 'rsrc/css/application/config/config-template.css' => 'e689dbbd', + 'rsrc/css/application/config/config-template.css' => '20babf50', 'rsrc/css/application/config/setup-issue.css' => '93231115', 'rsrc/css/application/config/unhandled-exception.css' => '9ecfc00d', 'rsrc/css/application/conpherence/color.css' => 'b17746b0', @@ -62,9 +66,9 @@ 'rsrc/css/application/diff/diff-tree-view.css' => 'e2d3e222', 'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d', 'rsrc/css/application/differential/add-comment.css' => '7e5900d9', - 'rsrc/css/application/differential/changeset-view.css' => '360630ff', + 'rsrc/css/application/differential/changeset-view.css' => '1b0476bc', 'rsrc/css/application/differential/core.css' => '7300a73e', - 'rsrc/css/application/differential/phui-inline-comment.css' => 'a864426f', + 'rsrc/css/application/differential/phui-inline-comment.css' => '9863a85e', 'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d', 'rsrc/css/application/differential/revision-history.css' => '237a2979', 'rsrc/css/application/differential/revision-list.css' => '93d2df7d', @@ -76,7 +80,7 @@ 'rsrc/css/application/feed/feed.css' => 'd8b6e3f8', 'rsrc/css/application/files/global-drag-and-drop.css' => '1d2713a4', 'rsrc/css/application/flag/flag.css' => '2b77be8d', - 'rsrc/css/application/harbormaster/harbormaster.css' => 'cd0ce059', + 'rsrc/css/application/harbormaster/harbormaster.css' => '9346e08b', 'rsrc/css/application/herald/herald-test.css' => '7e7bbdae', 'rsrc/css/application/herald/herald.css' => '648d39e2', 'rsrc/css/application/maniphest/report.css' => '3d53188b', @@ -100,16 +104,21 @@ 'rsrc/css/application/policy/policy-transaction-detail.css' => 'c02b8384', 'rsrc/css/application/policy/policy.css' => 'ceb56a08', 'rsrc/css/application/ponder/ponder-view.css' => 'b04bbaff', - 'rsrc/css/application/project/project-card-view.css' => 'c1200da7', + 'rsrc/css/application/project/project-card-view.css' => 'a9f2c2dd', 'rsrc/css/application/project/project-triggers.css' => 'cd9c8bb9', - 'rsrc/css/application/project/project-view.css' => '2f7caa20', + 'rsrc/css/application/project/project-view.css' => '567858b3', 'rsrc/css/application/search/application-search-view.css' => '0f7c06d8', 'rsrc/css/application/search/search-results.css' => '9ea70ace', 'rsrc/css/application/slowvote/slowvote.css' => '1694baed', 'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd', 'rsrc/css/application/uiexample/example.css' => 'b4795059', +<<<<<<< HEAD 'rsrc/css/core/core.css' => 'b3a5928e', 'rsrc/css/core/remarkup.css' => 'd91c2ee8', +======= + 'rsrc/css/core/core.css' => '531ad849', + 'rsrc/css/core/remarkup.css' => 'b251d2e7', +>>>>>>> 67b4f957d1 (Revert "Added cross-platform default fonts") 'rsrc/css/core/syntax.css' => '548567f6', 'rsrc/css/core/z-index.css' => 'ac3bfcd4', 'rsrc/css/diviner/diviner-shared.css' => '4bd263b0', @@ -123,7 +132,7 @@ 'rsrc/css/layout/phabricator-source-code-view.css' => 'e382316a', 'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4', 'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa', - 'rsrc/css/phui/button/phui-button.css' => 'f9d0f9c8', + 'rsrc/css/phui/button/phui-button.css' => '55025b10', 'rsrc/css/phui/calendar/phui-calendar-day.css' => '9597d706', 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2', 'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42', @@ -141,7 +150,7 @@ 'rsrc/css/phui/phui-big-info-view.css' => '362ad37b', 'rsrc/css/phui/phui-box.css' => '5ed3b8cb', 'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30', - 'rsrc/css/phui/phui-chart.css' => 'fe8f87a7', + 'rsrc/css/phui/phui-chart.css' => '14df9ae3', 'rsrc/css/phui/phui-cms.css' => '8c05c41e', 'rsrc/css/phui/phui-comment-form.css' => '3c6679a3', 'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0', @@ -154,7 +163,7 @@ 'rsrc/css/phui/phui-feed-story.css' => 'a0c05029', 'rsrc/css/phui/phui-fontkit.css' => '1ec937e5', 'rsrc/css/phui/phui-form-view.css' => '57edecb7', - 'rsrc/css/phui/phui-form.css' => 'd1adb52c', + 'rsrc/css/phui/phui-form.css' => '1f177cb7', 'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed', 'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', 'rsrc/css/phui/phui-header-view.css' => '36c86a58', @@ -167,7 +176,7 @@ 'rsrc/css/phui/phui-left-right.css' => '68513c34', 'rsrc/css/phui/phui-lightbox.css' => '4ebf22da', 'rsrc/css/phui/phui-list.css' => 'ccf73664', - 'rsrc/css/phui/phui-object-box.css' => 'fdffed5c', + 'rsrc/css/phui/phui-object-box.css' => 'b8d7eea0', 'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', 'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64', @@ -559,7 +568,7 @@ 'conpherence-transaction-css' => '3a3f5e7e', 'd3' => '9d068042', 'diff-tree-view-css' => 'e2d3e222', - 'differential-changeset-view-css' => '360630ff', + 'differential-changeset-view-css' => '1b0476bc', 'differential-core-view-css' => '7300a73e', 'differential-revision-add-comment-css' => '7e5900d9', 'differential-revision-comment-css' => '7dbc8d1d', @@ -578,7 +587,7 @@ 'fuel-map-css' => 'd6e31510', 'fuel-menu-css' => '21f5d199', 'global-drag-and-drop-css' => '1d2713a4', - 'harbormaster-css' => 'cd0ce059', + 'harbormaster-css' => '9346e08b', 'herald-css' => '648d39e2', 'herald-rule-editor' => '2633bef7', 'herald-test-css' => '7e7bbdae', @@ -770,7 +779,7 @@ 'phabricator-action-list-view-css' => '1b0085b2', 'phabricator-busy' => '5202e831', 'phabricator-content-source-view-css' => 'cdf0d579', - 'phabricator-core-css' => 'b3a5928e', + 'phabricator-core-css' => '531ad849', 'phabricator-countdown-css' => 'bff8012f', 'phabricator-darklog' => '3b869402', 'phabricator-darkmessage' => '26cd4b73', @@ -783,7 +792,7 @@ 'phabricator-diff-tree-view' => '5d83623b', 'phabricator-drag-and-drop-file-upload' => '4370900d', 'phabricator-draggable-list' => '0169e425', - 'phabricator-fatal-config-template-css' => 'e689dbbd', + 'phabricator-fatal-config-template-css' => '20babf50', 'phabricator-favicon' => '7930776a', 'phabricator-feed-css' => 'd8b6e3f8', 'phabricator-file-upload' => '331676ea', @@ -827,13 +836,13 @@ 'phui-box-css' => '5ed3b8cb', 'phui-bulk-editor-css' => '374d5e30', 'phui-button-bar-css' => 'a4aa75c4', - 'phui-button-css' => 'f9d0f9c8', + 'phui-button-css' => '55025b10', 'phui-button-simple-css' => '1ff278aa', 'phui-calendar-css' => 'f11073aa', 'phui-calendar-day-css' => '9597d706', 'phui-calendar-list-css' => 'ccd7e4e2', 'phui-calendar-month-css' => 'cb758c42', - 'phui-chart-css' => 'fe8f87a7', + 'phui-chart-css' => '14df9ae3', 'phui-cms-css' => '8c05c41e', 'phui-comment-form-css' => '3c6679a3', 'phui-comment-panel-css' => 'ec4e31c0', @@ -846,7 +855,7 @@ 'phui-feed-story-css' => 'a0c05029', 'phui-font-icon-base-css' => '303c9b87', 'phui-fontkit-css' => '1ec937e5', - 'phui-form-css' => 'd1adb52c', + 'phui-form-css' => '1f177cb7', 'phui-form-view-css' => '57edecb7', 'phui-formation-view-css' => 'd2dec8ed', 'phui-head-thing-view-css' => 'd7f293df', @@ -858,12 +867,12 @@ 'phui-icon-view-css' => '084ac612', 'phui-image-mask-css' => '62c7f4d2', 'phui-info-view-css' => 'a10a909b', - 'phui-inline-comment-view-css' => 'a864426f', + 'phui-inline-comment-view-css' => '9863a85e', 'phui-invisible-character-view-css' => 'c694c4a4', 'phui-left-right-css' => '68513c34', 'phui-lightbox-css' => '4ebf22da', 'phui-list-view-css' => 'ccf73664', - 'phui-object-box-css' => 'fdffed5c', + 'phui-object-box-css' => 'b8d7eea0', 'phui-oi-big-ui-css' => 'fa74cc35', 'phui-oi-color-css' => 'b517bfa0', 'phui-oi-drag-ui-css' => 'da15d3dc', @@ -900,9 +909,9 @@ 'policy-edit-css' => '8794e2ed', 'policy-transaction-detail-css' => 'c02b8384', 'ponder-view-css' => 'b04bbaff', - 'project-card-view-css' => 'c1200da7', + 'project-card-view-css' => 'a9f2c2dd', 'project-triggers-css' => 'cd9c8bb9', - 'project-view-css' => '2f7caa20', + 'project-view-css' => '567858b3', 'setup-issue-css' => '93231115', 'sprite-login-css' => '07052ee0', 'sprite-tokens-css' => 'f1896dc5', @@ -1055,6 +1064,9 @@ 'javelin-util', 'phabricator-keyboard-shortcut-manager', ), + '1b0476bc' => array( + 'phui-inline-comment-view-css', + ), '1b6acc2a' => array( 'javelin-magical-init', 'javelin-util', @@ -1229,9 +1241,6 @@ 'aphront-typeahead-control-css', 'phui-tag-view-css', ), - '360630ff' => array( - 'phui-inline-comment-view-css', - ), '3829a3cf' => array( 'javelin-behavior', 'javelin-uri', diff --git a/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php b/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php index 31932352ef..39ee4be57a 100644 --- a/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php @@ -15,13 +15,13 @@ public function buildVariables() { return array( // Fonts - 'basefont' => "13px -apple-system, system-ui, BlinkMacSystemFont, ". - "'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', ". - "'Helvetica Neue', Helvetica, Arial, sans-serif", + 'basefont' => "13px 'Segoe UI', 'Segoe UI Emoji', ". + "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ". + "Helvetica, Arial, sans-serif", - 'fontfamily' => "-apple-system, system-ui, BlinkMacSystemFont, ". - "'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', ". - "'Helvetica Neue', Helvetica, Arial, sans-serif", + 'fontfamily' => "'Segoe UI', 'Segoe UI Emoji', ". + "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ". + "Helvetica, Arial, sans-serif", // Drop Shadow 'dropshadow' => '0 2px 12px rgba(0, 0, 0, .20)', diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php index ff7e7f3668..be039772c1 100644 --- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php @@ -20,13 +20,13 @@ public function buildDefaultPostprocessor() { public function buildVariables() { return array( // Fonts - 'basefont' => "13px -apple-system, system-ui, BlinkMacSystemFont, ". - "'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', ". - "'Helvetica Neue', Helvetica, Arial, sans-serif", + 'basefont' => "13px 'Segoe UI', 'Segoe UI Emoji', ". + "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ". + "Helvetica, Arial, sans-serif", - 'fontfamily' => "-apple-system, system-ui, BlinkMacSystemFont, ". - "'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', ". - "'Helvetica Neue', Helvetica, Arial, sans-serif", + 'fontfamily' => "'Segoe UI', 'Segoe UI Emoji', ". + "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ". + "Helvetica, Arial, sans-serif", // Drop Shadow 'dropshadow' => '0 2px 12px rgba(0, 0, 0, .20)', diff --git a/src/applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php b/src/applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php index 1bc4e1cb3c..4dbe561951 100644 --- a/src/applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php @@ -14,9 +14,9 @@ public function getPostprocessorName() { public function buildVariables() { return array( - 'basefont' => "14px -apple-system, system-ui, BlinkMacSystemFont, ". - "'Segoe UI', 'Segoe UI Web Regular', 'Segoe UI Symbol', 'Lato', ". - "'Helvetica Neue', Helvetica, Arial, sans-serif", + 'basefont' => "14px 'Segoe UI', 'Segoe UI Web Regular', ". + "'Segoe UI Symbol', 'Lato', 'Helvetica Neue', Helvetica, ". + "Arial, sans-serif", // Font Sizes 'biggestfontsize' => '16px', diff --git a/webroot/rsrc/css/application/config/config-template.css b/webroot/rsrc/css/application/config/config-template.css index 8b212520fd..13ce56488a 100644 --- a/webroot/rsrc/css/application/config/config-template.css +++ b/webroot/rsrc/css/application/config/config-template.css @@ -6,8 +6,7 @@ body { background: #f9f9f9; margin: 0; padding: 0; - font: 13px/1.231 -apple-system, system-ui, BlinkMacSystemFont, - 'Segoe UI', 'Segoe UI Web Regular', 'Segoe UI Symbol', + font: 13px/1.231 'Segoe UI', 'Segoe UI Web Regular', 'Segoe UI Symbol', 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: left; -webkit-text-size-adjust: none; diff --git a/webroot/rsrc/css/application/harbormaster/harbormaster.css b/webroot/rsrc/css/application/harbormaster/harbormaster.css index 0b9aed76f8..b27d549b6b 100644 --- a/webroot/rsrc/css/application/harbormaster/harbormaster.css +++ b/webroot/rsrc/css/application/harbormaster/harbormaster.css @@ -103,8 +103,7 @@ .harbormaster-log-expand-table td { vertical-align: middle; - font: 13px -apple-system, system-ui, BlinkMacSystemFont, - 'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', + font: 13px 'Segoe UI', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Lato', 'Helvetica Neue', Helvetica, Arial, sans-serif; } From 25aebab655fdb50118b8dccc6a23981b8b43580b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 23 Jan 2024 14:34:58 +0100 Subject: [PATCH 383/425] Make misc get*ApplicationClass[Name]() calls return ::class constant instead of string Summary: `::class` is available since PHP 5.5 (5.5 is a minimal requirement by Phorge): https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.class It makes finding code using IDEs easier; see discussion in D25500. Thus replace all string return values with returning the `::class` constant instead, with one exception: 'PhabricatorSettingApplication' in `PhabricatorUserPreferencesSearchEngine.php` does not exist and makes arc lint fail so this string remained unchanged. Also note that two occurrences were wrapped in `pht()` for reasons I do not know. List of functions whose return value get updated in this code change: * getApplicationClassName() * getAdapterApplicationClass() * getDatasourceApplicationClass() * getEditorApplicationClass() * getEngineApplicationClass() * getPHIDTypeApplicationClass() * getQueryApplicationClass() cf. T15158 Test Plan: Too broad - click around, basically. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15158 Differential Revision: https://we.phorge.it/D25524 --- src/applications/almanac/editor/AlmanacBindingEditEngine.php | 2 +- src/applications/almanac/editor/AlmanacDeviceEditEngine.php | 2 +- src/applications/almanac/editor/AlmanacEditor.php | 2 +- src/applications/almanac/editor/AlmanacInterfaceEditEngine.php | 2 +- src/applications/almanac/editor/AlmanacNamespaceEditEngine.php | 2 +- src/applications/almanac/editor/AlmanacNetworkEditEngine.php | 2 +- src/applications/almanac/editor/AlmanacPropertyEditEngine.php | 2 +- src/applications/almanac/editor/AlmanacServiceEditEngine.php | 2 +- src/applications/almanac/phid/AlmanacBindingPHIDType.php | 2 +- src/applications/almanac/phid/AlmanacDevicePHIDType.php | 2 +- src/applications/almanac/phid/AlmanacInterfacePHIDType.php | 2 +- src/applications/almanac/phid/AlmanacNamespacePHIDType.php | 2 +- src/applications/almanac/phid/AlmanacNetworkPHIDType.php | 2 +- src/applications/almanac/phid/AlmanacServicePHIDType.php | 2 +- src/applications/almanac/query/AlmanacBindingSearchEngine.php | 2 +- src/applications/almanac/query/AlmanacDeviceQuery.php | 2 +- src/applications/almanac/query/AlmanacDeviceSearchEngine.php | 2 +- src/applications/almanac/query/AlmanacInterfaceQuery.php | 2 +- src/applications/almanac/query/AlmanacInterfaceSearchEngine.php | 2 +- src/applications/almanac/query/AlmanacNamespaceQuery.php | 2 +- src/applications/almanac/query/AlmanacNamespaceSearchEngine.php | 2 +- src/applications/almanac/query/AlmanacNetworkQuery.php | 2 +- src/applications/almanac/query/AlmanacNetworkSearchEngine.php | 2 +- src/applications/almanac/query/AlmanacPropertyQuery.php | 2 +- src/applications/almanac/query/AlmanacQuery.php | 2 +- src/applications/almanac/query/AlmanacServiceSearchEngine.php | 2 +- .../almanac/typeahead/AlmanacInterfaceDatasource.php | 2 +- src/applications/almanac/typeahead/AlmanacServiceDatasource.php | 2 +- .../almanac/typeahead/AlmanacServiceTypeDatasource.php | 2 +- src/applications/audit/editor/PhabricatorAuditEditor.php | 2 +- .../audit/query/DiffusionInternalCommitSearchEngine.php | 2 +- src/applications/audit/query/PhabricatorCommitSearchEngine.php | 2 +- .../auth/editor/PhabricatorAuthContactNumberEditEngine.php | 2 +- .../auth/editor/PhabricatorAuthContactNumberEditor.php | 2 +- .../auth/editor/PhabricatorAuthFactorProviderEditEngine.php | 2 +- .../auth/editor/PhabricatorAuthFactorProviderEditor.php | 2 +- .../auth/editor/PhabricatorAuthMessageEditEngine.php | 2 +- src/applications/auth/editor/PhabricatorAuthMessageEditor.php | 2 +- src/applications/auth/editor/PhabricatorAuthPasswordEditor.php | 2 +- .../auth/editor/PhabricatorAuthProviderConfigEditor.php | 2 +- src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php | 2 +- .../auth/phid/PhabricatorAuthAuthFactorPHIDType.php | 2 +- .../auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php | 2 +- .../auth/phid/PhabricatorAuthAuthProviderPHIDType.php | 2 +- src/applications/auth/phid/PhabricatorAuthChallengePHIDType.php | 2 +- .../auth/phid/PhabricatorAuthContactNumberPHIDType.php | 2 +- src/applications/auth/phid/PhabricatorAuthInvitePHIDType.php | 2 +- src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php | 2 +- src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php | 2 +- src/applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php | 2 +- src/applications/auth/phid/PhabricatorAuthSessionPHIDType.php | 2 +- src/applications/auth/query/PhabricatorAuthChallengeQuery.php | 2 +- .../auth/query/PhabricatorAuthContactNumberQuery.php | 2 +- .../auth/query/PhabricatorAuthFactorConfigQuery.php | 2 +- .../auth/query/PhabricatorAuthFactorProviderQuery.php | 2 +- .../auth/query/PhabricatorAuthInviteSearchEngine.php | 2 +- src/applications/auth/query/PhabricatorAuthMessageQuery.php | 2 +- src/applications/auth/query/PhabricatorAuthPasswordQuery.php | 2 +- .../auth/query/PhabricatorAuthProviderConfigQuery.php | 2 +- src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php | 2 +- .../auth/query/PhabricatorAuthSSHKeySearchEngine.php | 2 +- src/applications/auth/query/PhabricatorAuthSessionQuery.php | 2 +- .../auth/query/PhabricatorAuthTemporaryTokenQuery.php | 2 +- .../auth/query/PhabricatorExternalAccountIdentifierQuery.php | 2 +- src/applications/auth/query/PhabricatorExternalAccountQuery.php | 2 +- src/applications/badges/editor/PhabricatorBadgesEditEngine.php | 2 +- src/applications/badges/editor/PhabricatorBadgesEditor.php | 2 +- src/applications/badges/phid/PhabricatorBadgesPHIDType.php | 2 +- src/applications/badges/query/PhabricatorBadgesAwardQuery.php | 2 +- src/applications/badges/query/PhabricatorBadgesQuery.php | 2 +- src/applications/badges/query/PhabricatorBadgesSearchEngine.php | 2 +- .../badges/typeahead/PhabricatorBadgesDatasource.php | 2 +- .../calendar/editor/PhabricatorCalendarEventEditEngine.php | 2 +- .../calendar/editor/PhabricatorCalendarEventEditor.php | 2 +- .../calendar/editor/PhabricatorCalendarExportEditEngine.php | 2 +- .../calendar/editor/PhabricatorCalendarExportEditor.php | 2 +- .../calendar/editor/PhabricatorCalendarImportEditEngine.php | 2 +- .../calendar/editor/PhabricatorCalendarImportEditor.php | 2 +- .../calendar/herald/PhabricatorCalendarEventHeraldAdapter.php | 2 +- .../calendar/phid/PhabricatorCalendarEventPHIDType.php | 2 +- .../calendar/phid/PhabricatorCalendarExportPHIDType.php | 2 +- .../phid/PhabricatorCalendarExternalInviteePHIDType.php | 2 +- .../calendar/phid/PhabricatorCalendarImportPHIDType.php | 2 +- .../calendar/query/PhabricatorCalendarEventInviteeQuery.php | 2 +- .../calendar/query/PhabricatorCalendarEventQuery.php | 2 +- .../calendar/query/PhabricatorCalendarEventSearchEngine.php | 2 +- .../calendar/query/PhabricatorCalendarExportQuery.php | 2 +- .../calendar/query/PhabricatorCalendarExportSearchEngine.php | 2 +- .../calendar/query/PhabricatorCalendarExternalInviteeQuery.php | 2 +- .../calendar/query/PhabricatorCalendarImportLogQuery.php | 2 +- .../calendar/query/PhabricatorCalendarImportLogSearchEngine.php | 2 +- .../calendar/query/PhabricatorCalendarImportQuery.php | 2 +- .../calendar/query/PhabricatorCalendarImportSearchEngine.php | 2 +- .../calendar/typeahead/PhabricatorCalendarInviteeDatasource.php | 2 +- .../typeahead/PhabricatorCalendarInviteeUserDatasource.php | 2 +- .../PhabricatorCalendarInviteeViewerFunctionDatasource.php | 2 +- src/applications/conduit/query/PhabricatorConduitLogQuery.php | 2 +- .../conduit/query/PhabricatorConduitLogSearchEngine.php | 2 +- .../conduit/query/PhabricatorConduitMethodQuery.php | 2 +- .../conduit/query/PhabricatorConduitSearchEngine.php | 2 +- src/applications/conduit/query/PhabricatorConduitTokenQuery.php | 2 +- src/applications/config/editor/PhabricatorConfigEditor.php | 2 +- .../config/phid/PhabricatorConfigConfigPHIDType.php | 2 +- src/applications/config/query/PhabricatorConfigEntryQuery.php | 2 +- src/applications/conpherence/editor/ConpherenceEditEngine.php | 2 +- src/applications/conpherence/editor/ConpherenceEditor.php | 2 +- .../conpherence/phid/PhabricatorConpherenceThreadPHIDType.php | 2 +- src/applications/conpherence/query/ConpherenceThreadQuery.php | 2 +- .../conpherence/query/ConpherenceThreadSearchEngine.php | 2 +- .../conpherence/typeahead/ConpherenceThreadDatasource.php | 2 +- .../countdown/editor/PhabricatorCountdownEditEngine.php | 2 +- .../countdown/editor/PhabricatorCountdownEditor.php | 2 +- .../countdown/phid/PhabricatorCountdownCountdownPHIDType.php | 2 +- src/applications/countdown/query/PhabricatorCountdownQuery.php | 2 +- .../countdown/query/PhabricatorCountdownSearchEngine.php | 2 +- src/applications/daemon/query/PhabricatorDaemonLogQuery.php | 2 +- .../dashboard/editor/PhabricatorDashboardEditEngine.php | 2 +- .../dashboard/editor/PhabricatorDashboardPanelEditEngine.php | 2 +- .../editor/PhabricatorDashboardPanelTransactionEditor.php | 2 +- .../dashboard/editor/PhabricatorDashboardPortalEditEngine.php | 2 +- .../dashboard/editor/PhabricatorDashboardPortalEditor.php | 2 +- .../dashboard/editor/PhabricatorDashboardTransactionEditor.php | 2 +- .../dashboard/phid/PhabricatorDashboardDashboardPHIDType.php | 2 +- .../dashboard/phid/PhabricatorDashboardPanelPHIDType.php | 2 +- .../dashboard/phid/PhabricatorDashboardPortalPHIDType.php | 2 +- .../dashboard/query/PhabricatorDashboardPanelQuery.php | 2 +- .../dashboard/query/PhabricatorDashboardPanelSearchEngine.php | 2 +- .../dashboard/query/PhabricatorDashboardPortalQuery.php | 2 +- .../dashboard/query/PhabricatorDashboardPortalSearchEngine.php | 2 +- src/applications/dashboard/query/PhabricatorDashboardQuery.php | 2 +- .../dashboard/query/PhabricatorDashboardSearchEngine.php | 2 +- .../dashboard/typeahead/PhabricatorDashboardDatasource.php | 2 +- .../dashboard/typeahead/PhabricatorDashboardPanelDatasource.php | 2 +- .../typeahead/PhabricatorDashboardPortalDatasource.php | 2 +- src/applications/differential/editor/DifferentialDiffEditor.php | 2 +- .../differential/editor/DifferentialRevisionEditEngine.php | 2 +- .../differential/editor/DifferentialTransactionEditor.php | 2 +- .../differential/herald/HeraldDifferentialDiffAdapter.php | 2 +- .../differential/herald/HeraldDifferentialRevisionAdapter.php | 2 +- .../differential/phid/DifferentialChangesetPHIDType.php | 2 +- src/applications/differential/phid/DifferentialDiffPHIDType.php | 2 +- .../differential/phid/DifferentialRevisionPHIDType.php | 2 +- .../differential/query/DifferentialChangesetQuery.php | 2 +- .../differential/query/DifferentialChangesetSearchEngine.php | 2 +- src/applications/differential/query/DifferentialDiffQuery.php | 2 +- .../differential/query/DifferentialDiffSearchEngine.php | 2 +- src/applications/differential/query/DifferentialHunkQuery.php | 2 +- .../differential/query/DifferentialRevisionQuery.php | 2 +- .../differential/query/DifferentialRevisionSearchEngine.php | 2 +- .../differential/query/DifferentialViewStateQuery.php | 2 +- .../typeahead/DifferentialBlockingReviewerDatasource.php | 2 +- .../typeahead/DifferentialExactUserFunctionDatasource.php | 2 +- .../typeahead/DifferentialNoReviewersDatasource.php | 2 +- .../typeahead/DifferentialResponsibleDatasource.php | 2 +- .../typeahead/DifferentialResponsibleUserDatasource.php | 2 +- .../DifferentialResponsibleViewerFunctionDatasource.php | 2 +- .../differential/typeahead/DifferentialReviewerDatasource.php | 2 +- .../typeahead/DifferentialReviewerFunctionDatasource.php | 2 +- .../typeahead/DifferentialRevisionClosedStatusDatasource.php | 2 +- .../typeahead/DifferentialRevisionOpenStatusDatasource.php | 2 +- .../typeahead/DifferentialRevisionStatusDatasource.php | 2 +- src/applications/diffusion/editor/DiffusionCommitEditEngine.php | 2 +- .../diffusion/editor/DiffusionRepositoryEditEngine.php | 2 +- .../diffusion/editor/DiffusionRepositoryIdentityEditor.php | 2 +- src/applications/diffusion/editor/DiffusionURIEditEngine.php | 2 +- src/applications/diffusion/editor/DiffusionURIEditor.php | 2 +- src/applications/diffusion/herald/HeraldCommitAdapter.php | 2 +- src/applications/diffusion/herald/HeraldPreCommitAdapter.php | 2 +- src/applications/diffusion/query/DiffusionCommitHintQuery.php | 2 +- src/applications/diffusion/query/DiffusionCommitQuery.php | 2 +- .../diffusion/query/DiffusionPullLogSearchEngine.php | 2 +- .../diffusion/query/DiffusionRepositoryIdentitySearchEngine.php | 2 +- .../diffusion/query/DiffusionSyncLogSearchEngine.php | 2 +- .../diffusion/typeahead/DiffusionAuditorDatasource.php | 2 +- .../diffusion/typeahead/DiffusionAuditorFunctionDatasource.php | 2 +- .../typeahead/DiffusionIdentityUnassignedDatasource.php | 2 +- src/applications/diffusion/typeahead/DiffusionRefDatasource.php | 2 +- .../diffusion/typeahead/DiffusionRepositoryDatasource.php | 2 +- .../typeahead/DiffusionRepositoryFunctionDatasource.php | 2 +- .../diffusion/typeahead/DiffusionSymbolDatasource.php | 2 +- .../typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php | 2 +- src/applications/diviner/editor/DivinerLiveBookEditor.php | 2 +- src/applications/diviner/phid/DivinerAtomPHIDType.php | 2 +- src/applications/diviner/phid/DivinerBookPHIDType.php | 2 +- src/applications/diviner/query/DivinerAtomQuery.php | 2 +- src/applications/diviner/query/DivinerAtomSearchEngine.php | 2 +- src/applications/diviner/query/DivinerBookQuery.php | 2 +- src/applications/diviner/typeahead/DivinerBookDatasource.php | 2 +- .../doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php | 2 +- .../doorkeeper/query/DoorkeeperExternalObjectQuery.php | 2 +- src/applications/drydock/editor/DrydockBlueprintEditEngine.php | 2 +- src/applications/drydock/editor/DrydockBlueprintEditor.php | 2 +- src/applications/drydock/phid/DrydockAuthorizationPHIDType.php | 2 +- src/applications/drydock/phid/DrydockBlueprintPHIDType.php | 2 +- src/applications/drydock/phid/DrydockLeasePHIDType.php | 2 +- .../drydock/phid/DrydockRepositoryOperationPHIDType.php | 2 +- src/applications/drydock/phid/DrydockResourcePHIDType.php | 2 +- .../drydock/query/DrydockAuthorizationSearchEngine.php | 2 +- src/applications/drydock/query/DrydockBlueprintSearchEngine.php | 2 +- src/applications/drydock/query/DrydockLeaseSearchEngine.php | 2 +- src/applications/drydock/query/DrydockLogSearchEngine.php | 2 +- src/applications/drydock/query/DrydockQuery.php | 2 +- .../drydock/query/DrydockRepositoryOperationSearchEngine.php | 2 +- src/applications/drydock/query/DrydockResourceSearchEngine.php | 2 +- .../drydock/typeahead/DrydockBlueprintDatasource.php | 2 +- src/applications/drydock/typeahead/DrydockLeaseDatasource.php | 2 +- .../drydock/typeahead/DrydockResourceDatasource.php | 2 +- src/applications/feed/query/PhabricatorFeedQuery.php | 2 +- src/applications/feed/query/PhabricatorFeedSearchEngine.php | 2 +- src/applications/feed/query/PhabricatorFeedTransactionQuery.php | 2 +- .../feed/query/PhabricatorFeedTransactionSearchEngine.php | 2 +- src/applications/files/editor/PhabricatorFileEditEngine.php | 2 +- src/applications/files/editor/PhabricatorFileEditor.php | 2 +- src/applications/files/phid/PhabricatorFileFilePHIDType.php | 2 +- src/applications/files/query/PhabricatorFileAttachmentQuery.php | 2 +- src/applications/files/query/PhabricatorFileChunkQuery.php | 2 +- src/applications/files/query/PhabricatorFileQuery.php | 2 +- src/applications/files/query/PhabricatorFileSearchEngine.php | 2 +- src/applications/files/typeahead/PhabricatorIconDatasource.php | 2 +- src/applications/flag/query/PhabricatorFlagQuery.php | 2 +- src/applications/flag/query/PhabricatorFlagSearchEngine.php | 2 +- src/applications/fund/editor/FundBackerEditor.php | 2 +- src/applications/fund/editor/FundInitiativeEditEngine.php | 2 +- src/applications/fund/editor/FundInitiativeEditor.php | 2 +- src/applications/fund/phid/FundBackerPHIDType.php | 2 +- src/applications/fund/phid/FundInitiativePHIDType.php | 2 +- src/applications/fund/query/FundBackerQuery.php | 2 +- src/applications/fund/query/FundBackerSearchEngine.php | 2 +- src/applications/fund/query/FundInitiativeQuery.php | 2 +- src/applications/fund/query/FundInitiativeSearchEngine.php | 2 +- .../harbormaster/editor/HarbormasterBuildEditEngine.php | 2 +- .../harbormaster/editor/HarbormasterBuildPlanEditEngine.php | 2 +- .../harbormaster/editor/HarbormasterBuildPlanEditor.php | 2 +- .../harbormaster/editor/HarbormasterBuildStepEditEngine.php | 2 +- .../harbormaster/editor/HarbormasterBuildStepEditor.php | 2 +- .../harbormaster/editor/HarbormasterBuildTransactionEditor.php | 2 +- .../harbormaster/editor/HarbormasterBuildableEditEngine.php | 2 +- .../editor/HarbormasterBuildableTransactionEditor.php | 2 +- .../harbormaster/phid/HarbormasterBuildArtifactPHIDType.php | 2 +- .../harbormaster/phid/HarbormasterBuildLogPHIDType.php | 2 +- .../harbormaster/phid/HarbormasterBuildPHIDType.php | 2 +- .../harbormaster/phid/HarbormasterBuildPlanPHIDType.php | 2 +- .../harbormaster/phid/HarbormasterBuildStepPHIDType.php | 2 +- .../harbormaster/phid/HarbormasterBuildTargetPHIDType.php | 2 +- .../harbormaster/phid/HarbormasterBuildablePHIDType.php | 2 +- .../harbormaster/query/HarbormasterArtifactSearchEngine.php | 2 +- .../harbormaster/query/HarbormasterBuildArtifactQuery.php | 2 +- .../harbormaster/query/HarbormasterBuildLogQuery.php | 2 +- .../harbormaster/query/HarbormasterBuildLogSearchEngine.php | 2 +- .../harbormaster/query/HarbormasterBuildMessageQuery.php | 2 +- .../harbormaster/query/HarbormasterBuildPlanQuery.php | 2 +- .../harbormaster/query/HarbormasterBuildPlanSearchEngine.php | 2 +- src/applications/harbormaster/query/HarbormasterBuildQuery.php | 2 +- .../harbormaster/query/HarbormasterBuildSearchEngine.php | 2 +- .../harbormaster/query/HarbormasterBuildStepQuery.php | 2 +- .../harbormaster/query/HarbormasterBuildStepSearchEngine.php | 2 +- .../harbormaster/query/HarbormasterBuildTargetQuery.php | 2 +- .../harbormaster/query/HarbormasterBuildTargetSearchEngine.php | 2 +- .../harbormaster/query/HarbormasterBuildUnitMessageQuery.php | 2 +- .../harbormaster/query/HarbormasterBuildableQuery.php | 2 +- .../harbormaster/query/HarbormasterBuildableSearchEngine.php | 2 +- .../typeahead/HarbormasterBuildDependencyDatasource.php | 2 +- .../harbormaster/typeahead/HarbormasterBuildPlanDatasource.php | 2 +- .../typeahead/HarbormasterBuildStatusDatasource.php | 2 +- src/applications/herald/adapter/HeraldRuleAdapter.php | 2 +- src/applications/herald/editor/HeraldRuleEditor.php | 2 +- src/applications/herald/editor/HeraldWebhookEditEngine.php | 2 +- src/applications/herald/editor/HeraldWebhookEditor.php | 2 +- src/applications/herald/phid/HeraldRulePHIDType.php | 2 +- src/applications/herald/phid/HeraldTranscriptPHIDType.php | 2 +- src/applications/herald/phid/HeraldWebhookPHIDType.php | 2 +- src/applications/herald/phid/HeraldWebhookRequestPHIDType.php | 2 +- src/applications/herald/query/HeraldRuleQuery.php | 2 +- src/applications/herald/query/HeraldRuleSearchEngine.php | 2 +- src/applications/herald/query/HeraldTranscriptQuery.php | 2 +- src/applications/herald/query/HeraldTranscriptSearchEngine.php | 2 +- src/applications/herald/query/HeraldWebhookQuery.php | 2 +- src/applications/herald/query/HeraldWebhookRequestQuery.php | 2 +- src/applications/herald/query/HeraldWebhookSearchEngine.php | 2 +- src/applications/herald/typeahead/HeraldAdapterDatasource.php | 2 +- src/applications/herald/typeahead/HeraldRuleDatasource.php | 2 +- src/applications/herald/typeahead/HeraldRuleTypeDatasource.php | 2 +- src/applications/herald/typeahead/HeraldWebhookDatasource.php | 2 +- src/applications/legalpad/editor/LegalpadDocumentEditEngine.php | 2 +- src/applications/legalpad/editor/LegalpadDocumentEditor.php | 2 +- .../legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php | 2 +- .../phid/PhabricatorLegalpadDocumentSignaturePHIDType.php | 2 +- src/applications/legalpad/query/LegalpadDocumentQuery.php | 2 +- .../legalpad/query/LegalpadDocumentSearchEngine.php | 2 +- .../legalpad/query/LegalpadDocumentSignatureQuery.php | 2 +- .../legalpad/query/LegalpadDocumentSignatureSearchEngine.php | 2 +- .../legalpad/typeahead/LegalpadDocumentDatasource.php | 2 +- src/applications/macro/editor/PhabricatorMacroEditEngine.php | 2 +- src/applications/macro/editor/PhabricatorMacroEditor.php | 2 +- src/applications/macro/phid/PhabricatorMacroMacroPHIDType.php | 2 +- src/applications/macro/query/PhabricatorMacroQuery.php | 2 +- src/applications/macro/query/PhabricatorMacroSearchEngine.php | 2 +- src/applications/macro/typeahead/PhabricatorEmojiDatasource.php | 2 +- src/applications/macro/typeahead/PhabricatorMacroDatasource.php | 2 +- src/applications/maniphest/editor/ManiphestEditEngine.php | 2 +- .../maniphest/editor/ManiphestTransactionEditor.php | 2 +- .../maniphest/herald/HeraldManiphestTaskAdapter.php | 2 +- src/applications/maniphest/phid/ManiphestTaskPHIDType.php | 2 +- src/applications/maniphest/query/ManiphestTaskQuery.php | 2 +- src/applications/maniphest/query/ManiphestTaskSearchEngine.php | 2 +- .../maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php | 2 +- .../maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php | 2 +- .../maniphest/typeahead/ManiphestTaskPriorityDatasource.php | 2 +- .../maniphest/typeahead/ManiphestTaskStatusDatasource.php | 2 +- .../maniphest/typeahead/ManiphestTaskSubtypeDatasource.php | 2 +- .../meta/editor/PhabricatorApplicationEditEngine.php | 2 +- src/applications/meta/editor/PhabricatorApplicationEditor.php | 2 +- .../meta/phid/PhabricatorApplicationApplicationPHIDType.php | 2 +- src/applications/meta/query/PhabricatorAppSearchEngine.php | 2 +- .../meta/typeahead/PhabricatorApplicationDatasource.php | 2 +- .../metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php | 2 +- .../metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php | 2 +- .../metamta/phid/PhabricatorMetaMTAMailPHIDType.php | 2 +- .../metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php | 2 +- .../metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php | 2 +- src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php | 2 +- .../metamta/query/PhabricatorMetaMTAMailSearchEngine.php | 2 +- .../typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php | 2 +- .../metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php | 2 +- .../typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php | 2 +- .../notification/query/PhabricatorNotificationQuery.php | 2 +- .../notification/query/PhabricatorNotificationSearchEngine.php | 2 +- src/applications/nuance/editor/NuanceItemEditor.php | 2 +- src/applications/nuance/editor/NuanceQueueEditEngine.php | 2 +- src/applications/nuance/editor/NuanceQueueEditor.php | 2 +- src/applications/nuance/editor/NuanceSourceEditEngine.php | 2 +- src/applications/nuance/editor/NuanceSourceEditor.php | 2 +- src/applications/nuance/phid/NuanceImportCursorPHIDType.php | 2 +- src/applications/nuance/phid/NuanceItemPHIDType.php | 2 +- src/applications/nuance/phid/NuanceQueuePHIDType.php | 2 +- src/applications/nuance/phid/NuanceSourcePHIDType.php | 2 +- src/applications/nuance/query/NuanceItemSearchEngine.php | 2 +- src/applications/nuance/query/NuanceQuery.php | 2 +- src/applications/nuance/query/NuanceQueueSearchEngine.php | 2 +- src/applications/nuance/query/NuanceSourceSearchEngine.php | 2 +- src/applications/nuance/typeahead/NuanceQueueDatasource.php | 2 +- .../oauthserver/editor/PhabricatorOAuthServerEditEngine.php | 2 +- .../oauthserver/editor/PhabricatorOAuthServerEditor.php | 2 +- .../phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php | 2 +- .../oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php | 2 +- .../query/PhabricatorOAuthClientAuthorizationQuery.php | 2 +- .../oauthserver/query/PhabricatorOAuthServerClientQuery.php | 2 +- .../query/PhabricatorOAuthServerClientSearchEngine.php | 2 +- .../owners/editor/PhabricatorOwnersPackageEditEngine.php | 2 +- .../owners/editor/PhabricatorOwnersPackageTransactionEditor.php | 2 +- .../owners/phid/PhabricatorOwnersPackagePHIDType.php | 2 +- src/applications/owners/query/PhabricatorOwnersPackageQuery.php | 2 +- .../owners/query/PhabricatorOwnersPackageSearchEngine.php | 2 +- .../owners/typeahead/PhabricatorOwnersPackageDatasource.php | 2 +- .../typeahead/PhabricatorOwnersPackageFunctionDatasource.php | 2 +- .../typeahead/PhabricatorOwnersPackageOwnerDatasource.php | 2 +- .../packages/editor/PhabricatorPackagesEditEngine.php | 2 +- src/applications/packages/editor/PhabricatorPackagesEditor.php | 2 +- .../packages/phid/PhabricatorPackagesPackagePHIDType.php | 2 +- .../packages/phid/PhabricatorPackagesPublisherPHIDType.php | 2 +- .../packages/phid/PhabricatorPackagesVersionPHIDType.php | 2 +- .../packages/query/PhabricatorPackagesPackageSearchEngine.php | 2 +- .../packages/query/PhabricatorPackagesPublisherSearchEngine.php | 2 +- src/applications/packages/query/PhabricatorPackagesQuery.php | 2 +- .../packages/query/PhabricatorPackagesVersionSearchEngine.php | 2 +- .../packages/typeahead/PhabricatorPackagesPackageDatasource.php | 2 +- .../typeahead/PhabricatorPackagesPublisherDatasource.php | 2 +- .../passphrase/editor/PassphraseCredentialTransactionEditor.php | 2 +- .../passphrase/phid/PassphraseCredentialPHIDType.php | 2 +- src/applications/passphrase/query/PassphraseCredentialQuery.php | 2 +- .../passphrase/query/PassphraseCredentialSearchEngine.php | 2 +- src/applications/paste/editor/PhabricatorPasteEditEngine.php | 2 +- src/applications/paste/editor/PhabricatorPasteEditor.php | 2 +- src/applications/paste/phid/PhabricatorPastePastePHIDType.php | 2 +- src/applications/paste/query/PhabricatorPasteQuery.php | 2 +- src/applications/paste/query/PhabricatorPasteSearchEngine.php | 2 +- .../paste/typeahead/PasteLanguageSelectDatasource.php | 2 +- src/applications/people/editor/PhabricatorUserEditEngine.php | 2 +- .../people/editor/PhabricatorUserTransactionEditor.php | 2 +- .../people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php | 2 +- .../people/phid/PhabricatorPeopleExternalPHIDType.php | 2 +- .../people/phid/PhabricatorPeopleUserEmailPHIDType.php | 2 +- src/applications/people/phid/PhabricatorPeopleUserPHIDType.php | 2 +- src/applications/people/query/PhabricatorPeopleLogQuery.php | 2 +- .../people/query/PhabricatorPeopleLogSearchEngine.php | 2 +- src/applications/people/query/PhabricatorPeopleQuery.php | 2 +- src/applications/people/query/PhabricatorPeopleSearchEngine.php | 2 +- .../people/query/PhabricatorPeopleUserEmailQuery.php | 2 +- .../people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php | 2 +- .../people/typeahead/PhabricatorPeopleDatasource.php | 2 +- .../people/typeahead/PhabricatorPeopleNoOwnerDatasource.php | 2 +- .../people/typeahead/PhabricatorUserLogTypeDatasource.php | 2 +- .../people/typeahead/PhabricatorViewerDatasource.php | 2 +- src/applications/phame/editor/PhameBlogEditEngine.php | 2 +- src/applications/phame/editor/PhameBlogEditor.php | 2 +- src/applications/phame/editor/PhamePostEditEngine.php | 2 +- src/applications/phame/editor/PhamePostEditor.php | 2 +- src/applications/phame/herald/HeraldPhameBlogAdapter.php | 2 +- src/applications/phame/herald/HeraldPhamePostAdapter.php | 2 +- src/applications/phame/phid/PhabricatorPhameBlogPHIDType.php | 2 +- src/applications/phame/phid/PhabricatorPhamePostPHIDType.php | 2 +- src/applications/phame/query/PhameBlogSearchEngine.php | 2 +- src/applications/phame/query/PhamePostSearchEngine.php | 2 +- src/applications/phame/typeahead/PhameBlogDatasource.php | 2 +- .../phid/PhabricatorMetaMTAApplicationEmailPHIDType.php | 2 +- src/applications/phlux/editor/PhluxVariableEditor.php | 2 +- src/applications/phlux/phid/PhluxVariablePHIDType.php | 2 +- src/applications/phlux/query/PhluxVariableQuery.php | 2 +- src/applications/pholio/editor/PholioMockEditor.php | 2 +- src/applications/pholio/herald/HeraldPholioMockAdapter.php | 2 +- src/applications/pholio/phid/PholioImagePHIDType.php | 2 +- src/applications/pholio/phid/PholioMockPHIDType.php | 2 +- src/applications/pholio/query/PholioImageQuery.php | 2 +- src/applications/pholio/query/PholioMockQuery.php | 2 +- src/applications/pholio/query/PholioMockSearchEngine.php | 2 +- src/applications/phortune/editor/PhortuneAccountEditEngine.php | 2 +- src/applications/phortune/editor/PhortuneAccountEditor.php | 2 +- .../phortune/editor/PhortuneAccountEmailEditEngine.php | 2 +- src/applications/phortune/editor/PhortuneAccountEmailEditor.php | 2 +- src/applications/phortune/editor/PhortuneCartEditor.php | 2 +- src/applications/phortune/editor/PhortuneMerchantEditEngine.php | 2 +- src/applications/phortune/editor/PhortuneMerchantEditor.php | 2 +- .../phortune/editor/PhortunePaymentMethodEditor.php | 2 +- .../phortune/editor/PhortunePaymentProviderConfigEditor.php | 2 +- src/applications/phortune/editor/PhortuneSubscriptionEditor.php | 2 +- src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php | 2 +- src/applications/phortune/phid/PhortuneAccountPHIDType.php | 2 +- src/applications/phortune/phid/PhortuneCartPHIDType.php | 2 +- src/applications/phortune/phid/PhortuneChargePHIDType.php | 2 +- src/applications/phortune/phid/PhortuneMerchantPHIDType.php | 2 +- .../phortune/phid/PhortunePaymentMethodPHIDType.php | 2 +- .../phortune/phid/PhortunePaymentProviderPHIDType.php | 2 +- src/applications/phortune/phid/PhortuneProductPHIDType.php | 2 +- src/applications/phortune/phid/PhortunePurchasePHIDType.php | 2 +- src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php | 2 +- src/applications/phortune/query/PhortuneAccountEmailQuery.php | 2 +- src/applications/phortune/query/PhortuneAccountQuery.php | 2 +- src/applications/phortune/query/PhortuneCartQuery.php | 2 +- src/applications/phortune/query/PhortuneCartSearchEngine.php | 2 +- src/applications/phortune/query/PhortuneChargeQuery.php | 2 +- src/applications/phortune/query/PhortuneChargeSearchEngine.php | 2 +- src/applications/phortune/query/PhortuneMerchantQuery.php | 2 +- .../phortune/query/PhortuneMerchantSearchEngine.php | 2 +- src/applications/phortune/query/PhortunePaymentMethodQuery.php | 2 +- .../phortune/query/PhortunePaymentProviderConfigQuery.php | 2 +- src/applications/phortune/query/PhortuneProductQuery.php | 2 +- src/applications/phortune/query/PhortunePurchaseQuery.php | 2 +- src/applications/phortune/query/PhortuneSubscriptionQuery.php | 2 +- .../phortune/query/PhortuneSubscriptionSearchEngine.php | 2 +- src/applications/phrequent/query/PhrequentSearchEngine.php | 2 +- src/applications/phrequent/query/PhrequentUserTimeQuery.php | 2 +- .../phriction/editor/PhrictionDocumentEditEngine.php | 2 +- .../phriction/editor/PhrictionTransactionEditor.php | 2 +- .../phriction/herald/PhrictionDocumentHeraldAdapter.php | 2 +- src/applications/phriction/phid/PhrictionContentPHIDType.php | 2 +- src/applications/phriction/phid/PhrictionDocumentPHIDType.php | 2 +- src/applications/phriction/query/PhrictionContentQuery.php | 2 +- .../phriction/query/PhrictionContentSearchEngine.php | 2 +- src/applications/phriction/query/PhrictionDocumentQuery.php | 2 +- .../phriction/query/PhrictionDocumentSearchEngine.php | 2 +- .../phriction/typeahead/PhrictionDocumentDatasource.php | 2 +- src/applications/phurl/editor/PhabricatorPhurlURLEditEngine.php | 2 +- src/applications/phurl/editor/PhabricatorPhurlURLEditor.php | 2 +- src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php | 2 +- src/applications/phurl/query/PhabricatorPhurlURLQuery.php | 2 +- .../phurl/query/PhabricatorPhurlURLSearchEngine.php | 2 +- .../phurl/typeahead/PhabricatorPhurlURLDatasource.php | 2 +- .../policy/phid/PhabricatorPolicyPHIDTypePolicy.php | 2 +- src/applications/policy/query/PhabricatorPolicyQuery.php | 2 +- src/applications/ponder/editor/PonderEditor.php | 2 +- src/applications/ponder/editor/PonderQuestionEditEngine.php | 2 +- src/applications/ponder/herald/HeraldPonderQuestionAdapter.php | 2 +- src/applications/ponder/phid/PonderAnswerPHIDType.php | 2 +- src/applications/ponder/phid/PonderQuestionPHIDType.php | 2 +- src/applications/ponder/query/PonderAnswerQuery.php | 2 +- src/applications/ponder/query/PonderQuestionQuery.php | 2 +- src/applications/ponder/query/PonderQuestionSearchEngine.php | 2 +- .../editor/PhabricatorProjectColumnTransactionEditor.php | 2 +- .../project/editor/PhabricatorProjectTransactionEditor.php | 2 +- .../project/editor/PhabricatorProjectTriggerEditor.php | 2 +- .../project/engine/PhabricatorProjectEditEngine.php | 2 +- .../project/herald/PhabricatorProjectHeraldAdapter.php | 2 +- .../project/phid/PhabricatorProjectColumnPHIDType.php | 2 +- .../project/phid/PhabricatorProjectProjectPHIDType.php | 2 +- .../project/phid/PhabricatorProjectTriggerPHIDType.php | 2 +- .../project/query/PhabricatorProjectColumnPositionQuery.php | 2 +- .../project/query/PhabricatorProjectColumnQuery.php | 2 +- .../project/query/PhabricatorProjectColumnSearchEngine.php | 2 +- src/applications/project/query/PhabricatorProjectQuery.php | 2 +- .../project/query/PhabricatorProjectSearchEngine.php | 2 +- .../project/query/PhabricatorProjectTriggerQuery.php | 2 +- .../project/query/PhabricatorProjectTriggerSearchEngine.php | 2 +- .../project/typeahead/PhabricatorProjectDatasource.php | 2 +- .../typeahead/PhabricatorProjectLogicalAncestorDatasource.php | 2 +- .../project/typeahead/PhabricatorProjectLogicalDatasource.php | 2 +- .../typeahead/PhabricatorProjectLogicalOnlyDatasource.php | 2 +- .../typeahead/PhabricatorProjectLogicalOrNotDatasource.php | 2 +- .../typeahead/PhabricatorProjectLogicalUserDatasource.php | 2 +- .../typeahead/PhabricatorProjectLogicalViewerDatasource.php | 2 +- .../project/typeahead/PhabricatorProjectMembersDatasource.php | 2 +- .../typeahead/PhabricatorProjectNoProjectsDatasource.php | 2 +- .../project/typeahead/PhabricatorProjectSubtypeDatasource.php | 2 +- .../typeahead/PhabricatorProjectUserFunctionDatasource.php | 2 +- .../repository/editor/PhabricatorRepositoryEditor.php | 2 +- .../engine/PhabricatorRepositoryIdentityEditEngine.php | 2 +- .../repository/phid/PhabricatorRepositoryCommitPHIDType.php | 2 +- .../repository/phid/PhabricatorRepositoryIdentityPHIDType.php | 2 +- .../repository/phid/PhabricatorRepositoryPullEventPHIDType.php | 2 +- .../repository/phid/PhabricatorRepositoryPushEventPHIDType.php | 2 +- .../repository/phid/PhabricatorRepositoryPushLogPHIDType.php | 2 +- .../repository/phid/PhabricatorRepositoryRefCursorPHIDType.php | 2 +- .../repository/phid/PhabricatorRepositoryRepositoryPHIDType.php | 2 +- .../repository/phid/PhabricatorRepositorySyncEventPHIDType.php | 2 +- .../repository/phid/PhabricatorRepositoryURIPHIDType.php | 2 +- .../repository/query/PhabricatorRepositoryGitLFSRefQuery.php | 2 +- .../repository/query/PhabricatorRepositoryIdentityQuery.php | 2 +- .../repository/query/PhabricatorRepositoryPullEventQuery.php | 2 +- .../repository/query/PhabricatorRepositoryPushEventQuery.php | 2 +- .../repository/query/PhabricatorRepositoryPushLogQuery.php | 2 +- .../query/PhabricatorRepositoryPushLogSearchEngine.php | 2 +- .../repository/query/PhabricatorRepositoryQuery.php | 2 +- .../repository/query/PhabricatorRepositoryRefCursorQuery.php | 2 +- .../repository/query/PhabricatorRepositorySearchEngine.php | 2 +- .../repository/query/PhabricatorRepositorySyncEventQuery.php | 2 +- .../repository/query/PhabricatorRepositoryURIQuery.php | 2 +- .../search/editor/PhabricatorProfileMenuEditEngine.php | 2 +- src/applications/search/editor/PhabricatorProfileMenuEditor.php | 2 +- .../search/phidtype/PhabricatorProfileMenuItemPHIDType.php | 2 +- .../search/query/PhabricatorNamedQueryConfigQuery.php | 2 +- src/applications/search/query/PhabricatorNamedQueryQuery.php | 2 +- .../query/PhabricatorProfileMenuItemConfigurationQuery.php | 2 +- src/applications/search/query/PhabricatorSavedQueryQuery.php | 2 +- .../search/query/PhabricatorSearchApplicationSearchEngine.php | 2 +- .../search/query/PhabricatorSearchDocumentQuery.php | 2 +- .../search/typeahead/PhabricatorSearchDatasource.php | 2 +- .../typeahead/PhabricatorSearchDocumentTypeDatasource.php | 2 +- .../settings/editor/PhabricatorSettingsEditEngine.php | 2 +- .../settings/editor/PhabricatorUserPreferencesEditor.php | 2 +- .../settings/phid/PhabricatorUserPreferencesPHIDType.php | 2 +- .../settings/query/PhabricatorUserPreferencesQuery.php | 2 +- src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php | 2 +- .../slowvote/phid/PhabricatorSlowvotePollPHIDType.php | 2 +- src/applications/slowvote/query/PhabricatorSlowvoteQuery.php | 2 +- .../slowvote/query/PhabricatorSlowvoteSearchEngine.php | 2 +- .../spaces/editor/PhabricatorSpacesNamespaceEditor.php | 2 +- .../spaces/phid/PhabricatorSpacesNamespacePHIDType.php | 2 +- .../spaces/query/PhabricatorSpacesNamespaceQuery.php | 2 +- .../spaces/query/PhabricatorSpacesNamespaceSearchEngine.php | 2 +- .../spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php | 2 +- src/applications/tokens/phid/PhabricatorTokenTokenPHIDType.php | 2 +- src/applications/tokens/query/PhabricatorTokenGivenQuery.php | 2 +- src/applications/tokens/query/PhabricatorTokenQuery.php | 2 +- src/applications/tokens/query/PhabricatorTokenReceiverQuery.php | 2 +- .../editor/PhabricatorEditEngineConfigurationEditEngine.php | 2 +- .../editor/PhabricatorEditEngineConfigurationEditor.php | 2 +- .../PhabricatorApplicationTransactionTransactionPHIDType.php | 2 +- .../phid/PhabricatorEditEngineConfigurationPHIDType.php | 2 +- .../query/PhabricatorEditEngineConfigurationQuery.php | 2 +- .../query/PhabricatorEditEngineConfigurationSearchEngine.php | 2 +- .../transactions/query/PhabricatorEditEngineQuery.php | 2 +- .../transactions/query/PhabricatorEditEngineSearchEngine.php | 2 +- .../transactions/typeahead/PhabricatorEditEngineDatasource.php | 2 +- .../typeahead/PhabricatorTransactionsObjectTypeDatasource.php | 2 +- .../__tests__/PhabricatorTypeaheadTestNumbersDatasource.php | 2 +- src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php | 2 +- .../xhprof/query/PhabricatorXHProfSampleSearchEngine.php | 2 +- .../daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php | 2 +- .../daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php | 2 +- .../daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php | 2 +- .../daemon/workers/query/PhabricatorWorkerBulkJobQuery.php | 2 +- .../workers/query/PhabricatorWorkerBulkJobSearchEngine.php | 2 +- 571 files changed, 571 insertions(+), 571 deletions(-) diff --git a/src/applications/almanac/editor/AlmanacBindingEditEngine.php b/src/applications/almanac/editor/AlmanacBindingEditEngine.php index 66db7fcbab..8de74b471b 100644 --- a/src/applications/almanac/editor/AlmanacBindingEditEngine.php +++ b/src/applications/almanac/editor/AlmanacBindingEditEngine.php @@ -36,7 +36,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacDeviceEditEngine.php b/src/applications/almanac/editor/AlmanacDeviceEditEngine.php index d0c2b48f7a..e6d1b55d2e 100644 --- a/src/applications/almanac/editor/AlmanacDeviceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacDeviceEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacEditor.php b/src/applications/almanac/editor/AlmanacEditor.php index 54c32c79cd..4f92a748d2 100644 --- a/src/applications/almanac/editor/AlmanacEditor.php +++ b/src/applications/almanac/editor/AlmanacEditor.php @@ -4,7 +4,7 @@ abstract class AlmanacEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php b/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php index ca57113bf2..0cf3df229e 100644 --- a/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php @@ -36,7 +36,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php b/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php index cf6ad36c00..fe22d4f14c 100644 --- a/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacNetworkEditEngine.php b/src/applications/almanac/editor/AlmanacNetworkEditEngine.php index 027b2a9aa0..600ead5b05 100644 --- a/src/applications/almanac/editor/AlmanacNetworkEditEngine.php +++ b/src/applications/almanac/editor/AlmanacNetworkEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacPropertyEditEngine.php b/src/applications/almanac/editor/AlmanacPropertyEditEngine.php index 38139f79fa..999b7af9f4 100644 --- a/src/applications/almanac/editor/AlmanacPropertyEditEngine.php +++ b/src/applications/almanac/editor/AlmanacPropertyEditEngine.php @@ -35,7 +35,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacServiceEditEngine.php b/src/applications/almanac/editor/AlmanacServiceEditEngine.php index b63075543f..e0c4086d2e 100644 --- a/src/applications/almanac/editor/AlmanacServiceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacServiceEditEngine.php @@ -33,7 +33,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/phid/AlmanacBindingPHIDType.php b/src/applications/almanac/phid/AlmanacBindingPHIDType.php index db469690cd..5f0da9b491 100644 --- a/src/applications/almanac/phid/AlmanacBindingPHIDType.php +++ b/src/applications/almanac/phid/AlmanacBindingPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacDevicePHIDType.php b/src/applications/almanac/phid/AlmanacDevicePHIDType.php index 26c88a7a86..31be5eade2 100644 --- a/src/applications/almanac/phid/AlmanacDevicePHIDType.php +++ b/src/applications/almanac/phid/AlmanacDevicePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacInterfacePHIDType.php b/src/applications/almanac/phid/AlmanacInterfacePHIDType.php index c7fe73183a..50b1d3ca4d 100644 --- a/src/applications/almanac/phid/AlmanacInterfacePHIDType.php +++ b/src/applications/almanac/phid/AlmanacInterfacePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacNamespacePHIDType.php b/src/applications/almanac/phid/AlmanacNamespacePHIDType.php index aa0b8fc4ef..71a8a6b475 100644 --- a/src/applications/almanac/phid/AlmanacNamespacePHIDType.php +++ b/src/applications/almanac/phid/AlmanacNamespacePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacNetworkPHIDType.php b/src/applications/almanac/phid/AlmanacNetworkPHIDType.php index 2264ce0e5f..3aa4f1a24b 100644 --- a/src/applications/almanac/phid/AlmanacNetworkPHIDType.php +++ b/src/applications/almanac/phid/AlmanacNetworkPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacServicePHIDType.php b/src/applications/almanac/phid/AlmanacServicePHIDType.php index a64a229e94..fdc48706d4 100644 --- a/src/applications/almanac/phid/AlmanacServicePHIDType.php +++ b/src/applications/almanac/phid/AlmanacServicePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/query/AlmanacBindingSearchEngine.php b/src/applications/almanac/query/AlmanacBindingSearchEngine.php index fbeb7644b9..053cc2cd34 100644 --- a/src/applications/almanac/query/AlmanacBindingSearchEngine.php +++ b/src/applications/almanac/query/AlmanacBindingSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacDeviceQuery.php b/src/applications/almanac/query/AlmanacDeviceQuery.php index 42796c4839..47006cb756 100644 --- a/src/applications/almanac/query/AlmanacDeviceQuery.php +++ b/src/applications/almanac/query/AlmanacDeviceQuery.php @@ -148,7 +148,7 @@ public function getBuiltinOrders() { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacDeviceSearchEngine.php b/src/applications/almanac/query/AlmanacDeviceSearchEngine.php index 9f98abb292..86633ac870 100644 --- a/src/applications/almanac/query/AlmanacDeviceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacDeviceSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacInterfaceQuery.php b/src/applications/almanac/query/AlmanacInterfaceQuery.php index dbbc0cd53e..88eb9c2d74 100644 --- a/src/applications/almanac/query/AlmanacInterfaceQuery.php +++ b/src/applications/almanac/query/AlmanacInterfaceQuery.php @@ -169,7 +169,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function getBuiltinOrders() { diff --git a/src/applications/almanac/query/AlmanacInterfaceSearchEngine.php b/src/applications/almanac/query/AlmanacInterfaceSearchEngine.php index 9a3c5bc8f0..740affd526 100644 --- a/src/applications/almanac/query/AlmanacInterfaceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacInterfaceSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacNamespaceQuery.php b/src/applications/almanac/query/AlmanacNamespaceQuery.php index e6c99bf6ea..58ba54f74a 100644 --- a/src/applications/almanac/query/AlmanacNamespaceQuery.php +++ b/src/applications/almanac/query/AlmanacNamespaceQuery.php @@ -92,7 +92,7 @@ public function getBuiltinOrders() { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php b/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php index 14a96d22a0..a4658c5f33 100644 --- a/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacNetworkQuery.php b/src/applications/almanac/query/AlmanacNetworkQuery.php index 7af9db8585..a688fedda9 100644 --- a/src/applications/almanac/query/AlmanacNetworkQuery.php +++ b/src/applications/almanac/query/AlmanacNetworkQuery.php @@ -64,7 +64,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php b/src/applications/almanac/query/AlmanacNetworkSearchEngine.php index 53f9b24e44..5938fd44c7 100644 --- a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php +++ b/src/applications/almanac/query/AlmanacNetworkSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacPropertyQuery.php b/src/applications/almanac/query/AlmanacPropertyQuery.php index 4261c70fec..8c93d4d70b 100644 --- a/src/applications/almanac/query/AlmanacPropertyQuery.php +++ b/src/applications/almanac/query/AlmanacPropertyQuery.php @@ -99,7 +99,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacQuery.php b/src/applications/almanac/query/AlmanacQuery.php index b5b6146c7f..8b2bc894e8 100644 --- a/src/applications/almanac/query/AlmanacQuery.php +++ b/src/applications/almanac/query/AlmanacQuery.php @@ -55,7 +55,7 @@ protected function didFilterPage(array $objects) { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacServiceSearchEngine.php b/src/applications/almanac/query/AlmanacServiceSearchEngine.php index a2fd9c23b7..f9786a87d2 100644 --- a/src/applications/almanac/query/AlmanacServiceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacServiceSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php b/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php index dca5dd986b..8666265d27 100644 --- a/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php +++ b/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function loadResults() { diff --git a/src/applications/almanac/typeahead/AlmanacServiceDatasource.php b/src/applications/almanac/typeahead/AlmanacServiceDatasource.php index 4413465125..d5f990ce47 100644 --- a/src/applications/almanac/typeahead/AlmanacServiceDatasource.php +++ b/src/applications/almanac/typeahead/AlmanacServiceDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function loadResults() { diff --git a/src/applications/almanac/typeahead/AlmanacServiceTypeDatasource.php b/src/applications/almanac/typeahead/AlmanacServiceTypeDatasource.php index 5314084244..b0b81baed9 100644 --- a/src/applications/almanac/typeahead/AlmanacServiceTypeDatasource.php +++ b/src/applications/almanac/typeahead/AlmanacServiceTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function loadResults() { diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php index 9c2a5d7e96..c6fa129e41 100644 --- a/src/applications/audit/editor/PhabricatorAuditEditor.php +++ b/src/applications/audit/editor/PhabricatorAuditEditor.php @@ -22,7 +22,7 @@ public function getRawPatch() { } public function getEditorApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/audit/query/DiffusionInternalCommitSearchEngine.php b/src/applications/audit/query/DiffusionInternalCommitSearchEngine.php index 45780140fb..ece9d1d48c 100644 --- a/src/applications/audit/query/DiffusionInternalCommitSearchEngine.php +++ b/src/applications/audit/query/DiffusionInternalCommitSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index 7251857c7f..a6a2ee071e 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php b/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php index 5b1a059b2f..a4f1ab0cf2 100644 --- a/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php +++ b/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function newEditableObject() { diff --git a/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php b/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php index 9dfb569e89..d6c872743e 100644 --- a/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php @@ -4,7 +4,7 @@ final class PhabricatorAuthContactNumberEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthFactorProviderEditEngine.php b/src/applications/auth/editor/PhabricatorAuthFactorProviderEditEngine.php index ab74350cc9..ecccfb6026 100644 --- a/src/applications/auth/editor/PhabricatorAuthFactorProviderEditEngine.php +++ b/src/applications/auth/editor/PhabricatorAuthFactorProviderEditEngine.php @@ -24,7 +24,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function setProviderFactor(PhabricatorAuthFactor $factor) { diff --git a/src/applications/auth/editor/PhabricatorAuthFactorProviderEditor.php b/src/applications/auth/editor/PhabricatorAuthFactorProviderEditor.php index 144f275391..7ee30a0ece 100644 --- a/src/applications/auth/editor/PhabricatorAuthFactorProviderEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthFactorProviderEditor.php @@ -4,7 +4,7 @@ final class PhabricatorAuthFactorProviderEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php b/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php index 0a9aa32de4..3055201a8c 100644 --- a/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php +++ b/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php @@ -24,7 +24,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function setMessageType(PhabricatorAuthMessageType $type) { diff --git a/src/applications/auth/editor/PhabricatorAuthMessageEditor.php b/src/applications/auth/editor/PhabricatorAuthMessageEditor.php index 56e8e716cd..24bc235680 100644 --- a/src/applications/auth/editor/PhabricatorAuthMessageEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthMessageEditor.php @@ -4,7 +4,7 @@ final class PhabricatorAuthMessageEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthPasswordEditor.php b/src/applications/auth/editor/PhabricatorAuthPasswordEditor.php index 0867d22b87..447808f6c9 100644 --- a/src/applications/auth/editor/PhabricatorAuthPasswordEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthPasswordEditor.php @@ -15,7 +15,7 @@ public function getOldHasher() { } public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthProviderConfigEditor.php b/src/applications/auth/editor/PhabricatorAuthProviderConfigEditor.php index 1e75edfbf0..4f66d36d04 100644 --- a/src/applications/auth/editor/PhabricatorAuthProviderConfigEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthProviderConfigEditor.php @@ -4,7 +4,7 @@ final class PhabricatorAuthProviderConfigEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php b/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php index 3f178c9855..b8114a257f 100644 --- a/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php @@ -15,7 +15,7 @@ public function getIsAdministrativeEdit() { } public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php b/src/applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php index 2819c84572..c7531e61b0 100644 --- a/src/applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php b/src/applications/auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php index f0f9f572e8..23d932cfb9 100644 --- a/src/applications/auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php b/src/applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php index bf79ea8743..00b9cd9c6e 100644 --- a/src/applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthChallengePHIDType.php b/src/applications/auth/phid/PhabricatorAuthChallengePHIDType.php index 2d2fea26b6..23b2e485c2 100644 --- a/src/applications/auth/phid/PhabricatorAuthChallengePHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthChallengePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php b/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php index 8a4953f4d3..e883aaa5a6 100644 --- a/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthInvitePHIDType.php b/src/applications/auth/phid/PhabricatorAuthInvitePHIDType.php index b633e10eab..e22793990d 100644 --- a/src/applications/auth/phid/PhabricatorAuthInvitePHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthInvitePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php b/src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php index 005e363b08..c1cd101060 100644 --- a/src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php b/src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php index 8433df8824..b0256a1bf6 100644 --- a/src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php b/src/applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php index b09bdb8b80..e18475ceb9 100644 --- a/src/applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthSessionPHIDType.php b/src/applications/auth/phid/PhabricatorAuthSessionPHIDType.php index e031c4ae88..ae67f09433 100644 --- a/src/applications/auth/phid/PhabricatorAuthSessionPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthSessionPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/query/PhabricatorAuthChallengeQuery.php b/src/applications/auth/query/PhabricatorAuthChallengeQuery.php index c6abead11f..9b0b776d0d 100644 --- a/src/applications/auth/query/PhabricatorAuthChallengeQuery.php +++ b/src/applications/auth/query/PhabricatorAuthChallengeQuery.php @@ -89,7 +89,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php index ba507c757b..a3b781485f 100644 --- a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php +++ b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php @@ -93,7 +93,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php b/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php index dd73d0b081..52b907e62f 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php @@ -121,7 +121,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php b/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php index 7083545e3e..362f6be235 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php @@ -84,7 +84,7 @@ protected function willFilterPage(array $providers) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthInviteSearchEngine.php b/src/applications/auth/query/PhabricatorAuthInviteSearchEngine.php index e439dd9fb8..8ed2a31e6c 100644 --- a/src/applications/auth/query/PhabricatorAuthInviteSearchEngine.php +++ b/src/applications/auth/query/PhabricatorAuthInviteSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/auth/query/PhabricatorAuthMessageQuery.php b/src/applications/auth/query/PhabricatorAuthMessageQuery.php index 7158d03a00..c28d561297 100644 --- a/src/applications/auth/query/PhabricatorAuthMessageQuery.php +++ b/src/applications/auth/query/PhabricatorAuthMessageQuery.php @@ -73,7 +73,7 @@ protected function willFilterPage(array $messages) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthPasswordQuery.php b/src/applications/auth/query/PhabricatorAuthPasswordQuery.php index a77fd54b13..0e9e70e175 100644 --- a/src/applications/auth/query/PhabricatorAuthPasswordQuery.php +++ b/src/applications/auth/query/PhabricatorAuthPasswordQuery.php @@ -104,7 +104,7 @@ protected function willFilterPage(array $passwords) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php index 30e5dad113..d437d08fd5 100644 --- a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php +++ b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php @@ -80,7 +80,7 @@ protected function willFilterPage(array $configs) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php index 7d4aac4a01..e0970f9b51 100644 --- a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php @@ -128,7 +128,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php b/src/applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php index 0575b40b9c..133389dc8e 100644 --- a/src/applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php +++ b/src/applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php @@ -23,7 +23,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function newQuery() { diff --git a/src/applications/auth/query/PhabricatorAuthSessionQuery.php b/src/applications/auth/query/PhabricatorAuthSessionQuery.php index 4bc7eba73f..78974c7670 100644 --- a/src/applications/auth/query/PhabricatorAuthSessionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSessionQuery.php @@ -107,7 +107,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php b/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php index c5cb39096c..90b4741307 100644 --- a/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php +++ b/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php @@ -100,7 +100,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php b/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php index b5c5b6eaa2..716fad59ba 100644 --- a/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php +++ b/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php @@ -84,7 +84,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorExternalAccountQuery.php b/src/applications/auth/query/PhabricatorExternalAccountQuery.php index f44821d7a9..b2c61a1b91 100644 --- a/src/applications/auth/query/PhabricatorExternalAccountQuery.php +++ b/src/applications/auth/query/PhabricatorExternalAccountQuery.php @@ -241,7 +241,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } } diff --git a/src/applications/badges/editor/PhabricatorBadgesEditEngine.php b/src/applications/badges/editor/PhabricatorBadgesEditEngine.php index 721184852c..5d95254bbd 100644 --- a/src/applications/badges/editor/PhabricatorBadgesEditEngine.php +++ b/src/applications/badges/editor/PhabricatorBadgesEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/badges/editor/PhabricatorBadgesEditor.php b/src/applications/badges/editor/PhabricatorBadgesEditor.php index 785d8c989b..66b07390b5 100644 --- a/src/applications/badges/editor/PhabricatorBadgesEditor.php +++ b/src/applications/badges/editor/PhabricatorBadgesEditor.php @@ -4,7 +4,7 @@ final class PhabricatorBadgesEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/badges/phid/PhabricatorBadgesPHIDType.php b/src/applications/badges/phid/PhabricatorBadgesPHIDType.php index 0adea047f9..072d62f529 100644 --- a/src/applications/badges/phid/PhabricatorBadgesPHIDType.php +++ b/src/applications/badges/phid/PhabricatorBadgesPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/badges/query/PhabricatorBadgesAwardQuery.php b/src/applications/badges/query/PhabricatorBadgesAwardQuery.php index 57e53a5a30..ff975cf631 100644 --- a/src/applications/badges/query/PhabricatorBadgesAwardQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesAwardQuery.php @@ -115,7 +115,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } } diff --git a/src/applications/badges/query/PhabricatorBadgesQuery.php b/src/applications/badges/query/PhabricatorBadgesQuery.php index cc59465f67..6fb61d01c0 100644 --- a/src/applications/badges/query/PhabricatorBadgesQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesQuery.php @@ -77,7 +77,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function getBuiltinOrders() { diff --git a/src/applications/badges/query/PhabricatorBadgesSearchEngine.php b/src/applications/badges/query/PhabricatorBadgesSearchEngine.php index 6e84c30bc9..b08962ed4b 100644 --- a/src/applications/badges/query/PhabricatorBadgesSearchEngine.php +++ b/src/applications/badges/query/PhabricatorBadgesSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function newQuery() { diff --git a/src/applications/badges/typeahead/PhabricatorBadgesDatasource.php b/src/applications/badges/typeahead/PhabricatorBadgesDatasource.php index 458c9230d5..5fba42b933 100644 --- a/src/applications/badges/typeahead/PhabricatorBadgesDatasource.php +++ b/src/applications/badges/typeahead/PhabricatorBadgesDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function loadResults() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php index 1b23b13fbf..3737dc9ac1 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php @@ -33,7 +33,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function newEditableObject() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php index 9e3b23a43d..e5f5689025 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -7,7 +7,7 @@ final class PhabricatorCalendarEventEditor private $newIsAllDay; public function getEditorApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php index bc8fc360c6..b1268f1f4b 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function newEditableObject() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php b/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php index 6ddd172d58..e8ec5601f7 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php @@ -4,7 +4,7 @@ final class PhabricatorCalendarExportEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php index 7be3969671..210efe6961 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php @@ -33,7 +33,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function newEditableObject() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php b/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php index f83869d7b9..f168523988 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php @@ -4,7 +4,7 @@ final class PhabricatorCalendarImportEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php b/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php index 49d5f38959..59982cecbc 100644 --- a/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php +++ b/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php @@ -5,7 +5,7 @@ final class PhabricatorCalendarEventHeraldAdapter extends HeraldAdapter { private $object; public function getAdapterApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/calendar/phid/PhabricatorCalendarEventPHIDType.php b/src/applications/calendar/phid/PhabricatorCalendarEventPHIDType.php index fd8e86a1f2..9048a7d208 100644 --- a/src/applications/calendar/phid/PhabricatorCalendarEventPHIDType.php +++ b/src/applications/calendar/phid/PhabricatorCalendarEventPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/calendar/phid/PhabricatorCalendarExportPHIDType.php b/src/applications/calendar/phid/PhabricatorCalendarExportPHIDType.php index c642632ca8..eaf4562150 100644 --- a/src/applications/calendar/phid/PhabricatorCalendarExportPHIDType.php +++ b/src/applications/calendar/phid/PhabricatorCalendarExportPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php b/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php index a5e86893ac..6e50246bb0 100644 --- a/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php +++ b/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/calendar/phid/PhabricatorCalendarImportPHIDType.php b/src/applications/calendar/phid/PhabricatorCalendarImportPHIDType.php index 876ec7acf7..32118f4fd4 100644 --- a/src/applications/calendar/phid/PhabricatorCalendarImportPHIDType.php +++ b/src/applications/calendar/phid/PhabricatorCalendarImportPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php index 683d6cd918..2db8cdfdea 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php @@ -93,7 +93,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php index d20250e711..90f0bab517 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -513,7 +513,7 @@ protected function shouldGroupQueryResultRows() { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function willFilterPage(array $events) { diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php index 79d4321545..3a6e931515 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php @@ -12,7 +12,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function newQuery() { diff --git a/src/applications/calendar/query/PhabricatorCalendarExportQuery.php b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php index 7ef970216f..ecb2788459 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExportQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php @@ -84,7 +84,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php index 032cab0c02..c718374da2 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php index 35891cfd28..f5d8f1eb32 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php @@ -58,7 +58,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php index 731b1209cc..cd71c0f463 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php @@ -101,7 +101,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarImportLogSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarImportLogSearchEngine.php index 99f292f9a8..fd6896c37d 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportLogSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php index 0e3dbbf387..7a3f12895f 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php @@ -89,7 +89,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php index a5e44812ea..509b1224e6 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeDatasource.php b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeDatasource.php index 987f9cbf09..7e8271fa92 100644 --- a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeDatasource.php +++ b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeUserDatasource.php b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeUserDatasource.php index c799602d8a..0f834252ca 100644 --- a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeUserDatasource.php +++ b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeUserDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php index 6f9a8292f4..d653c9bdea 100644 --- a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php +++ b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/conduit/query/PhabricatorConduitLogQuery.php b/src/applications/conduit/query/PhabricatorConduitLogQuery.php index 19cb31a812..d9dcad695a 100644 --- a/src/applications/conduit/query/PhabricatorConduitLogQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitLogQuery.php @@ -107,7 +107,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } } diff --git a/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php index ab86ae6669..9e5e05b7b6 100644 --- a/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php index eb61d8c480..5e68850d5f 100644 --- a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php @@ -152,7 +152,7 @@ protected function willFilterPage(array $methods) { } public function getQueryApplicationClass() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } } diff --git a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php index 3dc5617bb5..2fd95c3550 100644 --- a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php index 384efccadf..0f3f581513 100644 --- a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php @@ -109,7 +109,7 @@ protected function willFilterPage(array $tokens) { } public function getQueryApplicationClass() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } } diff --git a/src/applications/config/editor/PhabricatorConfigEditor.php b/src/applications/config/editor/PhabricatorConfigEditor.php index 7a8833c84e..4eb8838d3e 100644 --- a/src/applications/config/editor/PhabricatorConfigEditor.php +++ b/src/applications/config/editor/PhabricatorConfigEditor.php @@ -4,7 +4,7 @@ final class PhabricatorConfigEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorConfigApplication'; + return PhabricatorConfigApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/config/phid/PhabricatorConfigConfigPHIDType.php b/src/applications/config/phid/PhabricatorConfigConfigPHIDType.php index 95c543427f..fb72a18257 100644 --- a/src/applications/config/phid/PhabricatorConfigConfigPHIDType.php +++ b/src/applications/config/phid/PhabricatorConfigConfigPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorConfigApplication'; + return PhabricatorConfigApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/config/query/PhabricatorConfigEntryQuery.php b/src/applications/config/query/PhabricatorConfigEntryQuery.php index f46fdb7d1e..c228c2b7cc 100644 --- a/src/applications/config/query/PhabricatorConfigEntryQuery.php +++ b/src/applications/config/query/PhabricatorConfigEntryQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorConfigApplication'; + return PhabricatorConfigApplication::class; } } diff --git a/src/applications/conpherence/editor/ConpherenceEditEngine.php b/src/applications/conpherence/editor/ConpherenceEditEngine.php index f5f850e637..7b1b7d1aa4 100644 --- a/src/applications/conpherence/editor/ConpherenceEditEngine.php +++ b/src/applications/conpherence/editor/ConpherenceEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php index a1d6431d8c..fe5d0a60ca 100644 --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -6,7 +6,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { const ERROR_EMPTY_MESSAGE = 'error-empty-message'; public function getEditorApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php b/src/applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php index 98f0d74797..d8273b6d6e 100644 --- a/src/applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php +++ b/src/applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/conpherence/query/ConpherenceThreadQuery.php b/src/applications/conpherence/query/ConpherenceThreadQuery.php index 34bbf7e6b0..72986d0c22 100644 --- a/src/applications/conpherence/query/ConpherenceThreadQuery.php +++ b/src/applications/conpherence/query/ConpherenceThreadQuery.php @@ -335,7 +335,7 @@ private function loadTransactionsAndHandles(array $conpherences) { } public function getQueryApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php index 77e54157cf..505d7c4a0e 100644 --- a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php +++ b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } public function newQuery() { diff --git a/src/applications/conpherence/typeahead/ConpherenceThreadDatasource.php b/src/applications/conpherence/typeahead/ConpherenceThreadDatasource.php index a18b4a73ea..735769a0cc 100644 --- a/src/applications/conpherence/typeahead/ConpherenceThreadDatasource.php +++ b/src/applications/conpherence/typeahead/ConpherenceThreadDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } public function loadResults() { diff --git a/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php b/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php index 4f67339ac5..003f448be9 100644 --- a/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php +++ b/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorCountdownApplication'; + return PhabricatorCountdownApplication::class; } protected function newEditableObject() { diff --git a/src/applications/countdown/editor/PhabricatorCountdownEditor.php b/src/applications/countdown/editor/PhabricatorCountdownEditor.php index 2102b3785b..1037320902 100644 --- a/src/applications/countdown/editor/PhabricatorCountdownEditor.php +++ b/src/applications/countdown/editor/PhabricatorCountdownEditor.php @@ -4,7 +4,7 @@ final class PhabricatorCountdownEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorCountdownApplication'; + return PhabricatorCountdownApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php b/src/applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php index 7e6278c8ea..f72ecb4808 100644 --- a/src/applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php +++ b/src/applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCountdownApplication'; + return PhabricatorCountdownApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/countdown/query/PhabricatorCountdownQuery.php b/src/applications/countdown/query/PhabricatorCountdownQuery.php index 4a6df16e8e..51360c52de 100644 --- a/src/applications/countdown/query/PhabricatorCountdownQuery.php +++ b/src/applications/countdown/query/PhabricatorCountdownQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorCountdownApplication'; + return PhabricatorCountdownApplication::class; } public function getBuiltinOrders() { diff --git a/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php b/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php index 2f9fe9c0e8..a0f93763c8 100644 --- a/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php +++ b/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCountdownApplication'; + return PhabricatorCountdownApplication::class; } public function newQuery() { diff --git a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php index 2c5b6baa3b..a604ef4939 100644 --- a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php +++ b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php @@ -189,7 +189,7 @@ private function getStatusConstants() { } public function getQueryApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } } diff --git a/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php b/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php index 84b36fe546..cae823f868 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function newEditableObject() { diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php b/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php index 129f28b347..92c7dbdf1a 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php @@ -57,7 +57,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function newEditableObject() { diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php b/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php index ea03c1ac7d..6e6d2b618b 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorDashboardPanelTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php b/src/applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php index 9945ea9d28..17880b5501 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function newEditableObject() { diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPortalEditor.php b/src/applications/dashboard/editor/PhabricatorDashboardPortalEditor.php index 2c8a3ccfac..ee2a2307ba 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardPortalEditor.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardPortalEditor.php @@ -4,7 +4,7 @@ final class PhabricatorDashboardPortalEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php b/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php index 4a84577467..8964beab70 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorDashboardTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php b/src/applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php index 1450e2aa68..0aae485b58 100644 --- a/src/applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php +++ b/src/applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php b/src/applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php index d84db72a25..1019740320 100644 --- a/src/applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php +++ b/src/applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php b/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php index 378748d96b..3faad72924 100644 --- a/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php +++ b/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php index 54f627eb00..7ec76898b9 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php @@ -95,7 +95,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php index dc50fa5d66..be59345aaa 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function newQuery() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php index 418262c745..9dd0da68e8 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardPortalSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardPortalSearchEngine.php index c1633f2e97..b5ce3068b7 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPortalSearchEngine.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPortalSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function newQuery() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardQuery.php b/src/applications/dashboard/query/PhabricatorDashboardQuery.php index 954a565ac6..3078140e24 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardQuery.php @@ -89,7 +89,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php index ea3f69faab..c02778c77d 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php +++ b/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function newQuery() { diff --git a/src/applications/dashboard/typeahead/PhabricatorDashboardDatasource.php b/src/applications/dashboard/typeahead/PhabricatorDashboardDatasource.php index ff3376bdf6..cc43c613c2 100644 --- a/src/applications/dashboard/typeahead/PhabricatorDashboardDatasource.php +++ b/src/applications/dashboard/typeahead/PhabricatorDashboardDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function loadResults() { diff --git a/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php b/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php index 9ad4901bbf..b6621480bc 100644 --- a/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php +++ b/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function loadResults() { diff --git a/src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php b/src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php index 008bb542ab..2d2e17b6b1 100644 --- a/src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php +++ b/src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function loadResults() { diff --git a/src/applications/differential/editor/DifferentialDiffEditor.php b/src/applications/differential/editor/DifferentialDiffEditor.php index e78e08d808..eaaeee60fd 100644 --- a/src/applications/differential/editor/DifferentialDiffEditor.php +++ b/src/applications/differential/editor/DifferentialDiffEditor.php @@ -12,7 +12,7 @@ public function setLookupRepository($bool) { } public function getEditorApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/differential/editor/DifferentialRevisionEditEngine.php b/src/applications/differential/editor/DifferentialRevisionEditEngine.php index bd1cee7c26..a0d0569069 100644 --- a/src/applications/differential/editor/DifferentialRevisionEditEngine.php +++ b/src/applications/differential/editor/DifferentialRevisionEditEngine.php @@ -24,7 +24,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function isEngineConfigurable() { diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index b935b09b2d..9ceb493b5f 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -15,7 +15,7 @@ final class DifferentialTransactionEditor private $ownersChangesets; public function getEditorApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/differential/herald/HeraldDifferentialDiffAdapter.php b/src/applications/differential/herald/HeraldDifferentialDiffAdapter.php index 966b306580..767cef9553 100644 --- a/src/applications/differential/herald/HeraldDifferentialDiffAdapter.php +++ b/src/applications/differential/herald/HeraldDifferentialDiffAdapter.php @@ -3,7 +3,7 @@ final class HeraldDifferentialDiffAdapter extends HeraldDifferentialAdapter { public function getAdapterApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function initializeNewAdapter() { diff --git a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php index 858a42dbc9..1ac6a33c28 100644 --- a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php +++ b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php @@ -13,7 +13,7 @@ final class HeraldDifferentialRevisionAdapter private $buildRequests = array(); public function getAdapterApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function newObject() { diff --git a/src/applications/differential/phid/DifferentialChangesetPHIDType.php b/src/applications/differential/phid/DifferentialChangesetPHIDType.php index c558c0a6a2..357d787cdf 100644 --- a/src/applications/differential/phid/DifferentialChangesetPHIDType.php +++ b/src/applications/differential/phid/DifferentialChangesetPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/differential/phid/DifferentialDiffPHIDType.php b/src/applications/differential/phid/DifferentialDiffPHIDType.php index 746da368c7..494f87f598 100644 --- a/src/applications/differential/phid/DifferentialDiffPHIDType.php +++ b/src/applications/differential/phid/DifferentialDiffPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/differential/phid/DifferentialRevisionPHIDType.php b/src/applications/differential/phid/DifferentialRevisionPHIDType.php index a7d3c9f4a7..b54d4a624f 100644 --- a/src/applications/differential/phid/DifferentialRevisionPHIDType.php +++ b/src/applications/differential/phid/DifferentialRevisionPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/differential/query/DifferentialChangesetQuery.php b/src/applications/differential/query/DifferentialChangesetQuery.php index ee29aa1cf8..99176c28d4 100644 --- a/src/applications/differential/query/DifferentialChangesetQuery.php +++ b/src/applications/differential/query/DifferentialChangesetQuery.php @@ -172,7 +172,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } } diff --git a/src/applications/differential/query/DifferentialChangesetSearchEngine.php b/src/applications/differential/query/DifferentialChangesetSearchEngine.php index b6279ec339..f3235443e9 100644 --- a/src/applications/differential/query/DifferentialChangesetSearchEngine.php +++ b/src/applications/differential/query/DifferentialChangesetSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/differential/query/DifferentialDiffQuery.php b/src/applications/differential/query/DifferentialDiffQuery.php index 04019df1e0..552a467898 100644 --- a/src/applications/differential/query/DifferentialDiffQuery.php +++ b/src/applications/differential/query/DifferentialDiffQuery.php @@ -185,7 +185,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } } diff --git a/src/applications/differential/query/DifferentialDiffSearchEngine.php b/src/applications/differential/query/DifferentialDiffSearchEngine.php index 89feeb5c3e..31393d4681 100644 --- a/src/applications/differential/query/DifferentialDiffSearchEngine.php +++ b/src/applications/differential/query/DifferentialDiffSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function newQuery() { diff --git a/src/applications/differential/query/DifferentialHunkQuery.php b/src/applications/differential/query/DifferentialHunkQuery.php index 21787f04e5..3b07676aa8 100644 --- a/src/applications/differential/query/DifferentialHunkQuery.php +++ b/src/applications/differential/query/DifferentialHunkQuery.php @@ -78,7 +78,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function getDefaultOrderVector() { diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index 75d3e052e4..d6b249cac5 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -1081,7 +1081,7 @@ private function loadReviewerAuthority( } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php index 1ec5265630..b69b764bb8 100644 --- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php +++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function newResultBuckets() { diff --git a/src/applications/differential/query/DifferentialViewStateQuery.php b/src/applications/differential/query/DifferentialViewStateQuery.php index 604a6de1db..19104748d3 100644 --- a/src/applications/differential/query/DifferentialViewStateQuery.php +++ b/src/applications/differential/query/DifferentialViewStateQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } } diff --git a/src/applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php b/src/applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php index bea7056ea2..e06693dc22 100644 --- a/src/applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php +++ b/src/applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php b/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php index 8ad8274ea3..bdb109fce9 100644 --- a/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php +++ b/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialNoReviewersDatasource.php b/src/applications/differential/typeahead/DifferentialNoReviewersDatasource.php index 083b6c7cb5..eeff6de538 100644 --- a/src/applications/differential/typeahead/DifferentialNoReviewersDatasource.php +++ b/src/applications/differential/typeahead/DifferentialNoReviewersDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php b/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php index 5307ab1866..4bca119347 100644 --- a/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php +++ b/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialResponsibleUserDatasource.php b/src/applications/differential/typeahead/DifferentialResponsibleUserDatasource.php index 8ac7b36520..494bca5f62 100644 --- a/src/applications/differential/typeahead/DifferentialResponsibleUserDatasource.php +++ b/src/applications/differential/typeahead/DifferentialResponsibleUserDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php b/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php index d23ecff47d..05ae39ec2e 100644 --- a/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php +++ b/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/differential/typeahead/DifferentialReviewerDatasource.php b/src/applications/differential/typeahead/DifferentialReviewerDatasource.php index 18aa9e48b7..9030213404 100644 --- a/src/applications/differential/typeahead/DifferentialReviewerDatasource.php +++ b/src/applications/differential/typeahead/DifferentialReviewerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialReviewerFunctionDatasource.php b/src/applications/differential/typeahead/DifferentialReviewerFunctionDatasource.php index 3b79016055..ac185878c9 100644 --- a/src/applications/differential/typeahead/DifferentialReviewerFunctionDatasource.php +++ b/src/applications/differential/typeahead/DifferentialReviewerFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php index 8487111452..3c9e16a268 100644 --- a/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php +++ b/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php index 0f00e470c3..51da971918 100644 --- a/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php +++ b/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php index 5e240d3c29..978e9a847f 100644 --- a/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php +++ b/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function loadResults() { diff --git a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php index 60a2667c95..02213cceee 100644 --- a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php @@ -25,7 +25,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newEditableObject() { diff --git a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php index 07213afd2b..c9dbb2b329 100644 --- a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php @@ -41,7 +41,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newEditableObject() { diff --git a/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php b/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php index 8e964e3e71..54bf9daea7 100644 --- a/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php +++ b/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php @@ -20,7 +20,7 @@ protected function supportsSearch() { } public function getEditorApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/diffusion/editor/DiffusionURIEditEngine.php b/src/applications/diffusion/editor/DiffusionURIEditEngine.php index dbc1238153..2c7f79359e 100644 --- a/src/applications/diffusion/editor/DiffusionURIEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionURIEditEngine.php @@ -33,7 +33,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newEditableObject() { diff --git a/src/applications/diffusion/editor/DiffusionURIEditor.php b/src/applications/diffusion/editor/DiffusionURIEditor.php index 90ced865f0..b8aecca8a3 100644 --- a/src/applications/diffusion/editor/DiffusionURIEditor.php +++ b/src/applications/diffusion/editor/DiffusionURIEditor.php @@ -7,7 +7,7 @@ final class DiffusionURIEditor private $repositoryPHID; public function getEditorApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/diffusion/herald/HeraldCommitAdapter.php b/src/applications/diffusion/herald/HeraldCommitAdapter.php index af7c757cd9..0ec9de7e87 100644 --- a/src/applications/diffusion/herald/HeraldCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldCommitAdapter.php @@ -18,7 +18,7 @@ final class HeraldCommitAdapter private $buildRequests = array(); public function getAdapterApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newObject() { diff --git a/src/applications/diffusion/herald/HeraldPreCommitAdapter.php b/src/applications/diffusion/herald/HeraldPreCommitAdapter.php index f63f647da6..4707545e9d 100644 --- a/src/applications/diffusion/herald/HeraldPreCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldPreCommitAdapter.php @@ -22,7 +22,7 @@ public function getHookEngine() { } public function getAdapterApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function isTestAdapterForObject($object) { diff --git a/src/applications/diffusion/query/DiffusionCommitHintQuery.php b/src/applications/diffusion/query/DiffusionCommitHintQuery.php index c0794a4387..fdf30e693b 100644 --- a/src/applications/diffusion/query/DiffusionCommitHintQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitHintQuery.php @@ -106,7 +106,7 @@ protected function didFilterPage(array $hints) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index 7b4e80bfec..e1bec46441 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -931,7 +931,7 @@ protected function shouldGroupQueryResultRows() { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getOrderableColumns() { diff --git a/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php b/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php index dfdfceb519..dbb9205f61 100644 --- a/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php +++ b/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php b/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php index f41b6b89a4..d6877433f7 100644 --- a/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php +++ b/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/diffusion/query/DiffusionSyncLogSearchEngine.php b/src/applications/diffusion/query/DiffusionSyncLogSearchEngine.php index 6b321189f4..02280efdd2 100644 --- a/src/applications/diffusion/query/DiffusionSyncLogSearchEngine.php +++ b/src/applications/diffusion/query/DiffusionSyncLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/diffusion/typeahead/DiffusionAuditorDatasource.php b/src/applications/diffusion/typeahead/DiffusionAuditorDatasource.php index 54c3aa72d9..fe0f12dd70 100644 --- a/src/applications/diffusion/typeahead/DiffusionAuditorDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionAuditorDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php b/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php index 2bfc1ee037..eecef5dd03 100644 --- a/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php b/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php index b69498931b..50ca68e59c 100644 --- a/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/diffusion/typeahead/DiffusionRefDatasource.php b/src/applications/diffusion/typeahead/DiffusionRefDatasource.php index f98960e7cc..a0a8e89237 100644 --- a/src/applications/diffusion/typeahead/DiffusionRefDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionRefDatasource.php @@ -15,7 +15,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function loadResults() { diff --git a/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php b/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php index 5953dac8b3..5ba53d252c 100644 --- a/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function loadResults() { diff --git a/src/applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php b/src/applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php index 5fb7d5f5ac..5865aa0ef4 100644 --- a/src/applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php b/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php index 1c29c0dde4..1382bd41e0 100644 --- a/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php @@ -18,7 +18,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function loadResults() { diff --git a/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php b/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php index 019d78d786..dbc7362b2a 100644 --- a/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/diviner/editor/DivinerLiveBookEditor.php b/src/applications/diviner/editor/DivinerLiveBookEditor.php index 3f392391bb..dea16af377 100644 --- a/src/applications/diviner/editor/DivinerLiveBookEditor.php +++ b/src/applications/diviner/editor/DivinerLiveBookEditor.php @@ -4,7 +4,7 @@ final class DivinerLiveBookEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/diviner/phid/DivinerAtomPHIDType.php b/src/applications/diviner/phid/DivinerAtomPHIDType.php index 7387b0cafe..3e040a9e79 100644 --- a/src/applications/diviner/phid/DivinerAtomPHIDType.php +++ b/src/applications/diviner/phid/DivinerAtomPHIDType.php @@ -17,7 +17,7 @@ public function getTypeIcon() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/diviner/phid/DivinerBookPHIDType.php b/src/applications/diviner/phid/DivinerBookPHIDType.php index da08ae3e87..bb840c5342 100644 --- a/src/applications/diviner/phid/DivinerBookPHIDType.php +++ b/src/applications/diviner/phid/DivinerBookPHIDType.php @@ -17,7 +17,7 @@ public function getTypeIcon() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/diviner/query/DivinerAtomQuery.php b/src/applications/diviner/query/DivinerAtomQuery.php index 65a5634008..1ca7aad984 100644 --- a/src/applications/diviner/query/DivinerAtomQuery.php +++ b/src/applications/diviner/query/DivinerAtomQuery.php @@ -505,7 +505,7 @@ private function attachAllChildren( } public function getQueryApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } } diff --git a/src/applications/diviner/query/DivinerAtomSearchEngine.php b/src/applications/diviner/query/DivinerAtomSearchEngine.php index 35caf20a63..47dd418c78 100644 --- a/src/applications/diviner/query/DivinerAtomSearchEngine.php +++ b/src/applications/diviner/query/DivinerAtomSearchEngine.php @@ -7,7 +7,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/diviner/query/DivinerBookQuery.php b/src/applications/diviner/query/DivinerBookQuery.php index e809099c7e..4bc9ff270d 100644 --- a/src/applications/diviner/query/DivinerBookQuery.php +++ b/src/applications/diviner/query/DivinerBookQuery.php @@ -167,7 +167,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } public function getOrderableColumns() { diff --git a/src/applications/diviner/typeahead/DivinerBookDatasource.php b/src/applications/diviner/typeahead/DivinerBookDatasource.php index 2584626d6e..8fc2a52238 100644 --- a/src/applications/diviner/typeahead/DivinerBookDatasource.php +++ b/src/applications/diviner/typeahead/DivinerBookDatasource.php @@ -11,7 +11,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } public function loadResults() { diff --git a/src/applications/doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php b/src/applications/doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php index be9e32cf2a..1817e97b37 100644 --- a/src/applications/doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php +++ b/src/applications/doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDoorkeeperApplication'; + return PhabricatorDoorkeeperApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php index 2a33206831..c9a7c140eb 100644 --- a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php +++ b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php @@ -41,7 +41,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDoorkeeperApplication'; + return PhabricatorDoorkeeperApplication::class; } } diff --git a/src/applications/drydock/editor/DrydockBlueprintEditEngine.php b/src/applications/drydock/editor/DrydockBlueprintEditEngine.php index a3b6fdaf75..e01513f2b9 100644 --- a/src/applications/drydock/editor/DrydockBlueprintEditEngine.php +++ b/src/applications/drydock/editor/DrydockBlueprintEditEngine.php @@ -24,7 +24,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function setBlueprintImplementation( diff --git a/src/applications/drydock/editor/DrydockBlueprintEditor.php b/src/applications/drydock/editor/DrydockBlueprintEditor.php index aac3e7a020..f07907147c 100644 --- a/src/applications/drydock/editor/DrydockBlueprintEditor.php +++ b/src/applications/drydock/editor/DrydockBlueprintEditor.php @@ -4,7 +4,7 @@ final class DrydockBlueprintEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php b/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php index 058ccff6a9..61495b73e4 100644 --- a/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php +++ b/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/phid/DrydockBlueprintPHIDType.php b/src/applications/drydock/phid/DrydockBlueprintPHIDType.php index ef286cf5f1..e363704454 100644 --- a/src/applications/drydock/phid/DrydockBlueprintPHIDType.php +++ b/src/applications/drydock/phid/DrydockBlueprintPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/phid/DrydockLeasePHIDType.php b/src/applications/drydock/phid/DrydockLeasePHIDType.php index faa751b0f1..51d6a6e31a 100644 --- a/src/applications/drydock/phid/DrydockLeasePHIDType.php +++ b/src/applications/drydock/phid/DrydockLeasePHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/phid/DrydockRepositoryOperationPHIDType.php b/src/applications/drydock/phid/DrydockRepositoryOperationPHIDType.php index 6222552f84..35c78b5e03 100644 --- a/src/applications/drydock/phid/DrydockRepositoryOperationPHIDType.php +++ b/src/applications/drydock/phid/DrydockRepositoryOperationPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/phid/DrydockResourcePHIDType.php b/src/applications/drydock/phid/DrydockResourcePHIDType.php index a36647964d..8ca8718e6e 100644 --- a/src/applications/drydock/phid/DrydockResourcePHIDType.php +++ b/src/applications/drydock/phid/DrydockResourcePHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php b/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php index 16737a9dfe..81e8c00ba0 100644 --- a/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php +++ b/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/drydock/query/DrydockBlueprintSearchEngine.php b/src/applications/drydock/query/DrydockBlueprintSearchEngine.php index 3c80ca2700..244afcb986 100644 --- a/src/applications/drydock/query/DrydockBlueprintSearchEngine.php +++ b/src/applications/drydock/query/DrydockBlueprintSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/query/DrydockLeaseSearchEngine.php b/src/applications/drydock/query/DrydockLeaseSearchEngine.php index 7e89336fab..95f713c0d4 100644 --- a/src/applications/drydock/query/DrydockLeaseSearchEngine.php +++ b/src/applications/drydock/query/DrydockLeaseSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/query/DrydockLogSearchEngine.php b/src/applications/drydock/query/DrydockLogSearchEngine.php index 3602714a60..553f4be486 100644 --- a/src/applications/drydock/query/DrydockLogSearchEngine.php +++ b/src/applications/drydock/query/DrydockLogSearchEngine.php @@ -54,7 +54,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/query/DrydockQuery.php b/src/applications/drydock/query/DrydockQuery.php index 4f3f768d37..3db247b581 100644 --- a/src/applications/drydock/query/DrydockQuery.php +++ b/src/applications/drydock/query/DrydockQuery.php @@ -3,7 +3,7 @@ abstract class DrydockQuery extends PhabricatorCursorPagedPolicyAwareQuery { public function getQueryApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } } diff --git a/src/applications/drydock/query/DrydockRepositoryOperationSearchEngine.php b/src/applications/drydock/query/DrydockRepositoryOperationSearchEngine.php index eeb85329bf..3ab573dd8f 100644 --- a/src/applications/drydock/query/DrydockRepositoryOperationSearchEngine.php +++ b/src/applications/drydock/query/DrydockRepositoryOperationSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/query/DrydockResourceSearchEngine.php b/src/applications/drydock/query/DrydockResourceSearchEngine.php index 9015f92408..d3fed0fd78 100644 --- a/src/applications/drydock/query/DrydockResourceSearchEngine.php +++ b/src/applications/drydock/query/DrydockResourceSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php b/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php index d36a2bc64b..7cdade856c 100644 --- a/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php +++ b/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php @@ -12,7 +12,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function loadResults() { diff --git a/src/applications/drydock/typeahead/DrydockLeaseDatasource.php b/src/applications/drydock/typeahead/DrydockLeaseDatasource.php index feb6bd9368..d21f99393e 100644 --- a/src/applications/drydock/typeahead/DrydockLeaseDatasource.php +++ b/src/applications/drydock/typeahead/DrydockLeaseDatasource.php @@ -8,7 +8,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function loadResults() { diff --git a/src/applications/drydock/typeahead/DrydockResourceDatasource.php b/src/applications/drydock/typeahead/DrydockResourceDatasource.php index 6e0b8f7116..b8fc292b5e 100644 --- a/src/applications/drydock/typeahead/DrydockResourceDatasource.php +++ b/src/applications/drydock/typeahead/DrydockResourceDatasource.php @@ -8,7 +8,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function loadResults() { diff --git a/src/applications/feed/query/PhabricatorFeedQuery.php b/src/applications/feed/query/PhabricatorFeedQuery.php index 8302af20c1..51d0d1544a 100644 --- a/src/applications/feed/query/PhabricatorFeedQuery.php +++ b/src/applications/feed/query/PhabricatorFeedQuery.php @@ -169,7 +169,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorFeedApplication'; + return PhabricatorFeedApplication::class; } } diff --git a/src/applications/feed/query/PhabricatorFeedSearchEngine.php b/src/applications/feed/query/PhabricatorFeedSearchEngine.php index d17c756524..58a00deda2 100644 --- a/src/applications/feed/query/PhabricatorFeedSearchEngine.php +++ b/src/applications/feed/query/PhabricatorFeedSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFeedApplication'; + return PhabricatorFeedApplication::class; } public function newQuery() { diff --git a/src/applications/feed/query/PhabricatorFeedTransactionQuery.php b/src/applications/feed/query/PhabricatorFeedTransactionQuery.php index d0a9f53e35..93e90e2ef7 100644 --- a/src/applications/feed/query/PhabricatorFeedTransactionQuery.php +++ b/src/applications/feed/query/PhabricatorFeedTransactionQuery.php @@ -145,7 +145,7 @@ protected function loadPage() { } public function getQueryApplicationClass() { - return 'PhabricatorFeedApplication'; + return PhabricatorFeedApplication::class; } private function newTransactionQueries() { diff --git a/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php b/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php index 0cbbcd23b1..cc04532d56 100644 --- a/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php +++ b/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFeedApplication'; + return PhabricatorFeedApplication::class; } public function newQuery() { diff --git a/src/applications/files/editor/PhabricatorFileEditEngine.php b/src/applications/files/editor/PhabricatorFileEditEngine.php index a6a39852cb..c5ff926418 100644 --- a/src/applications/files/editor/PhabricatorFileEditEngine.php +++ b/src/applications/files/editor/PhabricatorFileEditEngine.php @@ -28,7 +28,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } protected function newEditableObject() { diff --git a/src/applications/files/editor/PhabricatorFileEditor.php b/src/applications/files/editor/PhabricatorFileEditor.php index 91d168e68d..8892f80174 100644 --- a/src/applications/files/editor/PhabricatorFileEditor.php +++ b/src/applications/files/editor/PhabricatorFileEditor.php @@ -4,7 +4,7 @@ final class PhabricatorFileEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/files/phid/PhabricatorFileFilePHIDType.php b/src/applications/files/phid/PhabricatorFileFilePHIDType.php index c583595c4d..70a58834c4 100644 --- a/src/applications/files/phid/PhabricatorFileFilePHIDType.php +++ b/src/applications/files/phid/PhabricatorFileFilePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/files/query/PhabricatorFileAttachmentQuery.php b/src/applications/files/query/PhabricatorFileAttachmentQuery.php index 5fb37dc32e..50aea1a839 100644 --- a/src/applications/files/query/PhabricatorFileAttachmentQuery.php +++ b/src/applications/files/query/PhabricatorFileAttachmentQuery.php @@ -125,7 +125,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } } diff --git a/src/applications/files/query/PhabricatorFileChunkQuery.php b/src/applications/files/query/PhabricatorFileChunkQuery.php index 4398860569..f2c104dc7a 100644 --- a/src/applications/files/query/PhabricatorFileChunkQuery.php +++ b/src/applications/files/query/PhabricatorFileChunkQuery.php @@ -128,7 +128,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } } diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php index 80e511b1e2..e712d533b0 100644 --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -540,7 +540,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } } diff --git a/src/applications/files/query/PhabricatorFileSearchEngine.php b/src/applications/files/query/PhabricatorFileSearchEngine.php index 59810c830c..c2d36acfd6 100644 --- a/src/applications/files/query/PhabricatorFileSearchEngine.php +++ b/src/applications/files/query/PhabricatorFileSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/files/typeahead/PhabricatorIconDatasource.php b/src/applications/files/typeahead/PhabricatorIconDatasource.php index 63b554a9b7..e904742e26 100644 --- a/src/applications/files/typeahead/PhabricatorIconDatasource.php +++ b/src/applications/files/typeahead/PhabricatorIconDatasource.php @@ -11,7 +11,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } public function loadResults() { diff --git a/src/applications/flag/query/PhabricatorFlagQuery.php b/src/applications/flag/query/PhabricatorFlagQuery.php index c6c905465d..07b3e09d10 100644 --- a/src/applications/flag/query/PhabricatorFlagQuery.php +++ b/src/applications/flag/query/PhabricatorFlagQuery.php @@ -173,7 +173,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorFlagsApplication'; + return PhabricatorFlagsApplication::class; } } diff --git a/src/applications/flag/query/PhabricatorFlagSearchEngine.php b/src/applications/flag/query/PhabricatorFlagSearchEngine.php index a433e4241c..bc571b6bf4 100644 --- a/src/applications/flag/query/PhabricatorFlagSearchEngine.php +++ b/src/applications/flag/query/PhabricatorFlagSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFlagsApplication'; + return PhabricatorFlagsApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/fund/editor/FundBackerEditor.php b/src/applications/fund/editor/FundBackerEditor.php index 403853863b..0736f3339b 100644 --- a/src/applications/fund/editor/FundBackerEditor.php +++ b/src/applications/fund/editor/FundBackerEditor.php @@ -4,7 +4,7 @@ final class FundBackerEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/fund/editor/FundInitiativeEditEngine.php b/src/applications/fund/editor/FundInitiativeEditEngine.php index 824f4e009f..a8b90773ad 100644 --- a/src/applications/fund/editor/FundInitiativeEditEngine.php +++ b/src/applications/fund/editor/FundInitiativeEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/fund/editor/FundInitiativeEditor.php b/src/applications/fund/editor/FundInitiativeEditor.php index 9175156ffd..139d6073a2 100644 --- a/src/applications/fund/editor/FundInitiativeEditor.php +++ b/src/applications/fund/editor/FundInitiativeEditor.php @@ -4,7 +4,7 @@ final class FundInitiativeEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/fund/phid/FundBackerPHIDType.php b/src/applications/fund/phid/FundBackerPHIDType.php index 3feff6364a..0c41b6f697 100644 --- a/src/applications/fund/phid/FundBackerPHIDType.php +++ b/src/applications/fund/phid/FundBackerPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/fund/phid/FundInitiativePHIDType.php b/src/applications/fund/phid/FundInitiativePHIDType.php index 3399dc0a3f..d708139cbe 100644 --- a/src/applications/fund/phid/FundInitiativePHIDType.php +++ b/src/applications/fund/phid/FundInitiativePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/fund/query/FundBackerQuery.php b/src/applications/fund/query/FundBackerQuery.php index 2d6e13dfd9..e580e4339d 100644 --- a/src/applications/fund/query/FundBackerQuery.php +++ b/src/applications/fund/query/FundBackerQuery.php @@ -112,7 +112,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } } diff --git a/src/applications/fund/query/FundBackerSearchEngine.php b/src/applications/fund/query/FundBackerSearchEngine.php index ba4b3bc39e..8d4872f9e5 100644 --- a/src/applications/fund/query/FundBackerSearchEngine.php +++ b/src/applications/fund/query/FundBackerSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/fund/query/FundInitiativeQuery.php b/src/applications/fund/query/FundInitiativeQuery.php index 4b18597873..47d2969220 100644 --- a/src/applications/fund/query/FundInitiativeQuery.php +++ b/src/applications/fund/query/FundInitiativeQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/fund/query/FundInitiativeSearchEngine.php b/src/applications/fund/query/FundInitiativeSearchEngine.php index e075fe5063..2773d2a150 100644 --- a/src/applications/fund/query/FundInitiativeSearchEngine.php +++ b/src/applications/fund/query/FundInitiativeSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function newQuery() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php index 9f55965d9c..fcef490256 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function newEditableObject() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php index b7d73f238b..886cae3533 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function newEditableObject() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php index 1b340b6524..bf9d938bc8 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php @@ -4,7 +4,7 @@ final class HarbormasterBuildPlanEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildStepEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildStepEditEngine.php index d54832cad1..a93478a086 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildStepEditEngine.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildStepEditEngine.php @@ -37,7 +37,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function newEditableObject() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php index 8206edee4d..dec2a70a55 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php @@ -4,7 +4,7 @@ final class HarbormasterBuildStepEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php index 146caf9f2c..1746bf97b5 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php @@ -4,7 +4,7 @@ final class HarbormasterBuildTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php index bd60eeb53b..9e16038bc7 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function newEditableObject() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php index 895c89f858..df8b20864a 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php @@ -4,7 +4,7 @@ final class HarbormasterBuildableTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php index 29256afd96..180bb6ed82 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php index a2fa58fe79..faa26fab35 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php index 27551f13a8..be4bbd1256 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php index 86aacfb8d3..0b4f55f6e3 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/harbormaster/phid/HarbormasterBuildStepPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildStepPHIDType.php index 63e9bc6a12..75ede8a632 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildStepPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildStepPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php index b20d6dc0a4..002563a3f9 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/harbormaster/phid/HarbormasterBuildablePHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildablePHIDType.php index 674393dfc7..fac63376a4 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildablePHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildablePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/harbormaster/query/HarbormasterArtifactSearchEngine.php b/src/applications/harbormaster/query/HarbormasterArtifactSearchEngine.php index b7e93b2320..a839163879 100644 --- a/src/applications/harbormaster/query/HarbormasterArtifactSearchEngine.php +++ b/src/applications/harbormaster/query/HarbormasterArtifactSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function newQuery() { diff --git a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php index 59d4c07d2f..a1b40c6b1e 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php @@ -107,7 +107,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php b/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php index f62919f989..1bd48d4d05 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php @@ -80,7 +80,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildLogSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildLogSearchEngine.php index 3698238b12..473311064f 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildLogSearchEngine.php +++ b/src/applications/harbormaster/query/HarbormasterBuildLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function newQuery() { diff --git a/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php index 393ca0a493..779d256117 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php @@ -82,7 +82,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php b/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php index c8514b2186..04a14d32b5 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php @@ -116,7 +116,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function getOrderableColumns() { diff --git a/src/applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php index 709df7f94c..930308e496 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php +++ b/src/applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function newQuery() { diff --git a/src/applications/harbormaster/query/HarbormasterBuildQuery.php b/src/applications/harbormaster/query/HarbormasterBuildQuery.php index a397f5968a..e1260d9175 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildQuery.php @@ -219,7 +219,7 @@ private function shouldJoinPlanTable() { } public function getQueryApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php index b8140d84f6..4b722c4990 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php +++ b/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function newQuery() { diff --git a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php index 93010071e2..3c91b578c7 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php @@ -79,7 +79,7 @@ protected function willFilterPage(array $page) { } public function getQueryApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildStepSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildStepSearchEngine.php index b866a1dbc9..3ff1707edf 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildStepSearchEngine.php +++ b/src/applications/harbormaster/query/HarbormasterBuildStepSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function newQuery() { diff --git a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php index a93aff60bd..5abdbbd5a8 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php @@ -203,7 +203,7 @@ protected function willFilterPage(array $page) { } public function getQueryApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildTargetSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildTargetSearchEngine.php index db7d6f7c87..5ad5919eb9 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildTargetSearchEngine.php +++ b/src/applications/harbormaster/query/HarbormasterBuildTargetSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function newQuery() { diff --git a/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php b/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php index edfe102ca2..049ac7f4b7 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php @@ -85,7 +85,7 @@ protected function didFilterPage(array $messages) { } public function getQueryApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php index cf907a2dc3..fea8ba926d 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php @@ -174,7 +174,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildableSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildableSearchEngine.php index 36f5dc89c2..88fbd8f8b6 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildableSearchEngine.php +++ b/src/applications/harbormaster/query/HarbormasterBuildableSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function newQuery() { diff --git a/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php b/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php index 08b0be774e..8453c56239 100644 --- a/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php +++ b/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php @@ -17,7 +17,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function loadResults() { diff --git a/src/applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php b/src/applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php index cf5b815c57..ff831a74c5 100644 --- a/src/applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php +++ b/src/applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function loadResults() { diff --git a/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php b/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php index 6cc11bd0c2..86c9da35e0 100644 --- a/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php +++ b/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function loadResults() { diff --git a/src/applications/herald/adapter/HeraldRuleAdapter.php b/src/applications/herald/adapter/HeraldRuleAdapter.php index 8ed851a1a9..d5d1423ae7 100644 --- a/src/applications/herald/adapter/HeraldRuleAdapter.php +++ b/src/applications/herald/adapter/HeraldRuleAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/herald/editor/HeraldRuleEditor.php b/src/applications/herald/editor/HeraldRuleEditor.php index ea099cc6f3..01f42969f1 100644 --- a/src/applications/herald/editor/HeraldRuleEditor.php +++ b/src/applications/herald/editor/HeraldRuleEditor.php @@ -4,7 +4,7 @@ final class HeraldRuleEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/herald/editor/HeraldWebhookEditEngine.php b/src/applications/herald/editor/HeraldWebhookEditEngine.php index 5bca0af542..527aafe110 100644 --- a/src/applications/herald/editor/HeraldWebhookEditEngine.php +++ b/src/applications/herald/editor/HeraldWebhookEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function newEditableObject() { diff --git a/src/applications/herald/editor/HeraldWebhookEditor.php b/src/applications/herald/editor/HeraldWebhookEditor.php index 1f138e5028..4cbb57484b 100644 --- a/src/applications/herald/editor/HeraldWebhookEditor.php +++ b/src/applications/herald/editor/HeraldWebhookEditor.php @@ -4,7 +4,7 @@ final class HeraldWebhookEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/herald/phid/HeraldRulePHIDType.php b/src/applications/herald/phid/HeraldRulePHIDType.php index a70f89b406..767b242495 100644 --- a/src/applications/herald/phid/HeraldRulePHIDType.php +++ b/src/applications/herald/phid/HeraldRulePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/herald/phid/HeraldTranscriptPHIDType.php b/src/applications/herald/phid/HeraldTranscriptPHIDType.php index 12625d0c1a..a05c293a44 100644 --- a/src/applications/herald/phid/HeraldTranscriptPHIDType.php +++ b/src/applications/herald/phid/HeraldTranscriptPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/herald/phid/HeraldWebhookPHIDType.php b/src/applications/herald/phid/HeraldWebhookPHIDType.php index bf16e22b1a..e88945b13d 100644 --- a/src/applications/herald/phid/HeraldWebhookPHIDType.php +++ b/src/applications/herald/phid/HeraldWebhookPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/herald/phid/HeraldWebhookRequestPHIDType.php b/src/applications/herald/phid/HeraldWebhookRequestPHIDType.php index bcf5afb0d3..034bfa9b66 100644 --- a/src/applications/herald/phid/HeraldWebhookRequestPHIDType.php +++ b/src/applications/herald/phid/HeraldWebhookRequestPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/herald/query/HeraldRuleQuery.php b/src/applications/herald/query/HeraldRuleQuery.php index e104c44122..f5eedd8177 100644 --- a/src/applications/herald/query/HeraldRuleQuery.php +++ b/src/applications/herald/query/HeraldRuleQuery.php @@ -327,7 +327,7 @@ private function validateRuleAuthors(array $rules) { } public function getQueryApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/herald/query/HeraldRuleSearchEngine.php b/src/applications/herald/query/HeraldRuleSearchEngine.php index 95e3079717..b4e10035a3 100644 --- a/src/applications/herald/query/HeraldRuleSearchEngine.php +++ b/src/applications/herald/query/HeraldRuleSearchEngine.php @@ -7,7 +7,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function newQuery() { diff --git a/src/applications/herald/query/HeraldTranscriptQuery.php b/src/applications/herald/query/HeraldTranscriptQuery.php index 00a9dffeaf..6e9cf223b0 100644 --- a/src/applications/herald/query/HeraldTranscriptQuery.php +++ b/src/applications/herald/query/HeraldTranscriptQuery.php @@ -130,7 +130,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } } diff --git a/src/applications/herald/query/HeraldTranscriptSearchEngine.php b/src/applications/herald/query/HeraldTranscriptSearchEngine.php index e35620f0da..02e5b6b6e4 100644 --- a/src/applications/herald/query/HeraldTranscriptSearchEngine.php +++ b/src/applications/herald/query/HeraldTranscriptSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/herald/query/HeraldWebhookQuery.php b/src/applications/herald/query/HeraldWebhookQuery.php index 77307a71e6..fff20f7e6c 100644 --- a/src/applications/herald/query/HeraldWebhookQuery.php +++ b/src/applications/herald/query/HeraldWebhookQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } } diff --git a/src/applications/herald/query/HeraldWebhookRequestQuery.php b/src/applications/herald/query/HeraldWebhookRequestQuery.php index f0a61a2dc5..5c0e94cedc 100644 --- a/src/applications/herald/query/HeraldWebhookRequestQuery.php +++ b/src/applications/herald/query/HeraldWebhookRequestQuery.php @@ -116,7 +116,7 @@ protected function willFilterPage(array $requests) { public function getQueryApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } } diff --git a/src/applications/herald/query/HeraldWebhookSearchEngine.php b/src/applications/herald/query/HeraldWebhookSearchEngine.php index 84997b60b1..ba898f4434 100644 --- a/src/applications/herald/query/HeraldWebhookSearchEngine.php +++ b/src/applications/herald/query/HeraldWebhookSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function newQuery() { diff --git a/src/applications/herald/typeahead/HeraldAdapterDatasource.php b/src/applications/herald/typeahead/HeraldAdapterDatasource.php index 1fffd1bb6b..b0371b01a4 100644 --- a/src/applications/herald/typeahead/HeraldAdapterDatasource.php +++ b/src/applications/herald/typeahead/HeraldAdapterDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function loadResults() { diff --git a/src/applications/herald/typeahead/HeraldRuleDatasource.php b/src/applications/herald/typeahead/HeraldRuleDatasource.php index 6b73bc0ab6..20cb0cf437 100644 --- a/src/applications/herald/typeahead/HeraldRuleDatasource.php +++ b/src/applications/herald/typeahead/HeraldRuleDatasource.php @@ -12,7 +12,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function loadResults() { diff --git a/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php b/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php index 8dfa7d0f6a..2bd81ff4a4 100644 --- a/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php +++ b/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function loadResults() { diff --git a/src/applications/herald/typeahead/HeraldWebhookDatasource.php b/src/applications/herald/typeahead/HeraldWebhookDatasource.php index a66431d515..f35dea6d30 100644 --- a/src/applications/herald/typeahead/HeraldWebhookDatasource.php +++ b/src/applications/herald/typeahead/HeraldWebhookDatasource.php @@ -12,7 +12,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function loadResults() { diff --git a/src/applications/legalpad/editor/LegalpadDocumentEditEngine.php b/src/applications/legalpad/editor/LegalpadDocumentEditEngine.php index fb3f54275a..e9810fad84 100644 --- a/src/applications/legalpad/editor/LegalpadDocumentEditEngine.php +++ b/src/applications/legalpad/editor/LegalpadDocumentEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/legalpad/editor/LegalpadDocumentEditor.php b/src/applications/legalpad/editor/LegalpadDocumentEditor.php index 90f50564de..95ec232f09 100644 --- a/src/applications/legalpad/editor/LegalpadDocumentEditor.php +++ b/src/applications/legalpad/editor/LegalpadDocumentEditor.php @@ -4,7 +4,7 @@ final class LegalpadDocumentEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php index 39c744f271..9cd7da1105 100644 --- a/src/applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php +++ b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php index 782dbf13aa..97d280865c 100644 --- a/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php +++ b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/legalpad/query/LegalpadDocumentQuery.php b/src/applications/legalpad/query/LegalpadDocumentQuery.php index 854a187fab..5e26ec31b1 100644 --- a/src/applications/legalpad/query/LegalpadDocumentQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentQuery.php @@ -259,7 +259,7 @@ private function loadSignatures(array $documents) { } public function getQueryApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php index 591174be57..cf168451a4 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php +++ b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } public function newQuery() { diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php index f8ee72c913..e8b57073e3 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php @@ -151,7 +151,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } } diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php index ac3e181889..d2c8149349 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php +++ b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php @@ -79,7 +79,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } public function setDocument(LegalpadDocument $document) { diff --git a/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php b/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php index a0117ee7b0..fccf078f36 100644 --- a/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php +++ b/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php @@ -16,7 +16,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } public function loadResults() { diff --git a/src/applications/macro/editor/PhabricatorMacroEditEngine.php b/src/applications/macro/editor/PhabricatorMacroEditEngine.php index ff348c3163..d4a63f70ad 100644 --- a/src/applications/macro/editor/PhabricatorMacroEditEngine.php +++ b/src/applications/macro/editor/PhabricatorMacroEditEngine.php @@ -18,7 +18,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function isEngineConfigurable() { diff --git a/src/applications/macro/editor/PhabricatorMacroEditor.php b/src/applications/macro/editor/PhabricatorMacroEditor.php index 91ed23a259..31db69921e 100644 --- a/src/applications/macro/editor/PhabricatorMacroEditor.php +++ b/src/applications/macro/editor/PhabricatorMacroEditor.php @@ -4,7 +4,7 @@ final class PhabricatorMacroEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/macro/phid/PhabricatorMacroMacroPHIDType.php b/src/applications/macro/phid/PhabricatorMacroMacroPHIDType.php index 19c7f5a1fc..a63b02f8ec 100644 --- a/src/applications/macro/phid/PhabricatorMacroMacroPHIDType.php +++ b/src/applications/macro/phid/PhabricatorMacroMacroPHIDType.php @@ -9,7 +9,7 @@ public function getTypeName() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function getTypeIcon() { diff --git a/src/applications/macro/query/PhabricatorMacroQuery.php b/src/applications/macro/query/PhabricatorMacroQuery.php index 70e7f7e688..edfc97ef9d 100644 --- a/src/applications/macro/query/PhabricatorMacroQuery.php +++ b/src/applications/macro/query/PhabricatorMacroQuery.php @@ -230,7 +230,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function getOrderableColumns() { diff --git a/src/applications/macro/query/PhabricatorMacroSearchEngine.php b/src/applications/macro/query/PhabricatorMacroSearchEngine.php index 7ef57a6a10..e61f52b8b8 100644 --- a/src/applications/macro/query/PhabricatorMacroSearchEngine.php +++ b/src/applications/macro/query/PhabricatorMacroSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function newQuery() { diff --git a/src/applications/macro/typeahead/PhabricatorEmojiDatasource.php b/src/applications/macro/typeahead/PhabricatorEmojiDatasource.php index a0f0bda981..f967a37acc 100644 --- a/src/applications/macro/typeahead/PhabricatorEmojiDatasource.php +++ b/src/applications/macro/typeahead/PhabricatorEmojiDatasource.php @@ -11,7 +11,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function loadResults() { diff --git a/src/applications/macro/typeahead/PhabricatorMacroDatasource.php b/src/applications/macro/typeahead/PhabricatorMacroDatasource.php index b7b7efd630..c62adb041a 100644 --- a/src/applications/macro/typeahead/PhabricatorMacroDatasource.php +++ b/src/applications/macro/typeahead/PhabricatorMacroDatasource.php @@ -11,7 +11,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function loadResults() { diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index fc3c48b205..46877168e7 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -18,7 +18,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function isDefaultQuickCreateEngine() { diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index 01fc0af83d..a6a10b33ed 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -7,7 +7,7 @@ final class ManiphestTransactionEditor private $moreValidationErrors = array(); public function getEditorApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php b/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php index 1aa544de57..b43b914d96 100644 --- a/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php +++ b/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/maniphest/phid/ManiphestTaskPHIDType.php b/src/applications/maniphest/phid/ManiphestTaskPHIDType.php index 3b3c4a203f..1b0165fec7 100644 --- a/src/applications/maniphest/phid/ManiphestTaskPHIDType.php +++ b/src/applications/maniphest/phid/ManiphestTaskPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php index 8bbf976564..c206bd6599 100644 --- a/src/applications/maniphest/query/ManiphestTaskQuery.php +++ b/src/applications/maniphest/query/ManiphestTaskQuery.php @@ -1048,7 +1048,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } } diff --git a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php index 4c69c604e4..361cdca7e4 100644 --- a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php +++ b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php @@ -35,7 +35,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function newQuery() { diff --git a/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php index 68fb521980..7b9e6f91ed 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php index 85a201fdba..b08cae0a9d 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php index c4530f9bb3..db73f8d898 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function loadResults() { diff --git a/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php index 9d73ca994f..26bfacc520 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function loadResults() { diff --git a/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php index cfa5592ccc..b24a0cfaad 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function loadResults() { diff --git a/src/applications/meta/editor/PhabricatorApplicationEditEngine.php b/src/applications/meta/editor/PhabricatorApplicationEditEngine.php index 7cad5d9242..b6442ba3a8 100644 --- a/src/applications/meta/editor/PhabricatorApplicationEditEngine.php +++ b/src/applications/meta/editor/PhabricatorApplicationEditEngine.php @@ -6,7 +6,7 @@ final class PhabricatorApplicationEditEngine const ENGINECONST = 'application.application'; public function getEngineApplicationClass() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } public function getEngineName() { diff --git a/src/applications/meta/editor/PhabricatorApplicationEditor.php b/src/applications/meta/editor/PhabricatorApplicationEditor.php index 83003b4c27..99f291e6d8 100644 --- a/src/applications/meta/editor/PhabricatorApplicationEditor.php +++ b/src/applications/meta/editor/PhabricatorApplicationEditor.php @@ -4,7 +4,7 @@ final class PhabricatorApplicationEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php b/src/applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php index 946f56616d..b67ba1a023 100644 --- a/src/applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php +++ b/src/applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/meta/query/PhabricatorAppSearchEngine.php b/src/applications/meta/query/PhabricatorAppSearchEngine.php index 08283afc2e..b4d5d9b9c2 100644 --- a/src/applications/meta/query/PhabricatorAppSearchEngine.php +++ b/src/applications/meta/query/PhabricatorAppSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } public function getPageSize(PhabricatorSavedQuery $saved) { diff --git a/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php b/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php index acf9c8b32f..daf8494383 100644 --- a/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php +++ b/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } public function loadResults() { diff --git a/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php b/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php index 843e653039..601b3ffb89 100644 --- a/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php +++ b/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php @@ -4,7 +4,7 @@ final class PhabricatorMetaMTAApplicationEmailEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return pht('PhabricatorMetaMTAApplication'); + return PhabricatorMetaMTAApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php b/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php index 94c13c9e0a..2bfe376084 100644 --- a/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php +++ b/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php @@ -6,7 +6,7 @@ final class PhabricatorMailOutboundMailHeraldAdapter private $mail; public function getAdapterApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php b/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php index 1436038fae..e8fd037ad2 100644 --- a/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php +++ b/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php index 9c0c5d941a..80c2f91c3a 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php @@ -105,7 +105,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } } diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php index b7dd3e5ee4..4cb1e2dedc 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php @@ -41,7 +41,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } } diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php index d1f0235c90..dd7e961e24 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php @@ -112,7 +112,7 @@ public function newResultObject() { } public function getQueryApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } protected function shouldGroupQueryResultRows() { diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php b/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php index df7774aae2..d3ae165e2a 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php b/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php index 62c1275c39..117d0771c3 100644 --- a/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php +++ b/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php @@ -17,7 +17,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function loadResults() { diff --git a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php index 2e0e03bb3c..f9fa106e63 100644 --- a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php +++ b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php index b5d82c9749..4d17a8b840 100644 --- a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php +++ b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php @@ -13,7 +13,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/notification/query/PhabricatorNotificationQuery.php b/src/applications/notification/query/PhabricatorNotificationQuery.php index 2cd97f25c8..021d666b13 100644 --- a/src/applications/notification/query/PhabricatorNotificationQuery.php +++ b/src/applications/notification/query/PhabricatorNotificationQuery.php @@ -190,7 +190,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorNotificationsApplication'; + return PhabricatorNotificationsApplication::class; } } diff --git a/src/applications/notification/query/PhabricatorNotificationSearchEngine.php b/src/applications/notification/query/PhabricatorNotificationSearchEngine.php index 4b56c5f5a1..353ebe9402 100644 --- a/src/applications/notification/query/PhabricatorNotificationSearchEngine.php +++ b/src/applications/notification/query/PhabricatorNotificationSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorNotificationsApplication'; + return PhabricatorNotificationsApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/nuance/editor/NuanceItemEditor.php b/src/applications/nuance/editor/NuanceItemEditor.php index b41ca77563..4f2056d6ae 100644 --- a/src/applications/nuance/editor/NuanceItemEditor.php +++ b/src/applications/nuance/editor/NuanceItemEditor.php @@ -4,7 +4,7 @@ final class NuanceItemEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/nuance/editor/NuanceQueueEditEngine.php b/src/applications/nuance/editor/NuanceQueueEditEngine.php index 12f5c7b517..2a49079a47 100644 --- a/src/applications/nuance/editor/NuanceQueueEditEngine.php +++ b/src/applications/nuance/editor/NuanceQueueEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function newEditableObject() { diff --git a/src/applications/nuance/editor/NuanceQueueEditor.php b/src/applications/nuance/editor/NuanceQueueEditor.php index 2a18188f98..59abd3dd6d 100644 --- a/src/applications/nuance/editor/NuanceQueueEditor.php +++ b/src/applications/nuance/editor/NuanceQueueEditor.php @@ -4,7 +4,7 @@ final class NuanceQueueEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/nuance/editor/NuanceSourceEditEngine.php b/src/applications/nuance/editor/NuanceSourceEditEngine.php index eac751c3a5..882b9030fe 100644 --- a/src/applications/nuance/editor/NuanceSourceEditEngine.php +++ b/src/applications/nuance/editor/NuanceSourceEditEngine.php @@ -34,7 +34,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function newEditableObject() { diff --git a/src/applications/nuance/editor/NuanceSourceEditor.php b/src/applications/nuance/editor/NuanceSourceEditor.php index b56b183f9e..d141dd0108 100644 --- a/src/applications/nuance/editor/NuanceSourceEditor.php +++ b/src/applications/nuance/editor/NuanceSourceEditor.php @@ -4,7 +4,7 @@ final class NuanceSourceEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/nuance/phid/NuanceImportCursorPHIDType.php b/src/applications/nuance/phid/NuanceImportCursorPHIDType.php index 9d1f816a71..bc28710d37 100644 --- a/src/applications/nuance/phid/NuanceImportCursorPHIDType.php +++ b/src/applications/nuance/phid/NuanceImportCursorPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/nuance/phid/NuanceItemPHIDType.php b/src/applications/nuance/phid/NuanceItemPHIDType.php index 771b398419..c9491776ca 100644 --- a/src/applications/nuance/phid/NuanceItemPHIDType.php +++ b/src/applications/nuance/phid/NuanceItemPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/nuance/phid/NuanceQueuePHIDType.php b/src/applications/nuance/phid/NuanceQueuePHIDType.php index b51812320d..10cc4b707a 100644 --- a/src/applications/nuance/phid/NuanceQueuePHIDType.php +++ b/src/applications/nuance/phid/NuanceQueuePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/nuance/phid/NuanceSourcePHIDType.php b/src/applications/nuance/phid/NuanceSourcePHIDType.php index 774939bf29..17071684e8 100644 --- a/src/applications/nuance/phid/NuanceSourcePHIDType.php +++ b/src/applications/nuance/phid/NuanceSourcePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/nuance/query/NuanceItemSearchEngine.php b/src/applications/nuance/query/NuanceItemSearchEngine.php index 0868d7551a..4b65c73394 100644 --- a/src/applications/nuance/query/NuanceItemSearchEngine.php +++ b/src/applications/nuance/query/NuanceItemSearchEngine.php @@ -4,7 +4,7 @@ final class NuanceItemSearchEngine extends PhabricatorApplicationSearchEngine { public function getApplicationClassName() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getResultTypeDescription() { diff --git a/src/applications/nuance/query/NuanceQuery.php b/src/applications/nuance/query/NuanceQuery.php index 1e72f5501e..b8e05f4dfe 100644 --- a/src/applications/nuance/query/NuanceQuery.php +++ b/src/applications/nuance/query/NuanceQuery.php @@ -3,7 +3,7 @@ abstract class NuanceQuery extends PhabricatorCursorPagedPolicyAwareQuery { public function getQueryApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } } diff --git a/src/applications/nuance/query/NuanceQueueSearchEngine.php b/src/applications/nuance/query/NuanceQueueSearchEngine.php index 2f794c2a9c..7f3188b0c1 100644 --- a/src/applications/nuance/query/NuanceQueueSearchEngine.php +++ b/src/applications/nuance/query/NuanceQueueSearchEngine.php @@ -4,7 +4,7 @@ final class NuanceQueueSearchEngine extends PhabricatorApplicationSearchEngine { public function getApplicationClassName() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getResultTypeDescription() { diff --git a/src/applications/nuance/query/NuanceSourceSearchEngine.php b/src/applications/nuance/query/NuanceSourceSearchEngine.php index 44f131aa1b..7a1690eedf 100644 --- a/src/applications/nuance/query/NuanceSourceSearchEngine.php +++ b/src/applications/nuance/query/NuanceSourceSearchEngine.php @@ -4,7 +4,7 @@ final class NuanceSourceSearchEngine extends PhabricatorApplicationSearchEngine { public function getApplicationClassName() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getResultTypeDescription() { diff --git a/src/applications/nuance/typeahead/NuanceQueueDatasource.php b/src/applications/nuance/typeahead/NuanceQueueDatasource.php index 15b01fcecd..916dfaa75f 100644 --- a/src/applications/nuance/typeahead/NuanceQueueDatasource.php +++ b/src/applications/nuance/typeahead/NuanceQueueDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function loadResults() { diff --git a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php index ad47552c19..276863b7f3 100644 --- a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php +++ b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } protected function newEditableObject() { diff --git a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php index 32b9d45054..27454e8777 100644 --- a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php +++ b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php @@ -4,7 +4,7 @@ final class PhabricatorOAuthServerEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/oauthserver/phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php b/src/applications/oauthserver/phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php index b2fc1554fd..071fa5a8f6 100644 --- a/src/applications/oauthserver/phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php +++ b/src/applications/oauthserver/phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php b/src/applications/oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php index 4d3d64738b..0fb623903b 100644 --- a/src/applications/oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php +++ b/src/applications/oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php index 63a62e8cd9..6a431daf3a 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php @@ -79,7 +79,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } } diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php index 46fc514824..e500b3f42f 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } } diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php index e07b1ea2c2..064ff34a26 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php index 416f2e38f2..7defcaacc0 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php @@ -18,7 +18,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } protected function newEditableObject() { diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php index 8885872706..797fe1ff44 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorOwnersPackageTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php b/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php index fbff6a2103..a780c31709 100644 --- a/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php +++ b/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php index a80b972548..71ba6ebd52 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php @@ -284,7 +284,7 @@ protected function newPagingMapFromPartialObject($object) { } public function getQueryApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php b/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php index 26889c0e7a..d0f2790ba7 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function newQuery() { diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php index e93b3df7e9..8ad4db33f6 100644 --- a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php +++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function loadResults() { diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php index 2ad0512ee2..a8f4c12b19 100644 --- a/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php +++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php index 2a0b16e60f..ee60a85615 100644 --- a/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php +++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/packages/editor/PhabricatorPackagesEditEngine.php b/src/applications/packages/editor/PhabricatorPackagesEditEngine.php index 1ba71d7274..3bd4d29395 100644 --- a/src/applications/packages/editor/PhabricatorPackagesEditEngine.php +++ b/src/applications/packages/editor/PhabricatorPackagesEditEngine.php @@ -8,7 +8,7 @@ public function isEngineConfigurable() { } public function getEngineApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } } diff --git a/src/applications/packages/editor/PhabricatorPackagesEditor.php b/src/applications/packages/editor/PhabricatorPackagesEditor.php index 492b14643a..10d4c608a3 100644 --- a/src/applications/packages/editor/PhabricatorPackagesEditor.php +++ b/src/applications/packages/editor/PhabricatorPackagesEditor.php @@ -4,7 +4,7 @@ abstract class PhabricatorPackagesEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function supportsSearch() { diff --git a/src/applications/packages/phid/PhabricatorPackagesPackagePHIDType.php b/src/applications/packages/phid/PhabricatorPackagesPackagePHIDType.php index 6f8e982c81..3f8ba658c9 100644 --- a/src/applications/packages/phid/PhabricatorPackagesPackagePHIDType.php +++ b/src/applications/packages/phid/PhabricatorPackagesPackagePHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/packages/phid/PhabricatorPackagesPublisherPHIDType.php b/src/applications/packages/phid/PhabricatorPackagesPublisherPHIDType.php index f746ddce22..c63f27652c 100644 --- a/src/applications/packages/phid/PhabricatorPackagesPublisherPHIDType.php +++ b/src/applications/packages/phid/PhabricatorPackagesPublisherPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/packages/phid/PhabricatorPackagesVersionPHIDType.php b/src/applications/packages/phid/PhabricatorPackagesVersionPHIDType.php index 203cca20e0..ffad58ffcf 100644 --- a/src/applications/packages/phid/PhabricatorPackagesVersionPHIDType.php +++ b/src/applications/packages/phid/PhabricatorPackagesVersionPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/packages/query/PhabricatorPackagesPackageSearchEngine.php b/src/applications/packages/query/PhabricatorPackagesPackageSearchEngine.php index 817321dec1..5c8c465085 100644 --- a/src/applications/packages/query/PhabricatorPackagesPackageSearchEngine.php +++ b/src/applications/packages/query/PhabricatorPackagesPackageSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function newQuery() { diff --git a/src/applications/packages/query/PhabricatorPackagesPublisherSearchEngine.php b/src/applications/packages/query/PhabricatorPackagesPublisherSearchEngine.php index be7b83f5fc..03e2e968a4 100644 --- a/src/applications/packages/query/PhabricatorPackagesPublisherSearchEngine.php +++ b/src/applications/packages/query/PhabricatorPackagesPublisherSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function newQuery() { diff --git a/src/applications/packages/query/PhabricatorPackagesQuery.php b/src/applications/packages/query/PhabricatorPackagesQuery.php index e7e882bdad..1ce60d9486 100644 --- a/src/applications/packages/query/PhabricatorPackagesQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesQuery.php @@ -4,7 +4,7 @@ abstract class PhabricatorPackagesQuery extends PhabricatorCursorPagedPolicyAwareQuery { public function getQueryApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function buildFullKeyClauseParts( diff --git a/src/applications/packages/query/PhabricatorPackagesVersionSearchEngine.php b/src/applications/packages/query/PhabricatorPackagesVersionSearchEngine.php index b3592ffd70..464b423ee4 100644 --- a/src/applications/packages/query/PhabricatorPackagesVersionSearchEngine.php +++ b/src/applications/packages/query/PhabricatorPackagesVersionSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function newQuery() { diff --git a/src/applications/packages/typeahead/PhabricatorPackagesPackageDatasource.php b/src/applications/packages/typeahead/PhabricatorPackagesPackageDatasource.php index 9b2296db12..f9e88c7ac0 100644 --- a/src/applications/packages/typeahead/PhabricatorPackagesPackageDatasource.php +++ b/src/applications/packages/typeahead/PhabricatorPackagesPackageDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function loadResults() { diff --git a/src/applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php b/src/applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php index e8ca7d4c7f..d8fe5ffc52 100644 --- a/src/applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php +++ b/src/applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function loadResults() { diff --git a/src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php b/src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php index 1e23c874cf..ead8658543 100644 --- a/src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php +++ b/src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php @@ -4,7 +4,7 @@ final class PassphraseCredentialTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPassphraseApplication'; + return PhabricatorPassphraseApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/passphrase/phid/PassphraseCredentialPHIDType.php b/src/applications/passphrase/phid/PassphraseCredentialPHIDType.php index 25681906e3..76fb537554 100644 --- a/src/applications/passphrase/phid/PassphraseCredentialPHIDType.php +++ b/src/applications/passphrase/phid/PassphraseCredentialPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPassphraseApplication'; + return PhabricatorPassphraseApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/passphrase/query/PassphraseCredentialQuery.php b/src/applications/passphrase/query/PassphraseCredentialQuery.php index 98ae9429a8..33a6947324 100644 --- a/src/applications/passphrase/query/PassphraseCredentialQuery.php +++ b/src/applications/passphrase/query/PassphraseCredentialQuery.php @@ -155,7 +155,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPassphraseApplication'; + return PhabricatorPassphraseApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php b/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php index 971094afc4..24cc6609ff 100644 --- a/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php +++ b/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPassphraseApplication'; + return PhabricatorPassphraseApplication::class; } public function newQuery() { diff --git a/src/applications/paste/editor/PhabricatorPasteEditEngine.php b/src/applications/paste/editor/PhabricatorPasteEditEngine.php index 146565e87e..31859e7530 100644 --- a/src/applications/paste/editor/PhabricatorPasteEditEngine.php +++ b/src/applications/paste/editor/PhabricatorPasteEditEngine.php @@ -18,7 +18,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } protected function newEditableObject() { diff --git a/src/applications/paste/editor/PhabricatorPasteEditor.php b/src/applications/paste/editor/PhabricatorPasteEditor.php index b120f58a57..1af612b08e 100644 --- a/src/applications/paste/editor/PhabricatorPasteEditor.php +++ b/src/applications/paste/editor/PhabricatorPasteEditor.php @@ -10,7 +10,7 @@ public function getNewPasteTitle() { } public function getEditorApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/paste/phid/PhabricatorPastePastePHIDType.php b/src/applications/paste/phid/PhabricatorPastePastePHIDType.php index d08d52e2e1..b04553aa14 100644 --- a/src/applications/paste/phid/PhabricatorPastePastePHIDType.php +++ b/src/applications/paste/phid/PhabricatorPastePastePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/paste/query/PhabricatorPasteQuery.php b/src/applications/paste/query/PhabricatorPasteQuery.php index e841bff300..bcc1560700 100644 --- a/src/applications/paste/query/PhabricatorPasteQuery.php +++ b/src/applications/paste/query/PhabricatorPasteQuery.php @@ -390,7 +390,7 @@ private function highlightSource($source, $title, $language) { } public function getQueryApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } } diff --git a/src/applications/paste/query/PhabricatorPasteSearchEngine.php b/src/applications/paste/query/PhabricatorPasteSearchEngine.php index e269088f60..88482b94cc 100644 --- a/src/applications/paste/query/PhabricatorPasteSearchEngine.php +++ b/src/applications/paste/query/PhabricatorPasteSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } public function newQuery() { diff --git a/src/applications/paste/typeahead/PasteLanguageSelectDatasource.php b/src/applications/paste/typeahead/PasteLanguageSelectDatasource.php index 45c36db968..4bdf0b545c 100644 --- a/src/applications/paste/typeahead/PasteLanguageSelectDatasource.php +++ b/src/applications/paste/typeahead/PasteLanguageSelectDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } public function loadResults() { diff --git a/src/applications/people/editor/PhabricatorUserEditEngine.php b/src/applications/people/editor/PhabricatorUserEditEngine.php index 4b3a60a2fc..05738145f9 100644 --- a/src/applications/people/editor/PhabricatorUserEditEngine.php +++ b/src/applications/people/editor/PhabricatorUserEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function newEditableObject() { diff --git a/src/applications/people/editor/PhabricatorUserTransactionEditor.php b/src/applications/people/editor/PhabricatorUserTransactionEditor.php index 929b2e224c..88c27d7ee8 100644 --- a/src/applications/people/editor/PhabricatorUserTransactionEditor.php +++ b/src/applications/people/editor/PhabricatorUserTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorUserTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php b/src/applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php index acd9a24f56..2e2862892f 100644 --- a/src/applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php +++ b/src/applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/people/phid/PhabricatorPeopleExternalPHIDType.php b/src/applications/people/phid/PhabricatorPeopleExternalPHIDType.php index 4be4cc5359..ebda57c423 100644 --- a/src/applications/people/phid/PhabricatorPeopleExternalPHIDType.php +++ b/src/applications/people/phid/PhabricatorPeopleExternalPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/people/phid/PhabricatorPeopleUserEmailPHIDType.php b/src/applications/people/phid/PhabricatorPeopleUserEmailPHIDType.php index e0f0478033..206b27d77e 100644 --- a/src/applications/people/phid/PhabricatorPeopleUserEmailPHIDType.php +++ b/src/applications/people/phid/PhabricatorPeopleUserEmailPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php b/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php index 7867f098f1..65139814b7 100644 --- a/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php +++ b/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/people/query/PhabricatorPeopleLogQuery.php b/src/applications/people/query/PhabricatorPeopleLogQuery.php index e87fb33660..d439894e5c 100644 --- a/src/applications/people/query/PhabricatorPeopleLogQuery.php +++ b/src/applications/people/query/PhabricatorPeopleLogQuery.php @@ -129,7 +129,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } } diff --git a/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php b/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php index 7e7fca1cd4..fc1819debc 100644 --- a/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php +++ b/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getPageSize(PhabricatorSavedQuery $saved) { diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index b74b936ba8..acbeb3b34f 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -360,7 +360,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getOrderableColumns() { diff --git a/src/applications/people/query/PhabricatorPeopleSearchEngine.php b/src/applications/people/query/PhabricatorPeopleSearchEngine.php index bdeab953ec..1e0c0052c1 100644 --- a/src/applications/people/query/PhabricatorPeopleSearchEngine.php +++ b/src/applications/people/query/PhabricatorPeopleSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function newQuery() { diff --git a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php index ead44a56dc..178a3b9460 100644 --- a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php +++ b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php @@ -71,7 +71,7 @@ protected function willLoadPage(array $page) { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } } diff --git a/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php index 2a2a451a49..5a8e70f1b8 100644 --- a/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/people/typeahead/PhabricatorPeopleDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleDatasource.php index d4a5ad96c7..c06ba5b458 100644 --- a/src/applications/people/typeahead/PhabricatorPeopleDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function loadResults() { diff --git a/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php index fb3a6226d9..cd3f2939b9 100644 --- a/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/people/typeahead/PhabricatorUserLogTypeDatasource.php b/src/applications/people/typeahead/PhabricatorUserLogTypeDatasource.php index 39241a020c..26ccdfe2ea 100644 --- a/src/applications/people/typeahead/PhabricatorUserLogTypeDatasource.php +++ b/src/applications/people/typeahead/PhabricatorUserLogTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function loadResults() { diff --git a/src/applications/people/typeahead/PhabricatorViewerDatasource.php b/src/applications/people/typeahead/PhabricatorViewerDatasource.php index e29fd4586b..6f6d1181fe 100644 --- a/src/applications/people/typeahead/PhabricatorViewerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorViewerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/phame/editor/PhameBlogEditEngine.php b/src/applications/phame/editor/PhameBlogEditEngine.php index e11dec6847..9238f7ea01 100644 --- a/src/applications/phame/editor/PhameBlogEditEngine.php +++ b/src/applications/phame/editor/PhameBlogEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phame/editor/PhameBlogEditor.php b/src/applications/phame/editor/PhameBlogEditor.php index 656ede95fe..eaf1389f17 100644 --- a/src/applications/phame/editor/PhameBlogEditor.php +++ b/src/applications/phame/editor/PhameBlogEditor.php @@ -4,7 +4,7 @@ final class PhameBlogEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phame/editor/PhamePostEditEngine.php b/src/applications/phame/editor/PhamePostEditEngine.php index 04738beb80..651f29b091 100644 --- a/src/applications/phame/editor/PhamePostEditEngine.php +++ b/src/applications/phame/editor/PhamePostEditEngine.php @@ -25,7 +25,7 @@ public function setBlog(PhameBlog $blog) { } public function getEngineApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } protected function newEditableObject() { diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index 61092114d1..60ea50aeec 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -4,7 +4,7 @@ final class PhamePostEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phame/herald/HeraldPhameBlogAdapter.php b/src/applications/phame/herald/HeraldPhameBlogAdapter.php index d9368a97d2..160387d24c 100644 --- a/src/applications/phame/herald/HeraldPhameBlogAdapter.php +++ b/src/applications/phame/herald/HeraldPhameBlogAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/phame/herald/HeraldPhamePostAdapter.php b/src/applications/phame/herald/HeraldPhamePostAdapter.php index 72e7124271..f9446c6866 100644 --- a/src/applications/phame/herald/HeraldPhamePostAdapter.php +++ b/src/applications/phame/herald/HeraldPhamePostAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/phame/phid/PhabricatorPhameBlogPHIDType.php b/src/applications/phame/phid/PhabricatorPhameBlogPHIDType.php index 905f4b893b..ccca276032 100644 --- a/src/applications/phame/phid/PhabricatorPhameBlogPHIDType.php +++ b/src/applications/phame/phid/PhabricatorPhameBlogPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phame/phid/PhabricatorPhamePostPHIDType.php b/src/applications/phame/phid/PhabricatorPhamePostPHIDType.php index 7c86d89d41..174043d206 100644 --- a/src/applications/phame/phid/PhabricatorPhamePostPHIDType.php +++ b/src/applications/phame/phid/PhabricatorPhamePostPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phame/query/PhameBlogSearchEngine.php b/src/applications/phame/query/PhameBlogSearchEngine.php index eaef32af1c..4b8c287173 100644 --- a/src/applications/phame/query/PhameBlogSearchEngine.php +++ b/src/applications/phame/query/PhameBlogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function newQuery() { diff --git a/src/applications/phame/query/PhamePostSearchEngine.php b/src/applications/phame/query/PhamePostSearchEngine.php index d1d9a791ec..1cff6d1891 100644 --- a/src/applications/phame/query/PhamePostSearchEngine.php +++ b/src/applications/phame/query/PhamePostSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function newQuery() { diff --git a/src/applications/phame/typeahead/PhameBlogDatasource.php b/src/applications/phame/typeahead/PhameBlogDatasource.php index 0658dec3b9..87d673b544 100644 --- a/src/applications/phame/typeahead/PhameBlogDatasource.php +++ b/src/applications/phame/typeahead/PhameBlogDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function loadResults() { diff --git a/src/applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php b/src/applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php index 93d8a47de7..d38d65f1a4 100644 --- a/src/applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php +++ b/src/applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phlux/editor/PhluxVariableEditor.php b/src/applications/phlux/editor/PhluxVariableEditor.php index 2e36ba8162..5784d19d80 100644 --- a/src/applications/phlux/editor/PhluxVariableEditor.php +++ b/src/applications/phlux/editor/PhluxVariableEditor.php @@ -4,7 +4,7 @@ final class PhluxVariableEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhluxApplication'; + return PhabricatorPhluxApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phlux/phid/PhluxVariablePHIDType.php b/src/applications/phlux/phid/PhluxVariablePHIDType.php index 97525c4052..89e244ee75 100644 --- a/src/applications/phlux/phid/PhluxVariablePHIDType.php +++ b/src/applications/phlux/phid/PhluxVariablePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhluxApplication'; + return PhabricatorPhluxApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phlux/query/PhluxVariableQuery.php b/src/applications/phlux/query/PhluxVariableQuery.php index 8ec4bc9334..27ffd9875f 100644 --- a/src/applications/phlux/query/PhluxVariableQuery.php +++ b/src/applications/phlux/query/PhluxVariableQuery.php @@ -89,7 +89,7 @@ protected function newPagingMapFromPartialObject($object) { } public function getQueryApplicationClass() { - return 'PhabricatorPhluxApplication'; + return PhabricatorPhluxApplication::class; } } diff --git a/src/applications/pholio/editor/PholioMockEditor.php b/src/applications/pholio/editor/PholioMockEditor.php index df5d55672c..6eeb83c1bd 100644 --- a/src/applications/pholio/editor/PholioMockEditor.php +++ b/src/applications/pholio/editor/PholioMockEditor.php @@ -5,7 +5,7 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { private $images = array(); public function getEditorApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/pholio/herald/HeraldPholioMockAdapter.php b/src/applications/pholio/herald/HeraldPholioMockAdapter.php index 5520a60cdc..ed1d2daea1 100644 --- a/src/applications/pholio/herald/HeraldPholioMockAdapter.php +++ b/src/applications/pholio/herald/HeraldPholioMockAdapter.php @@ -5,7 +5,7 @@ final class HeraldPholioMockAdapter extends HeraldAdapter { private $mock; public function getAdapterApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/pholio/phid/PholioImagePHIDType.php b/src/applications/pholio/phid/PholioImagePHIDType.php index d7a9984fd5..ffbb3f71bf 100644 --- a/src/applications/pholio/phid/PholioImagePHIDType.php +++ b/src/applications/pholio/phid/PholioImagePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/pholio/phid/PholioMockPHIDType.php b/src/applications/pholio/phid/PholioMockPHIDType.php index 13ee4cb81f..bfdecae90d 100644 --- a/src/applications/pholio/phid/PholioMockPHIDType.php +++ b/src/applications/pholio/phid/PholioMockPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/pholio/query/PholioImageQuery.php b/src/applications/pholio/query/PholioImageQuery.php index 69d890ab98..44bee77205 100644 --- a/src/applications/pholio/query/PholioImageQuery.php +++ b/src/applications/pholio/query/PholioImageQuery.php @@ -152,7 +152,7 @@ protected function didFilterPage(array $images) { } public function getQueryApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } } diff --git a/src/applications/pholio/query/PholioMockQuery.php b/src/applications/pholio/query/PholioMockQuery.php index 4820ffd8eb..24d56a3b33 100644 --- a/src/applications/pholio/query/PholioMockQuery.php +++ b/src/applications/pholio/query/PholioMockQuery.php @@ -152,7 +152,7 @@ protected function didFilterPage(array $mocks) { } public function getQueryApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/pholio/query/PholioMockSearchEngine.php b/src/applications/pholio/query/PholioMockSearchEngine.php index cbb175b2de..104533eee2 100644 --- a/src/applications/pholio/query/PholioMockSearchEngine.php +++ b/src/applications/pholio/query/PholioMockSearchEngine.php @@ -7,7 +7,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } public function newQuery() { diff --git a/src/applications/phortune/editor/PhortuneAccountEditEngine.php b/src/applications/phortune/editor/PhortuneAccountEditEngine.php index d7272965c2..20df9d16d9 100644 --- a/src/applications/phortune/editor/PhortuneAccountEditEngine.php +++ b/src/applications/phortune/editor/PhortuneAccountEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phortune/editor/PhortuneAccountEditor.php b/src/applications/phortune/editor/PhortuneAccountEditor.php index 8344bc2b6e..534c701616 100644 --- a/src/applications/phortune/editor/PhortuneAccountEditor.php +++ b/src/applications/phortune/editor/PhortuneAccountEditor.php @@ -4,7 +4,7 @@ final class PhortuneAccountEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php b/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php index c732e4215f..9bfed006b1 100644 --- a/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php +++ b/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php @@ -21,7 +21,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phortune/editor/PhortuneAccountEmailEditor.php b/src/applications/phortune/editor/PhortuneAccountEmailEditor.php index 40d12a97ba..986d8cdbb6 100644 --- a/src/applications/phortune/editor/PhortuneAccountEmailEditor.php +++ b/src/applications/phortune/editor/PhortuneAccountEmailEditor.php @@ -4,7 +4,7 @@ final class PhortuneAccountEmailEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortuneCartEditor.php b/src/applications/phortune/editor/PhortuneCartEditor.php index aef59f059a..72d7ba6ef9 100644 --- a/src/applications/phortune/editor/PhortuneCartEditor.php +++ b/src/applications/phortune/editor/PhortuneCartEditor.php @@ -19,7 +19,7 @@ public function isInvoice() { } public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortuneMerchantEditEngine.php b/src/applications/phortune/editor/PhortuneMerchantEditEngine.php index e09fba99ab..51e93f9c94 100644 --- a/src/applications/phortune/editor/PhortuneMerchantEditEngine.php +++ b/src/applications/phortune/editor/PhortuneMerchantEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phortune/editor/PhortuneMerchantEditor.php b/src/applications/phortune/editor/PhortuneMerchantEditor.php index 79fc7d534e..38a844c3ff 100644 --- a/src/applications/phortune/editor/PhortuneMerchantEditor.php +++ b/src/applications/phortune/editor/PhortuneMerchantEditor.php @@ -4,7 +4,7 @@ final class PhortuneMerchantEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortunePaymentMethodEditor.php b/src/applications/phortune/editor/PhortunePaymentMethodEditor.php index 4b6c8cedb3..629958dad1 100644 --- a/src/applications/phortune/editor/PhortunePaymentMethodEditor.php +++ b/src/applications/phortune/editor/PhortunePaymentMethodEditor.php @@ -4,7 +4,7 @@ final class PhortunePaymentMethodEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php b/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php index 96ac6e830a..84d7cff9ee 100644 --- a/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php +++ b/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php @@ -4,7 +4,7 @@ final class PhortunePaymentProviderConfigEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortuneSubscriptionEditor.php b/src/applications/phortune/editor/PhortuneSubscriptionEditor.php index a2314f5650..4e9c7dc919 100644 --- a/src/applications/phortune/editor/PhortuneSubscriptionEditor.php +++ b/src/applications/phortune/editor/PhortuneSubscriptionEditor.php @@ -4,7 +4,7 @@ final class PhortuneSubscriptionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php b/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php index fccd50cf16..3ae2f295e5 100644 --- a/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php +++ b/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneAccountPHIDType.php b/src/applications/phortune/phid/PhortuneAccountPHIDType.php index 90632a980d..7f38be410c 100644 --- a/src/applications/phortune/phid/PhortuneAccountPHIDType.php +++ b/src/applications/phortune/phid/PhortuneAccountPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneCartPHIDType.php b/src/applications/phortune/phid/PhortuneCartPHIDType.php index c805a4a921..eceddfe2ad 100644 --- a/src/applications/phortune/phid/PhortuneCartPHIDType.php +++ b/src/applications/phortune/phid/PhortuneCartPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneChargePHIDType.php b/src/applications/phortune/phid/PhortuneChargePHIDType.php index 013db6ab1c..b41d64b5b6 100644 --- a/src/applications/phortune/phid/PhortuneChargePHIDType.php +++ b/src/applications/phortune/phid/PhortuneChargePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneMerchantPHIDType.php b/src/applications/phortune/phid/PhortuneMerchantPHIDType.php index 69b93582b3..0f4fe43174 100644 --- a/src/applications/phortune/phid/PhortuneMerchantPHIDType.php +++ b/src/applications/phortune/phid/PhortuneMerchantPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortunePaymentMethodPHIDType.php b/src/applications/phortune/phid/PhortunePaymentMethodPHIDType.php index 7de421a7b4..846c3c2cbc 100644 --- a/src/applications/phortune/phid/PhortunePaymentMethodPHIDType.php +++ b/src/applications/phortune/phid/PhortunePaymentMethodPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php b/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php index dc96d08648..74b4a05a69 100644 --- a/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php +++ b/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneProductPHIDType.php b/src/applications/phortune/phid/PhortuneProductPHIDType.php index 409377186f..3d79471b1f 100644 --- a/src/applications/phortune/phid/PhortuneProductPHIDType.php +++ b/src/applications/phortune/phid/PhortuneProductPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortunePurchasePHIDType.php b/src/applications/phortune/phid/PhortunePurchasePHIDType.php index 08f88b1d33..e1faad923b 100644 --- a/src/applications/phortune/phid/PhortunePurchasePHIDType.php +++ b/src/applications/phortune/phid/PhortunePurchasePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php b/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php index e07dce12f4..caf505fa55 100644 --- a/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php +++ b/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/query/PhortuneAccountEmailQuery.php b/src/applications/phortune/query/PhortuneAccountEmailQuery.php index 0e0a668b8c..3e2aee1f6a 100644 --- a/src/applications/phortune/query/PhortuneAccountEmailQuery.php +++ b/src/applications/phortune/query/PhortuneAccountEmailQuery.php @@ -103,7 +103,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/phortune/query/PhortuneAccountQuery.php b/src/applications/phortune/query/PhortuneAccountQuery.php index 43c7c5f976..5defc2d9d6 100644 --- a/src/applications/phortune/query/PhortuneAccountQuery.php +++ b/src/applications/phortune/query/PhortuneAccountQuery.php @@ -124,7 +124,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/phortune/query/PhortuneCartQuery.php b/src/applications/phortune/query/PhortuneCartQuery.php index 0b3325b932..e54578731f 100644 --- a/src/applications/phortune/query/PhortuneCartQuery.php +++ b/src/applications/phortune/query/PhortuneCartQuery.php @@ -217,7 +217,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortuneCartSearchEngine.php b/src/applications/phortune/query/PhortuneCartSearchEngine.php index a4a0f2848d..b017591fb8 100644 --- a/src/applications/phortune/query/PhortuneCartSearchEngine.php +++ b/src/applications/phortune/query/PhortuneCartSearchEngine.php @@ -44,7 +44,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/phortune/query/PhortuneChargeQuery.php b/src/applications/phortune/query/PhortuneChargeQuery.php index a7eda9d6a6..a0f6026fa6 100644 --- a/src/applications/phortune/query/PhortuneChargeQuery.php +++ b/src/applications/phortune/query/PhortuneChargeQuery.php @@ -138,7 +138,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortuneChargeSearchEngine.php b/src/applications/phortune/query/PhortuneChargeSearchEngine.php index e1fb5a47ba..568d60fc1a 100644 --- a/src/applications/phortune/query/PhortuneChargeSearchEngine.php +++ b/src/applications/phortune/query/PhortuneChargeSearchEngine.php @@ -24,7 +24,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/phortune/query/PhortuneMerchantQuery.php b/src/applications/phortune/query/PhortuneMerchantQuery.php index 2c9aefc74d..19b32d1338 100644 --- a/src/applications/phortune/query/PhortuneMerchantQuery.php +++ b/src/applications/phortune/query/PhortuneMerchantQuery.php @@ -118,7 +118,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/phortune/query/PhortuneMerchantSearchEngine.php b/src/applications/phortune/query/PhortuneMerchantSearchEngine.php index a818f37d28..0688fa5344 100644 --- a/src/applications/phortune/query/PhortuneMerchantSearchEngine.php +++ b/src/applications/phortune/query/PhortuneMerchantSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/phortune/query/PhortunePaymentMethodQuery.php b/src/applications/phortune/query/PhortunePaymentMethodQuery.php index b95881d3a7..aeea908506 100644 --- a/src/applications/phortune/query/PhortunePaymentMethodQuery.php +++ b/src/applications/phortune/query/PhortunePaymentMethodQuery.php @@ -140,7 +140,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php index a850acec28..eb478a2997 100644 --- a/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php +++ b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php @@ -89,7 +89,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortuneProductQuery.php b/src/applications/phortune/query/PhortuneProductQuery.php index 30701d4e7b..d479c37843 100644 --- a/src/applications/phortune/query/PhortuneProductQuery.php +++ b/src/applications/phortune/query/PhortuneProductQuery.php @@ -114,7 +114,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortunePurchaseQuery.php b/src/applications/phortune/query/PhortunePurchaseQuery.php index 275537c351..7ed5d1c9a6 100644 --- a/src/applications/phortune/query/PhortunePurchaseQuery.php +++ b/src/applications/phortune/query/PhortunePurchaseQuery.php @@ -104,7 +104,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortuneSubscriptionQuery.php b/src/applications/phortune/query/PhortuneSubscriptionQuery.php index 5622578738..c848f74333 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionQuery.php +++ b/src/applications/phortune/query/PhortuneSubscriptionQuery.php @@ -194,7 +194,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php b/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php index 0d2e720aa7..9da6f34c72 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php +++ b/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php @@ -34,7 +34,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/phrequent/query/PhrequentSearchEngine.php b/src/applications/phrequent/query/PhrequentSearchEngine.php index d137c40b64..2cf1bb7d4c 100644 --- a/src/applications/phrequent/query/PhrequentSearchEngine.php +++ b/src/applications/phrequent/query/PhrequentSearchEngine.php @@ -7,7 +7,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhrequentApplication'; + return PhabricatorPhrequentApplication::class; } public function getPageSize(PhabricatorSavedQuery $saved) { diff --git a/src/applications/phrequent/query/PhrequentUserTimeQuery.php b/src/applications/phrequent/query/PhrequentUserTimeQuery.php index 6400771a00..7bbd267c00 100644 --- a/src/applications/phrequent/query/PhrequentUserTimeQuery.php +++ b/src/applications/phrequent/query/PhrequentUserTimeQuery.php @@ -326,7 +326,7 @@ public static function getUserTimeSpentOnObject( } public function getQueryApplicationClass() { - return 'PhabricatorPhrequentApplication'; + return PhabricatorPhrequentApplication::class; } } diff --git a/src/applications/phriction/editor/PhrictionDocumentEditEngine.php b/src/applications/phriction/editor/PhrictionDocumentEditEngine.php index 22f56f2f57..a9c42216bc 100644 --- a/src/applications/phriction/editor/PhrictionDocumentEditEngine.php +++ b/src/applications/phriction/editor/PhrictionDocumentEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } protected function newEditableObject() { diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index d59541c401..da6bd30b6c 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -90,7 +90,7 @@ public function setShouldPublishContent( } public function getEditorApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php b/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php index 74de97d5fc..7eae636b3c 100644 --- a/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php +++ b/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php @@ -5,7 +5,7 @@ final class PhrictionDocumentHeraldAdapter extends HeraldAdapter { private $document; public function getAdapterApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/phriction/phid/PhrictionContentPHIDType.php b/src/applications/phriction/phid/PhrictionContentPHIDType.php index b8f39c0ef4..4db60601d9 100644 --- a/src/applications/phriction/phid/PhrictionContentPHIDType.php +++ b/src/applications/phriction/phid/PhrictionContentPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phriction/phid/PhrictionDocumentPHIDType.php b/src/applications/phriction/phid/PhrictionDocumentPHIDType.php index 57afdb84d6..4dcb039121 100644 --- a/src/applications/phriction/phid/PhrictionDocumentPHIDType.php +++ b/src/applications/phriction/phid/PhrictionDocumentPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phriction/query/PhrictionContentQuery.php b/src/applications/phriction/query/PhrictionContentQuery.php index 8ac92be351..ded328ea25 100644 --- a/src/applications/phriction/query/PhrictionContentQuery.php +++ b/src/applications/phriction/query/PhrictionContentQuery.php @@ -118,7 +118,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } } diff --git a/src/applications/phriction/query/PhrictionContentSearchEngine.php b/src/applications/phriction/query/PhrictionContentSearchEngine.php index f7cd9be2b6..5bdbaa7535 100644 --- a/src/applications/phriction/query/PhrictionContentSearchEngine.php +++ b/src/applications/phriction/query/PhrictionContentSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function newQuery() { diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php index 298db97dbb..491458a128 100644 --- a/src/applications/phriction/query/PhrictionDocumentQuery.php +++ b/src/applications/phriction/query/PhrictionDocumentQuery.php @@ -392,7 +392,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } } diff --git a/src/applications/phriction/query/PhrictionDocumentSearchEngine.php b/src/applications/phriction/query/PhrictionDocumentSearchEngine.php index 49a332268b..cf59159298 100644 --- a/src/applications/phriction/query/PhrictionDocumentSearchEngine.php +++ b/src/applications/phriction/query/PhrictionDocumentSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function newQuery() { diff --git a/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php b/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php index 8e16051840..fbc48af97d 100644 --- a/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php +++ b/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function loadResults() { diff --git a/src/applications/phurl/editor/PhabricatorPhurlURLEditEngine.php b/src/applications/phurl/editor/PhabricatorPhurlURLEditEngine.php index 12a4d2672f..efadadd745 100644 --- a/src/applications/phurl/editor/PhabricatorPhurlURLEditEngine.php +++ b/src/applications/phurl/editor/PhabricatorPhurlURLEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php index 49f290c343..a67a1c425d 100644 --- a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php +++ b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php @@ -4,7 +4,7 @@ final class PhabricatorPhurlURLEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php b/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php index 979b25c791..14bdb11df5 100644 --- a/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php +++ b/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phurl/query/PhabricatorPhurlURLQuery.php b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php index c30cedf09d..cc82264c45 100644 --- a/src/applications/phurl/query/PhabricatorPhurlURLQuery.php +++ b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php @@ -103,6 +103,6 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } } diff --git a/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php b/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php index 8814b25b9c..a3a38c8ee9 100644 --- a/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php +++ b/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } public function newQuery() { diff --git a/src/applications/phurl/typeahead/PhabricatorPhurlURLDatasource.php b/src/applications/phurl/typeahead/PhabricatorPhurlURLDatasource.php index f8d3cda0cd..77eabd8a28 100644 --- a/src/applications/phurl/typeahead/PhabricatorPhurlURLDatasource.php +++ b/src/applications/phurl/typeahead/PhabricatorPhurlURLDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } public function loadResults() { diff --git a/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php b/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php index ed75561327..afc84dd853 100644 --- a/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php +++ b/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPolicyApplication'; + return PhabricatorPolicyApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/policy/query/PhabricatorPolicyQuery.php b/src/applications/policy/query/PhabricatorPolicyQuery.php index 9bd6ba5994..58e03d325f 100644 --- a/src/applications/policy/query/PhabricatorPolicyQuery.php +++ b/src/applications/policy/query/PhabricatorPolicyQuery.php @@ -286,7 +286,7 @@ protected function shouldDisablePolicyFiltering() { } public function getQueryApplicationClass() { - return 'PhabricatorPolicyApplication'; + return PhabricatorPolicyApplication::class; } public static function isSpecialPolicy($identifier) { diff --git a/src/applications/ponder/editor/PonderEditor.php b/src/applications/ponder/editor/PonderEditor.php index fcfa981f16..76615175e9 100644 --- a/src/applications/ponder/editor/PonderEditor.php +++ b/src/applications/ponder/editor/PonderEditor.php @@ -4,7 +4,7 @@ abstract class PonderEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } protected function getMailSubjectPrefix() { diff --git a/src/applications/ponder/editor/PonderQuestionEditEngine.php b/src/applications/ponder/editor/PonderQuestionEditEngine.php index 640f657ad1..0365226666 100644 --- a/src/applications/ponder/editor/PonderQuestionEditEngine.php +++ b/src/applications/ponder/editor/PonderQuestionEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php b/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php index f9434c7f78..6a46f56b3e 100644 --- a/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php +++ b/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/ponder/phid/PonderAnswerPHIDType.php b/src/applications/ponder/phid/PonderAnswerPHIDType.php index be148af2f2..19ce39b59d 100644 --- a/src/applications/ponder/phid/PonderAnswerPHIDType.php +++ b/src/applications/ponder/phid/PonderAnswerPHIDType.php @@ -9,7 +9,7 @@ public function getTypeName() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } public function newObject() { diff --git a/src/applications/ponder/phid/PonderQuestionPHIDType.php b/src/applications/ponder/phid/PonderQuestionPHIDType.php index 9bdd80aae6..b135e9fc3c 100644 --- a/src/applications/ponder/phid/PonderQuestionPHIDType.php +++ b/src/applications/ponder/phid/PonderQuestionPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/ponder/query/PonderAnswerQuery.php b/src/applications/ponder/query/PonderAnswerQuery.php index f100f05ae3..9b362cf71e 100644 --- a/src/applications/ponder/query/PonderAnswerQuery.php +++ b/src/applications/ponder/query/PonderAnswerQuery.php @@ -78,7 +78,7 @@ protected function willFilterPage(array $answers) { } public function getQueryApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } } diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php index 323f34aac5..535e38ab51 100644 --- a/src/applications/ponder/query/PonderQuestionQuery.php +++ b/src/applications/ponder/query/PonderQuestionQuery.php @@ -144,7 +144,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } } diff --git a/src/applications/ponder/query/PonderQuestionSearchEngine.php b/src/applications/ponder/query/PonderQuestionSearchEngine.php index ecca1a91e5..ab08637124 100644 --- a/src/applications/ponder/query/PonderQuestionSearchEngine.php +++ b/src/applications/ponder/query/PonderQuestionSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } public function newQuery() { diff --git a/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php index e0becc3470..3ebae4c247 100644 --- a/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorProjectColumnTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index 9c6e5cf4cc..ab9fca1109 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -15,7 +15,7 @@ public function getIsMilestone() { } public function getEditorApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/project/editor/PhabricatorProjectTriggerEditor.php b/src/applications/project/editor/PhabricatorProjectTriggerEditor.php index 9014fd6f16..6ac1644fb4 100644 --- a/src/applications/project/editor/PhabricatorProjectTriggerEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTriggerEditor.php @@ -4,7 +4,7 @@ final class PhabricatorProjectTriggerEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php index 1c84932656..51cd840d3d 100644 --- a/src/applications/project/engine/PhabricatorProjectEditEngine.php +++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php @@ -47,7 +47,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function newEditableObject() { diff --git a/src/applications/project/herald/PhabricatorProjectHeraldAdapter.php b/src/applications/project/herald/PhabricatorProjectHeraldAdapter.php index 72f064a5c6..43c46a878c 100644 --- a/src/applications/project/herald/PhabricatorProjectHeraldAdapter.php +++ b/src/applications/project/herald/PhabricatorProjectHeraldAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php b/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php index c58bb44671..cfcf56f8c0 100644 --- a/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php +++ b/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php b/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php index 4a9cfbb8a6..e032f98588 100644 --- a/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php +++ b/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/project/phid/PhabricatorProjectTriggerPHIDType.php b/src/applications/project/phid/PhabricatorProjectTriggerPHIDType.php index 346b0e69fa..cdcd3c1146 100644 --- a/src/applications/project/phid/PhabricatorProjectTriggerPHIDType.php +++ b/src/applications/project/phid/PhabricatorProjectTriggerPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php index 2673902780..c4f9818a4a 100644 --- a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } } diff --git a/src/applications/project/query/PhabricatorProjectColumnQuery.php b/src/applications/project/query/PhabricatorProjectColumnQuery.php index 478d872cb4..6c1763623a 100644 --- a/src/applications/project/query/PhabricatorProjectColumnQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnQuery.php @@ -225,7 +225,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } } diff --git a/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php b/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php index 68fe1e7eb0..49702b11ea 100644 --- a/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index b02fa647a2..7b1ee9afec 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -705,7 +705,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index cb179c995f..936264a285 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function newQuery() { diff --git a/src/applications/project/query/PhabricatorProjectTriggerQuery.php b/src/applications/project/query/PhabricatorProjectTriggerQuery.php index 306fcb50fe..4f5f6d997c 100644 --- a/src/applications/project/query/PhabricatorProjectTriggerQuery.php +++ b/src/applications/project/query/PhabricatorProjectTriggerQuery.php @@ -121,7 +121,7 @@ protected function didFilterPage(array $triggers) { } public function getQueryApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/project/query/PhabricatorProjectTriggerSearchEngine.php b/src/applications/project/query/PhabricatorProjectTriggerSearchEngine.php index a178ed3e6c..1239dbeade 100644 --- a/src/applications/project/query/PhabricatorProjectTriggerSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectTriggerSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function newQuery() { diff --git a/src/applications/project/typeahead/PhabricatorProjectDatasource.php b/src/applications/project/typeahead/PhabricatorProjectDatasource.php index 86478a5e1e..acb6999018 100644 --- a/src/applications/project/typeahead/PhabricatorProjectDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function loadResults() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php index 070cb88485..1c7b1ff3fd 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalDatasource.php index b7003cb69e..9cd0fa8b06 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php index fe521fc6d8..4967e1c59d 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php index 026c7ed2ee..b3c7c9b68b 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php index bc19c12aa3..b0dca85a05 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php index 807986457a..9d1a91376a 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php b/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php index 524ced454d..308352b39c 100644 --- a/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php b/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php index d3b5e484e9..a3288b390f 100644 --- a/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php b/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php index 68de11e630..c688239fbb 100644 --- a/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function loadResults() { diff --git a/src/applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php b/src/applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php index c0e237f0c6..dfd10c051d 100644 --- a/src/applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index c23fc41624..19cd02507f 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -4,7 +4,7 @@ final class PhabricatorRepositoryEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php b/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php index 9d80d42bb7..8cadee7d52 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newEditableObject() { diff --git a/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php index df84f2dcfd..a803de5023 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php index 873cfbbae6..c3844c3ef2 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php index 45ed2819fb..3b0400436d 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php index 364ce62e5d..65741fded1 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryPushLogPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryPushLogPHIDType.php index 6af117db19..a5dc223ac8 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryPushLogPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryPushLogPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryRefCursorPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryRefCursorPHIDType.php index 30fbc4b6ae..aac22de8ca 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryRefCursorPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryRefCursorPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php index 6ca67257cf..dcc0e174e5 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php b/src/applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php index ade9560a93..86a3a6d0f9 100644 --- a/src/applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryURIPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryURIPHIDType.php index 658b942418..b330631c55 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryURIPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryURIPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php b/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php index 08f0ada159..976aef9ab4 100644 --- a/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php index 2b05b542d5..78fd4f729d 100644 --- a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php @@ -150,7 +150,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php index 8d4f14e0ce..b398012173 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php @@ -125,7 +125,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php index d1ce937b86..eec40da96d 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php @@ -112,7 +112,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php index 16897a1e4b..2f264510b2 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php @@ -184,7 +184,7 @@ private function shouldJoinPushEventTable() { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php b/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php index 271d8a9c14..642b22bc24 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 8dead39758..04a99bcf11 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -659,7 +659,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } private function getNormalizedURIs() { diff --git a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php index a6177c20b5..41cc4aff7f 100644 --- a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php @@ -143,7 +143,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php index 90a10e9872..33ae85481d 100644 --- a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php +++ b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php b/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php index cc568ef8e1..983187ffab 100644 --- a/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php @@ -105,7 +105,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryURIQuery.php b/src/applications/repository/query/PhabricatorRepositoryURIQuery.php index 5b75e1ef63..566aa0977f 100644 --- a/src/applications/repository/query/PhabricatorRepositoryURIQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryURIQuery.php @@ -91,7 +91,7 @@ protected function willFilterPage(array $uris) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php index b55f43255c..445be7777f 100644 --- a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php +++ b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php @@ -74,7 +74,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } protected function newEditableObject() { diff --git a/src/applications/search/editor/PhabricatorProfileMenuEditor.php b/src/applications/search/editor/PhabricatorProfileMenuEditor.php index 71f8c32e94..d385c8e90e 100644 --- a/src/applications/search/editor/PhabricatorProfileMenuEditor.php +++ b/src/applications/search/editor/PhabricatorProfileMenuEditor.php @@ -4,7 +4,7 @@ final class PhabricatorProfileMenuEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php b/src/applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php index 0fbdcaca48..53f248114e 100644 --- a/src/applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php +++ b/src/applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php b/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php index 862f694fc8..82778fa1e2 100644 --- a/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } } diff --git a/src/applications/search/query/PhabricatorNamedQueryQuery.php b/src/applications/search/query/PhabricatorNamedQueryQuery.php index 0ed92646e6..3d57005745 100644 --- a/src/applications/search/query/PhabricatorNamedQueryQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } } diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php index 9f26a81424..0d26dace7a 100644 --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php @@ -149,7 +149,7 @@ protected function willFilterPage(array $page) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/search/query/PhabricatorSavedQueryQuery.php b/src/applications/search/query/PhabricatorSavedQueryQuery.php index 765c751940..d8e939d424 100644 --- a/src/applications/search/query/PhabricatorSavedQueryQuery.php +++ b/src/applications/search/query/PhabricatorSavedQueryQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } } diff --git a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php index 3fcf0a8f7a..dca98e8411 100644 --- a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php +++ b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php @@ -10,7 +10,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/search/query/PhabricatorSearchDocumentQuery.php b/src/applications/search/query/PhabricatorSearchDocumentQuery.php index 4aed4722bd..7282b33efb 100644 --- a/src/applications/search/query/PhabricatorSearchDocumentQuery.php +++ b/src/applications/search/query/PhabricatorSearchDocumentQuery.php @@ -99,7 +99,7 @@ protected function willFilterPage(array $handles) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } protected function nextPage(array $page) { diff --git a/src/applications/search/typeahead/PhabricatorSearchDatasource.php b/src/applications/search/typeahead/PhabricatorSearchDatasource.php index 2d8182a2a2..c609d022bb 100644 --- a/src/applications/search/typeahead/PhabricatorSearchDatasource.php +++ b/src/applications/search/typeahead/PhabricatorSearchDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/search/typeahead/PhabricatorSearchDocumentTypeDatasource.php b/src/applications/search/typeahead/PhabricatorSearchDocumentTypeDatasource.php index c99cc4ede3..9b94d0b24d 100644 --- a/src/applications/search/typeahead/PhabricatorSearchDocumentTypeDatasource.php +++ b/src/applications/search/typeahead/PhabricatorSearchDocumentTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } public function loadResults() { diff --git a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php index b45de2dce1..a5ae9b3348 100644 --- a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php +++ b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php @@ -53,7 +53,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorSettingsApplication'; + return PhabricatorSettingsApplication::class; } protected function newEditableObject() { diff --git a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php index a927cc7475..d44766f136 100644 --- a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php +++ b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php @@ -4,7 +4,7 @@ final class PhabricatorUserPreferencesEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorSettingsApplication'; + return PhabricatorSettingsApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/settings/phid/PhabricatorUserPreferencesPHIDType.php b/src/applications/settings/phid/PhabricatorUserPreferencesPHIDType.php index 7022c56c33..ad24ae5af6 100644 --- a/src/applications/settings/phid/PhabricatorUserPreferencesPHIDType.php +++ b/src/applications/settings/phid/PhabricatorUserPreferencesPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorSettingsApplication'; + return PhabricatorSettingsApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/settings/query/PhabricatorUserPreferencesQuery.php b/src/applications/settings/query/PhabricatorUserPreferencesQuery.php index 1a6133724d..5fa9b89bdd 100644 --- a/src/applications/settings/query/PhabricatorUserPreferencesQuery.php +++ b/src/applications/settings/query/PhabricatorUserPreferencesQuery.php @@ -190,7 +190,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorSettingsApplication'; + return PhabricatorSettingsApplication::class; } } diff --git a/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php b/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php index cf088f37d4..14135bf88d 100644 --- a/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php +++ b/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php @@ -4,7 +4,7 @@ final class PhabricatorSlowvoteEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorSlowvoteApplication'; + return PhabricatorSlowvoteApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/slowvote/phid/PhabricatorSlowvotePollPHIDType.php b/src/applications/slowvote/phid/PhabricatorSlowvotePollPHIDType.php index 42ced85b22..f31ae71588 100644 --- a/src/applications/slowvote/phid/PhabricatorSlowvotePollPHIDType.php +++ b/src/applications/slowvote/phid/PhabricatorSlowvotePollPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorSlowvoteApplication'; + return PhabricatorSlowvoteApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php index d2ae9f1a35..bdc736022a 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php @@ -165,7 +165,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorSlowvoteApplication'; + return PhabricatorSlowvoteApplication::class; } } diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php b/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php index 8b93f75faf..557f6b815c 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorSlowvoteApplication'; + return PhabricatorSlowvoteApplication::class; } public function newQuery() { diff --git a/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php b/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php index e4dc9c5b69..abfafa491e 100644 --- a/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php +++ b/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php @@ -4,7 +4,7 @@ final class PhabricatorSpacesNamespaceEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return pht('PhabricatorSpacesApplication'); + return PhabricatorSpacesApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php b/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php index 1399e71c8e..a46040f98d 100644 --- a/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php +++ b/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorSpacesApplication'; + return PhabricatorSpacesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php index 388b6ab4d8..6703ed664e 100644 --- a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php +++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php @@ -33,7 +33,7 @@ public function withIsArchived($archived) { } public function getQueryApplicationClass() { - return 'PhabricatorSpacesApplication'; + return PhabricatorSpacesApplication::class; } public function newResultObject() { diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php index 3880143a24..835a5d596f 100644 --- a/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php +++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php @@ -4,7 +4,7 @@ final class PhabricatorSpacesNamespaceSearchEngine extends PhabricatorApplicationSearchEngine { public function getApplicationClassName() { - return 'PhabricatorSpacesApplication'; + return PhabricatorSpacesApplication::class; } public function getResultTypeDescription() { diff --git a/src/applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php b/src/applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php index 25951b53af..05008cd1fd 100644 --- a/src/applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php +++ b/src/applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorSpacesApplication'; + return PhabricatorSpacesApplication::class; } public function loadResults() { diff --git a/src/applications/tokens/phid/PhabricatorTokenTokenPHIDType.php b/src/applications/tokens/phid/PhabricatorTokenTokenPHIDType.php index 93ff9500ac..0ff41cb4e5 100644 --- a/src/applications/tokens/phid/PhabricatorTokenTokenPHIDType.php +++ b/src/applications/tokens/phid/PhabricatorTokenTokenPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorTokensApplication'; + return PhabricatorTokensApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php index 14e8016cf1..63c23e12fb 100644 --- a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php @@ -120,7 +120,7 @@ protected function willFilterPage(array $results) { } public function getQueryApplicationClass() { - return 'PhabricatorTokensApplication'; + return PhabricatorTokensApplication::class; } } diff --git a/src/applications/tokens/query/PhabricatorTokenQuery.php b/src/applications/tokens/query/PhabricatorTokenQuery.php index 07e15d4b03..f20bbebe1e 100644 --- a/src/applications/tokens/query/PhabricatorTokenQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenQuery.php @@ -71,7 +71,7 @@ private function getBuiltinTokens() { public function getQueryApplicationClass() { - return 'PhabricatorTokensApplication'; + return PhabricatorTokensApplication::class; } } diff --git a/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php b/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php index 2eb8158418..c1ba9f5998 100644 --- a/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php @@ -35,7 +35,7 @@ public function getTokenCounts() { } public function getQueryApplicationClass() { - return 'PhabricatorTokensApplication'; + return PhabricatorTokensApplication::class; } } diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php index 5a30fbfdde..b95c31cc56 100644 --- a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php @@ -41,7 +41,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function newEditableObject() { diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php index 34b7653001..5229ee6bec 100644 --- a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php @@ -4,7 +4,7 @@ final class PhabricatorEditEngineConfigurationEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php b/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php index e6184b1ae4..796f68e2cc 100644 --- a/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php +++ b/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php index b4ac0ddbfe..d10446e6eb 100644 --- a/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php +++ b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php index d2cbc3e697..d29fd10873 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php @@ -278,7 +278,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } } diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php index 694b4c48b6..c74ba38713 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php @@ -23,7 +23,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } public function newQuery() { diff --git a/src/applications/transactions/query/PhabricatorEditEngineQuery.php b/src/applications/transactions/query/PhabricatorEditEngineQuery.php index 400b62a487..48295ab31c 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineQuery.php +++ b/src/applications/transactions/query/PhabricatorEditEngineQuery.php @@ -43,7 +43,7 @@ protected function willFilterPage(array $engines) { } public function getQueryApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } } diff --git a/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php index 4ba1a0e879..822548f860 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php +++ b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } public function newQuery() { diff --git a/src/applications/transactions/typeahead/PhabricatorEditEngineDatasource.php b/src/applications/transactions/typeahead/PhabricatorEditEngineDatasource.php index 6520ca6732..74771ee834 100644 --- a/src/applications/transactions/typeahead/PhabricatorEditEngineDatasource.php +++ b/src/applications/transactions/typeahead/PhabricatorEditEngineDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function renderSpecialTokens(array $values) { diff --git a/src/applications/transactions/typeahead/PhabricatorTransactionsObjectTypeDatasource.php b/src/applications/transactions/typeahead/PhabricatorTransactionsObjectTypeDatasource.php index e7292fc522..d515b8303a 100644 --- a/src/applications/transactions/typeahead/PhabricatorTransactionsObjectTypeDatasource.php +++ b/src/applications/transactions/typeahead/PhabricatorTransactionsObjectTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function renderSpecialTokens(array $values) { diff --git a/src/applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php b/src/applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php index f16a64bb9b..ae5c87b45c 100644 --- a/src/applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php +++ b/src/applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php b/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php index 2001fec0c7..2160507a53 100644 --- a/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php +++ b/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php @@ -41,7 +41,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorXHProfApplication'; + return PhabricatorXHProfApplication::class; } } diff --git a/src/applications/xhprof/query/PhabricatorXHProfSampleSearchEngine.php b/src/applications/xhprof/query/PhabricatorXHProfSampleSearchEngine.php index 171f28f027..f0a5273f3e 100644 --- a/src/applications/xhprof/query/PhabricatorXHProfSampleSearchEngine.php +++ b/src/applications/xhprof/query/PhabricatorXHProfSampleSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorXHProfApplication'; + return PhabricatorXHProfApplication::class; } public function newQuery() { diff --git a/src/infrastructure/daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php b/src/infrastructure/daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php index e94ca6dc49..bd4a271fc7 100644 --- a/src/infrastructure/daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php +++ b/src/infrastructure/daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php @@ -4,7 +4,7 @@ final class PhabricatorWorkerBulkJobEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/infrastructure/daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php b/src/infrastructure/daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php index b94f7eab27..d3d672897a 100644 --- a/src/infrastructure/daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php +++ b/src/infrastructure/daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } protected function buildQueryForObjects( diff --git a/src/infrastructure/daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php b/src/infrastructure/daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php index b1dadb568e..c301e3d2be 100644 --- a/src/infrastructure/daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php +++ b/src/infrastructure/daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } protected function buildQueryForObjects( diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php index b359157e06..bdedc3584c 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php @@ -96,7 +96,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php index 3b9d6c9d48..cda4176f29 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } public function newQuery() { From bb23e86dafc6b818b9a3f649507f8857f8e30113 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Fri, 12 Jan 2024 12:03:14 +0100 Subject: [PATCH 384/425] Fix PHP 8.1 "json_decode(null)" exception editing a form when custom field of type Users exists Summary: When `$value` is `null`, do not pass `$value` down the stack in `buildControl()` to ultimately end up with `json_decode` complaining. Instead, just skip this call. ``` EXCEPTION: (RuntimeException) json_decode(): Passing null to parameter #1 ($json) of type string is deprecated at [/src/error/PhutilErrorHandler.php:261] arcanist(head=customOAuthUrlencodeNull, ref.master=788098096e11, ref.customOAuthUrlencodeNull=4f0f2043b7e9), phorge(head=customFieldDate, ref.master=bcfcd9acfc12, ref.customFieldDate=ae8cbe84252d) #0 <#2> PhutilErrorHandler::handleError(integer, string, string, integer) called at [/src/error/PhutilErrorHandler.php:261] #1 <#2> json_decode(NULL, boolean) called at [/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php:44] #2 <#2> PhabricatorStandardCustomFieldPHIDs::setValueFromStorage(NULL) called at [/src/infrastructure/customfield/field/PhabricatorCustomField.php:895] #3 <#2> PhabricatorCustomField::setValueFromApplicationTransactions(NULL) called at [/src/infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php:70] #4 <#2> PhabricatorCustomFieldEditField::buildControl() called at [/src/applications/transactions/editfield/PhabricatorEditField.php:385] ``` Closes T15602 Test Plan: After applying these three changes and creating a custom field with `"type": "users"` under `/config/edit/maniphest.custom-field-definitions/`, the website `/transactions/editengine/maniphest.task/view/5/` renders correctly in the browser, showing "This is a preview of the current form configuration." Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15602 Differential Revision: https://we.phorge.it/D25390 --- .../standard/PhabricatorStandardCustomFieldPHIDs.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php index 48d5ea725a..e34c3913f0 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php @@ -39,14 +39,9 @@ public function setValueFromStorage($value) { // should hold until this can get cleaned up more thoroughly. // TODO: Clean this up. - $result = array(); - if (!is_array($value) && phutil_nonempty_string($value)) { + if (is_string($value) && phutil_nonempty_string($value)) { $value = json_decode($value, true); - if (is_array($value)) { - $result = array_values($value); - } } - $this->setFieldValue($value); return $this; From 9c9fbc3d04843ab6efb0093053c9d0ca6d9d131e Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Mon, 5 Feb 2024 20:07:41 +0200 Subject: [PATCH 385/425] unbreak celerity/map Test Plan: phorge will load and serve pages Reviewers: O1 Blessed Committers, aklapper Reviewed By: O1 Blessed Committers, aklapper Subscribers: aklapper, philj0st, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25532 --- resources/celerity/map.php | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index db4047fc00..0a15330eec 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,11 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', -<<<<<<< HEAD - 'core.pkg.css' => 'c753df75', -======= - 'core.pkg.css' => '77d64402', ->>>>>>> 67b4f957d1 (Revert "Added cross-platform default fonts") + 'core.pkg.css' => '7f47b2ac', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '6d3700f0', @@ -112,13 +108,8 @@ 'rsrc/css/application/slowvote/slowvote.css' => '1694baed', 'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd', 'rsrc/css/application/uiexample/example.css' => 'b4795059', -<<<<<<< HEAD - 'rsrc/css/core/core.css' => 'b3a5928e', - 'rsrc/css/core/remarkup.css' => 'd91c2ee8', -======= 'rsrc/css/core/core.css' => '531ad849', - 'rsrc/css/core/remarkup.css' => 'b251d2e7', ->>>>>>> 67b4f957d1 (Revert "Added cross-platform default fonts") + 'rsrc/css/core/remarkup.css' => 'd91c2ee8', 'rsrc/css/core/syntax.css' => '548567f6', 'rsrc/css/core/z-index.css' => 'ac3bfcd4', 'rsrc/css/diviner/diviner-shared.css' => '4bd263b0', From e3714c86a7d5b9b7f1d413b6c4aba20f3ef8edb5 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 5 Feb 2024 22:41:13 +0100 Subject: [PATCH 386/425] Validate PhabricatorSearchTextField value not to be an array Summary: PhabricatorSearchTextField.php's parent class PhabricatorSearchField.php defines an (empty) `protected function validateControlValue($value)`. Override this function in PhabricatorSearchTextField.php by disallowing arrays (as the only data type which cannot be casted into a text value). Closes T15714 Test Plan: Apply D25518; then manually edit the code in `ManiphestTaskSearchEngine.php` by defining an array and passing that array to the Page Size text field via `id(new PhabricatorSearchTextField())->setDefaultValue($empty_array)`, then go to `/maniphest/query/advanced/` and see the error message. Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: 20after4, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15714 Differential Revision: https://we.phorge.it/D25519 --- .../search/field/PhabricatorSearchTextField.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/applications/search/field/PhabricatorSearchTextField.php b/src/applications/search/field/PhabricatorSearchTextField.php index 915f22a6e9..21d256e882 100644 --- a/src/applications/search/field/PhabricatorSearchTextField.php +++ b/src/applications/search/field/PhabricatorSearchTextField.php @@ -11,6 +11,15 @@ protected function getValueFromRequest(AphrontRequest $request, $key) { return $request->getStr($key); } + protected function validateControlValue($value) { + if (!is_array($value)) { + return; + } + $this->addError( + pht('Invalid'), + pht('Text value for "%s" can not be parsed.', $this->getLabel())); + } + protected function newControl() { return new AphrontFormTextControl(); } From 01b5bf9ea10218e5c0b9805eb8c2a070df2c8766 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 5 Feb 2024 22:37:34 +0100 Subject: [PATCH 387/425] Deprecate Fund and Phortune prototype applications Summary: See T15726 Test Plan: Go to `/applications/` and see that Fund and Phortune say "Deprecated". Reviewers: O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25533 --- .../fund/application/PhabricatorFundApplication.php | 4 ++++ .../phortune/application/PhabricatorPhortuneApplication.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/applications/fund/application/PhabricatorFundApplication.php b/src/applications/fund/application/PhabricatorFundApplication.php index 5e2d33c1a4..8fe832473e 100644 --- a/src/applications/fund/application/PhabricatorFundApplication.php +++ b/src/applications/fund/application/PhabricatorFundApplication.php @@ -30,6 +30,10 @@ public function isPrototype() { return true; } + public function isDeprecated() { + return true; + } + public function getRemarkupRules() { return array( new FundInitiativeRemarkupRule(), diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php index 25680ba6c5..0311c94a00 100644 --- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php +++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php @@ -30,6 +30,10 @@ public function isPrototype() { return true; } + public function isDeprecated() { + return true; + } + public function getRoutes() { return array( '/phortune/' => array( From f81e821abf2b275d1778d02a018a4d0af7208be8 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 9 Feb 2024 11:18:23 +0200 Subject: [PATCH 388/425] Mark manually created Diffs as new objects to fix empty Feed transaction entries Summary: When creating a Differential diff manually via `/differential/diff/create/` in the web browser instead of using Arcanist, `setIsCreateTransaction(true)` for the transaction type `DifferentialDiffTransaction::TYPE_DIFF_CREATE` to avoid an empty `Transaction` field in the Feed and avoid `strncmp()` complaining about a null value being passed. For gory details, see the comments in T15659. ``` ERROR 8192: strncmp(): Passing null to parameter #1 ($string1) of type string is deprecated at [/var/www/html/phorge/phorge/src/applications/policy/query/PhabricatorPolicyQuery.php:314] ``` Closes T15659 Test Plan: 1. Create a valid diff via `/differential/diff/create/` 2. Go to `/feed/transactions/query/advanced/` and set `Object Types` to `Differential Diff` and click `Search` 3. See two entries in the `Transaction` column for the just created diff: One says "created this diff", the other one says "created this object with visibility" and is not an empty line anymore which triggered an exception. Reviewers: O1 Blessed Committers, aklapper Reviewed By: O1 Blessed Committers, aklapper Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15659 Differential Revision: https://we.phorge.it/D25517 --- .../conduit/DifferentialCreateRawDiffConduitAPIMethod.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php index ae55135e92..cb3eaf537d 100644 --- a/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php @@ -71,12 +71,14 @@ protected function execute(ConduitAPIRequest $request) { $xactions = array( id(new DifferentialDiffTransaction()) ->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE) + ->setIsCreateTransaction(true) ->setNewValue($diff_data_dict), ); if ($request->getValue('viewPolicy')) { $xactions[] = id(new DifferentialDiffTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) + ->setIsCreateTransaction(true) ->setNewValue($request->getValue('viewPolicy')); } From a7fb04f96c92d4ff57ac8c01213e6dd73a03f5d6 Mon Sep 17 00:00:00 2001 From: Jean Guyomarc'h Date: Mon, 12 Feb 2024 11:04:29 +0100 Subject: [PATCH 389/425] fix PHP 8 "strlen(null)" when moving paths with missing options Summary: Running ./bin/repository move-paths without specifying --from nor --to leads to the PHP 8 error about strlen() not accepting null parameters. Test Plan: Running ./bin/repository move-paths without --from/--to (shows a proper error) Reviewers: O1 Blessed Committers, speck, valerio.bozzolan Reviewed By: O1 Blessed Committers, speck, valerio.bozzolan Subscribers: aklapper, speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25484 --- .../PhabricatorRepositoryManagementMovePathsWorkflow.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php index ced8663b3c..2c684dcb1f 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php @@ -38,14 +38,14 @@ public function execute(PhutilArgumentParser $args) { } $from = $args->getArg('from'); - if (!strlen($from)) { + if (!phutil_nonempty_string($from)) { throw new Exception( pht( 'You must specify a path prefix to move from with --from.')); } $to = $args->getArg('to'); - if (!strlen($to)) { + if (!phutil_nonempty_string($to)) { throw new Exception( pht( 'You must specify a path prefix to move to with --to.')); From b445e1d80df90f7fb1a2955fde818e72359970b7 Mon Sep 17 00:00:00 2001 From: Mark Jervelund Date: Mon, 12 Feb 2024 11:51:24 +0100 Subject: [PATCH 390/425] Fix config transaction rendering error on auth page by allowing PhutilSafeHTML Summary: Closes T15706 Test Plan: Checked that the page worked as expected with changes implemented Reviewers: O1 Blessed Committers, speck, valerio.bozzolan Reviewed By: O1 Blessed Committers, speck, valerio.bozzolan Subscribers: sirocyl, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15706 Differential Revision: https://we.phorge.it/D25512 --- .../auth/storage/PhabricatorAuthProviderConfigTransaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php b/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php index d7f974cb15..92bb4883bc 100644 --- a/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php +++ b/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php @@ -150,7 +150,7 @@ public function getTitle() { $provider = $this->getProvider(); if ($provider) { $title = $provider->renderConfigPropertyTransactionTitle($this); - if (phutil_nonempty_string($title)) { + if (phutil_nonempty_stringlike($title)) { return $title; } } From 821708414eecc90824810f7805cf236da3124498 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 4 Jan 2024 08:33:37 +0100 Subject: [PATCH 391/425] Allow to uninstall (hide) Audit application Summary: While many Phorge applications can be uninstalled (such as Differential for pre-merge commit review), this has not been the case for Audit (post-merge commit review) since rP11861265fe94fa97e4d0563c5bdb7b8cac27282d. In installations which do not use Audit in their workflows, exposing Audit related UI elements creates additional clutter and complexity. Fixes T15129 Test Plan: * Go to `/applications/view/PhabricatorAuditApplication/` and click "Uninstall" * View an individual merged commit in Diffusion, click "Edit Commit" in the sidebar to go to `/diffusion/commit/edit/1/`, see that "Auditors" datasource is not displayed * View an individual merged commit in Diffusion, click the "Add Action..." dropdown, see that the "Audit Action" section and its entries "Accept Commit" and "Raise Concern" are not displayed * View an individual merged commit in Diffusion, click the "Add Action..." dropdown, see that the "Change Auditors" entry is not displayed * Go to `/conduit/` and see that the API method "audit.query" and the entire section "audit" is not displayed * Go to `/diffusion/commit/` and see no "Active Audits" and "Audited" searches in the left side bar * Go to `/diffusion/commit/query/all/` and see that no "Auditors" entry is shown for each commit * Go to `/diffusion/commit/query/all/` and see that no audit related entry is shown in the grey column on the right (if Harbormaster is not installed, there will be no grey column at all) * Go to `/herald/edit/?content_type=commit&rule_type=global` and see that no Condition entry "Auditors" and no Condition entry "Affected packages that need audit" is displayed * Go to `/herald/edit/?content_type=commit&rule_type=personal` and see that no "Add me as an auditor" Herald action and no "Added Auditors" Herald condition is displayed Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15129 Differential Revision: https://we.phorge.it/D25503 --- .../PhabricatorAuditApplication.php | 4 +-- .../audit/conduit/AuditConduitAPIMethod.php | 2 +- .../query/PhabricatorCommitSearchEngine.php | 15 ++++++++-- .../editor/DiffusionCommitEditEngine.php | 29 ++++++++++--------- ...ffusionAuditorsAddAuditorsHeraldAction.php | 7 ++++- .../DiffusionAuditorsAddSelfHeraldAction.php | 7 ++++- .../DiffusionCommitAuditorsHeraldField.php | 9 ++++++ ...DiffusionCommitPackageAuditHeraldField.php | 10 +++++++ .../view/DiffusionCommitGraphView.php | 5 +++- .../DiffusionCommitActionTransaction.php | 3 ++ .../PhabricatorApplicationSearchEngine.php | 22 +++++++------- 11 files changed, 80 insertions(+), 33 deletions(-) diff --git a/src/applications/audit/application/PhabricatorAuditApplication.php b/src/applications/audit/application/PhabricatorAuditApplication.php index 7d8ad94aa7..ee5cbe7781 100644 --- a/src/applications/audit/application/PhabricatorAuditApplication.php +++ b/src/applications/audit/application/PhabricatorAuditApplication.php @@ -19,9 +19,7 @@ public function getShortDescription() { } public function canUninstall() { - // Audit was once a separate application, but has largely merged with - // Diffusion. - return false; + return true; } public function isPinnedByDefault(PhabricatorUser $viewer) { diff --git a/src/applications/audit/conduit/AuditConduitAPIMethod.php b/src/applications/audit/conduit/AuditConduitAPIMethod.php index a5a7957b1f..4dc9eb103e 100644 --- a/src/applications/audit/conduit/AuditConduitAPIMethod.php +++ b/src/applications/audit/conduit/AuditConduitAPIMethod.php @@ -4,7 +4,7 @@ abstract class AuditConduitAPIMethod extends ConduitAPIMethod { final public function getApplication() { return PhabricatorApplication::getByClass( - 'PhabricatorDiffusionApplication'); + 'PhabricatorAuditApplication'); } } diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index a6a2ee071e..dfc038c197 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -70,6 +70,7 @@ protected function buildQueryFromParameters(array $map) { } protected function buildCustomSearchFields() { + $show_audit_fields = (id(new PhabricatorAuditApplication())->isInstalled()); $show_packages = PhabricatorApplication::isClassInstalled( 'PhabricatorPackagesApplication'); return array( @@ -95,6 +96,7 @@ protected function buildCustomSearchFields() { ->setConduitKey('auditors') ->setAliases(array('auditor', 'auditors', 'auditorPHID')) ->setDatasource(new DiffusionAuditorFunctionDatasource()) + ->setIsHidden(!$show_audit_fields) ->setDescription( pht( 'Find commits where given users, projects, or packages are '. @@ -106,6 +108,7 @@ protected function buildCustomSearchFields() { ->setOptions(DiffusionCommitAuditStatus::newOptions()) ->setDeprecatedOptions( DiffusionCommitAuditStatus::newDeprecatedOptions()) + ->setIsHidden(!$show_audit_fields) ->setDescription(pht('Find commits with given audit statuses.')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Repositories')) @@ -172,9 +175,13 @@ protected function getBuiltinQueryNames() { $names = array(); if ($this->requireViewer()->isLoggedIn()) { - $names['active'] = pht('Active Audits'); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $names['active'] = pht('Active Audits'); + } $names['authored'] = pht('Authored'); - $names['audited'] = pht('Audited'); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $names['audited'] = pht('Audited'); + } } $names['all'] = pht('All Commits'); @@ -224,9 +231,11 @@ protected function renderResultList( $bucket = $this->getResultBucket($query); + // hide "Auditors" on /diffusion/commit/query/all/ if Audit not installed + $show_auditors = id(new PhabricatorAuditApplication())->isInstalled(); $template = id(new DiffusionCommitGraphView()) ->setViewer($viewer) - ->setShowAuditors(true); + ->setShowAuditors($show_auditors); $views = array(); if ($bucket) { diff --git a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php index 02213cceee..c807d4e8c4 100644 --- a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php @@ -100,19 +100,22 @@ protected function buildCustomEditFields($object) { $data = $object->getCommitData(); $fields = array(); - - $fields[] = id(new PhabricatorDatasourceEditField()) - ->setKey('auditors') - ->setLabel(pht('Auditors')) - ->setDatasource(new DiffusionAuditorDatasource()) - ->setUseEdgeTransactions(true) - ->setTransactionType( - DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE) - ->setCommentActionLabel(pht('Change Auditors')) - ->setDescription(pht('Auditors for this commit.')) - ->setConduitDescription(pht('Change the auditors for this commit.')) - ->setConduitTypeDescription(pht('New auditors.')) - ->setValue($object->getAuditorPHIDsForEdit()); + // remove "Change Auditors" from "Add Action" dropdown etc + // if Audit is not installed + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $fields[] = id(new PhabricatorDatasourceEditField()) + ->setKey('auditors') + ->setLabel(pht('Auditors')) + ->setDatasource(new DiffusionAuditorDatasource()) + ->setUseEdgeTransactions(true) + ->setTransactionType( + DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE) + ->setCommentActionLabel(pht('Change Auditors')) + ->setDescription(pht('Auditors for this commit.')) + ->setConduitDescription(pht('Change the auditors for this commit.')) + ->setConduitTypeDescription(pht('New auditors.')) + ->setValue($object->getAuditorPHIDsForEdit()); + } $actions = DiffusionCommitActionTransaction::loadAllActions(); $actions = msortv($actions, 'getCommitActionOrderVector'); diff --git a/src/applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php b/src/applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php index f895847d4a..f3ae92248c 100644 --- a/src/applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php +++ b/src/applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php @@ -9,8 +9,13 @@ public function getHeraldActionName() { return pht('Add auditors'); } + // hide "Add auditors" Herald action if Audit not installed public function supportsRuleType($rule_type) { - return ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + return ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); + } else { + return false; + } } public function applyEffect($object, HeraldEffect $effect) { diff --git a/src/applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php b/src/applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php index d27876d40e..b1ab98db77 100644 --- a/src/applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php +++ b/src/applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php @@ -9,8 +9,13 @@ public function getHeraldActionName() { return pht('Add me as an auditor'); } + // hide "Add me as an auditor" Herald action if Audit not installed public function supportsRuleType($rule_type) { - return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); + } else { + return false; + } } public function applyEffect($object, HeraldEffect $effect) { diff --git a/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php b/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php index 5f0da133f8..7afd0f6705 100644 --- a/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php +++ b/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php @@ -5,6 +5,15 @@ final class DiffusionCommitAuditorsHeraldField const FIELDCONST = 'diffusion.commit.auditors'; + // hide "Auditors" Herald condition if Audit not installed + public function supportsObject($object) { + if (id(new PhabricatorAuditApplication())->isInstalled()) { + return ($object instanceof PhabricatorRepositoryCommit); + } else { + return false; + } + } + public function getHeraldFieldName() { return pht('Auditors'); } diff --git a/src/applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php b/src/applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php index 587c0b58d3..9fdd785fcb 100644 --- a/src/applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php +++ b/src/applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php @@ -5,6 +5,16 @@ final class DiffusionCommitPackageAuditHeraldField const FIELDCONST = 'diffusion.commit.package.audit'; + // hide "Affected packages that need audit" Herald condition + // if Audit not installed + public function supportsObject($object) { + if (id(new PhabricatorAuditApplication())->isInstalled()) { + return ($object instanceof PhabricatorRepositoryCommit); + } else { + return false; + } + } + public function getHeraldFieldName() { return pht('Affected packages that need audit'); } diff --git a/src/applications/diffusion/view/DiffusionCommitGraphView.php b/src/applications/diffusion/view/DiffusionCommitGraphView.php index 30c252712c..7e70a4e655 100644 --- a/src/applications/diffusion/view/DiffusionCommitGraphView.php +++ b/src/applications/diffusion/view/DiffusionCommitGraphView.php @@ -169,7 +169,10 @@ private function newObjectItemViews() { $this->addBuildAction($item_view, $hash); } - $this->addAuditAction($item_view, $hash); + // hide Audit entry on /diffusion/commit/query/all if Audit not installed + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $this->addAuditAction($item_view, $hash); + } if ($show_auditors) { $auditor_list = $item_view->newMapView(); diff --git a/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php index 1d351ffa5d..d381602766 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php @@ -8,6 +8,9 @@ final public function getCommitActionKey() { } public function isActionAvailable($object, PhabricatorUser $viewer) { + if (!id(new PhabricatorAuditApplication())->isInstalled()) { + return false; + } try { $this->validateAction($object, $viewer); return true; diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 076a9a1e52..cad397b0ac 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -275,16 +275,18 @@ protected function buildSearchFields() { ->setOptions($orders); } - $buckets = $this->newResultBuckets(); - if ($query && $buckets) { - $bucket_options = array( - self::BUCKET_NONE => pht('No Bucketing'), - ) + mpull($buckets, 'getResultBucketName'); - - $fields[] = id(new PhabricatorSearchSelectField()) - ->setLabel(pht('Bucket')) - ->setKey('bucket') - ->setOptions($bucket_options); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $buckets = $this->newResultBuckets(); + if ($query && $buckets) { + $bucket_options = array( + self::BUCKET_NONE => pht('No Bucketing'), + ) + mpull($buckets, 'getResultBucketName'); + + $fields[] = id(new PhabricatorSearchSelectField()) + ->setLabel(pht('Bucket')) + ->setKey('bucket') + ->setOptions($bucket_options); + } } $field_map = array(); From 052b5f41c70516a76d0c1e031a5f18eb7293ffe2 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 22 Jan 2024 19:39:37 +0100 Subject: [PATCH 392/425] Fix PHP 8.1 "strlen(null)" exception rendering dashboard panel with latest tasks when custom int field configured Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` ERROR 8192: strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/var/www/html/phorge/phorge/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php:56] ``` Closes T15685 Test Plan: After configuring a custom `int` field and a dashboard panel to query and listed the latest created tasks, access the panel and check the PHP error log. Reviewers: O1 Blessed Committers, speck, valerio.bozzolan Reviewed By: O1 Blessed Committers, speck, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15685 Differential Revision: https://we.phorge.it/D25489 --- .../standard/PhabricatorStandardCustomFieldInt.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php index 0ea7233768..3ee42a531c 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php @@ -11,7 +11,7 @@ public function buildFieldIndexes() { $indexes = array(); $value = $this->getFieldValue(); - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $indexes[] = $this->newNumericIndex((int)$value); } @@ -53,7 +53,7 @@ public function applyApplicationSearchConstraintToQuery( PhabricatorCursorPagedPolicyAwareQuery $query, $value) { - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $query->withApplicationSearchContainsConstraint( $this->newNumericIndex(null), $value); @@ -84,7 +84,7 @@ public function validateApplicationTransactions( foreach ($xactions as $xaction) { $value = $xaction->getNewValue(); - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { if (!preg_match('/^-?\d+/', $value)) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, @@ -104,9 +104,9 @@ public function getApplicationTransactionHasEffect( $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); - if (!strlen($old) && strlen($new)) { + if (!phutil_nonempty_scalar($old) && phutil_nonempty_scalar($new)) { return true; - } else if (strlen($old) && !strlen($new)) { + } else if (phutil_nonempty_scalar($old) && !phutil_nonempty_scalar($new)) { return true; } else { return ((int)$old !== (int)$new); From e2bec4c1f58b6fc26dafd348a01f6ee02a1596ec Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 24 Jan 2024 11:14:28 +0100 Subject: [PATCH 393/425] Improve messages when no image formats are supported (due to GD not installed) Summary: When GD is not installed, trying to set a custom image for a project/blog/repository/user/etc displays unhelpful error messages (`This server only supports these image formats: .` and `Supported formats: `) due to the array of supported image formats being empty. Display clearer messages instead. Closes T15720 Test Plan: Do not have php-gd installed, go to `/project/manage/1/`, take a look at the string below the "Upload Picture" button, select {nav icon=picture, name=Edit Picture} in the sidebar, select `Custom: Choose Icon and Color...`, `Choose Background Color` and `Choose Icon`, then click the `Save Image` button. Also try to upload a custom image and look at the error message. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15720 Differential Revision: https://we.phorge.it/D25525 --- .../controller/ConpherenceRoomPictureController.php | 13 ++++++++----- .../DiffusionRepositoryProfilePictureController.php | 13 ++++++++----- .../PhabricatorPeopleProfilePictureController.php | 13 ++++++++----- .../blog/PhameBlogHeaderPictureController.php | 13 ++++++++----- .../blog/PhameBlogProfilePictureController.php | 13 ++++++++----- .../post/PhamePostHeaderPictureController.php | 13 ++++++++----- .../merchant/PhortuneMerchantPictureController.php | 13 ++++++++----- .../PhabricatorProjectEditPictureController.php | 13 ++++++++----- 8 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/applications/conpherence/controller/ConpherenceRoomPictureController.php b/src/applications/conpherence/controller/ConpherenceRoomPictureController.php index fbf3def9cd..5be8331610 100644 --- a/src/applications/conpherence/controller/ConpherenceRoomPictureController.php +++ b/src/applications/conpherence/controller/ConpherenceRoomPictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { $monogram = $conpherence->getMonogram(); $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -56,9 +62,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -195,8 +199,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton('/'.$monogram) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php b/src/applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php index cd4e3148c7..59a49def6f 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { } $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); $done_uri = $repository->getURI(); @@ -57,9 +63,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -213,8 +217,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($done_uri) diff --git a/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php index d50eccff13..72f90f024b 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php @@ -27,6 +27,12 @@ public function handleRequest(AphrontRequest $request) { $done_uri = '/p/'.$name.'/'; $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -59,9 +65,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -250,8 +254,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($done_uri) diff --git a/src/applications/phame/controller/blog/PhameBlogHeaderPictureController.php b/src/applications/phame/controller/blog/PhameBlogHeaderPictureController.php index 0106b9571d..4bec8efecc 100644 --- a/src/applications/phame/controller/blog/PhameBlogHeaderPictureController.php +++ b/src/applications/phame/controller/blog/PhameBlogHeaderPictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { $blog_uri = '/phame/blog/manage/'.$id; $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); $delete_header = ($request->getInt('delete') == 1); @@ -45,9 +51,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$delete_header) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } } @@ -86,8 +90,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('header') ->setLabel(pht('Upload Header')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormCheckboxControl()) ->setName('delete') diff --git a/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php b/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php index 4e69034253..91daa784cd 100644 --- a/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php +++ b/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { $blog_uri = '/phame/blog/manage/'.$id; $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -56,9 +62,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -196,8 +200,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($blog_uri) diff --git a/src/applications/phame/controller/post/PhamePostHeaderPictureController.php b/src/applications/phame/controller/post/PhamePostHeaderPictureController.php index 60075cc077..db8f662c39 100644 --- a/src/applications/phame/controller/post/PhamePostHeaderPictureController.php +++ b/src/applications/phame/controller/post/PhamePostHeaderPictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { $post_uri = '/phame/post/view/'.$id; $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); $delete_header = ($request->getInt('delete') == 1); @@ -45,9 +51,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$delete_header) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } } @@ -86,8 +90,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('header') ->setLabel(pht('Upload Header')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormCheckboxControl()) ->setName('delete') diff --git a/src/applications/phortune/controller/merchant/PhortuneMerchantPictureController.php b/src/applications/phortune/controller/merchant/PhortuneMerchantPictureController.php index 96f4f8e367..a3b9ada7af 100644 --- a/src/applications/phortune/controller/merchant/PhortuneMerchantPictureController.php +++ b/src/applications/phortune/controller/merchant/PhortuneMerchantPictureController.php @@ -14,6 +14,12 @@ protected function handleMerchantRequest(AphrontRequest $request) { $uri = $merchant->getDetailsURI(); $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -46,9 +52,7 @@ protected function handleMerchantRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -185,8 +189,7 @@ protected function handleMerchantRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Logo')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($uri) diff --git a/src/applications/project/controller/PhabricatorProjectEditPictureController.php b/src/applications/project/controller/PhabricatorProjectEditPictureController.php index d141dc07e2..b8be55899b 100644 --- a/src/applications/project/controller/PhabricatorProjectEditPictureController.php +++ b/src/applications/project/controller/PhabricatorProjectEditPictureController.php @@ -26,6 +26,12 @@ public function handleRequest(AphrontRequest $request) { $manage_uri = $this->getApplicationURI('manage/'.$project->getID().'/'); $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -58,9 +64,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -257,8 +261,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($manage_uri) From 89a5d3132c94dec8de47f5052fd0441e44365751 Mon Sep 17 00:00:00 2001 From: Benjamin Kausch Date: Thu, 15 Feb 2024 19:30:57 +0100 Subject: [PATCH 394/425] Implements copy button in clone repo modal Summary: This diff adds a copy button to every repo uri in the clone repo modal. I have made the button to select the text to a merely structural span before the input - it just shows the type of the repository uri. When you click inside the input, the entire uri will be selected. Also I have uncluttered the HTML structure. A table is not needed here, nothing a flex block can't handle. | Before | After | |-----------|-----------| | {F1360344} | {F1368592} | While at it, I have extended the used javascript copy behavior. First of all: `document.execCommand('copy')` [[ https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand | could stop working every moment in every browser ]]. The [[ https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard | new clipboard API ]] is the way to go, so I have implemented it as the preferred method. The old method is kept as a fallback. And I have added a very nice feature: If defined, the behavior will now issue success or error notifications. See the changed UIExamples for that. To support the shrinking of JS code with async functions I have patched the JsShrink source. Test Plan: Go to a repository, hit the clone button and use the new copy button. You will see a shiny notification as a reward. Reviewers: O1 Blessed Committers, avivey, valerio.bozzolan Reviewed By: O1 Blessed Committers, avivey, valerio.bozzolan Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25536 --- externals/JsShrink/jsShrink.php | 2 +- resources/celerity/map.php | 35 +++++----- .../diffusion/view/DiffusionCloneURIView.php | 46 ++++++++---- .../uiexample/examples/PHUIButtonExample.php | 2 + .../application/diffusion/diffusion-icons.css | 33 +++++---- webroot/rsrc/js/core/behavior-copy.js | 70 +++++++++++++++---- .../rsrc/js/core/behavior-select-content.js | 8 +++ 7 files changed, 137 insertions(+), 59 deletions(-) diff --git a/externals/JsShrink/jsShrink.php b/externals/JsShrink/jsShrink.php index 239e6d7978..34a5a5b8e8 100644 --- a/externals/JsShrink/jsShrink.php +++ b/externals/JsShrink/jsShrink.php @@ -35,7 +35,7 @@ function jsShrinkCallback($match) { list(, $context, $regexp, $result, $word, $operator) = $match; if ($word != '') { $result = ($last == 'word' ? "\n" : ($last == 'return' ? " " : "")) . $result; - $last = ($word == 'return' || $word == 'throw' || $word == 'break' ? 'return' : 'word'); + $last = ($word == 'return' || $word == 'throw' || $word == 'break' || $word == 'async' ? 'return' : 'word'); } elseif ($operator) { $result = ($last == $operator[0] ? "\n" : "") . $result; $last = $operator[0]; diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 0a15330eec..a9919fc998 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -14,7 +14,7 @@ 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '6d3700f0', 'differential.pkg.js' => '46fcb3af', - 'diffusion.pkg.css' => '42c75c37', + 'diffusion.pkg.css' => '354279ea', 'diffusion.pkg.js' => '78c9885d', 'maniphest.pkg.css' => '35995d6d', 'maniphest.pkg.js' => 'c9308721', @@ -69,7 +69,7 @@ 'rsrc/css/application/differential/revision-history.css' => '237a2979', 'rsrc/css/application/differential/revision-list.css' => '93d2df7d', 'rsrc/css/application/differential/table-of-contents.css' => 'bba788b9', - 'rsrc/css/application/diffusion/diffusion-icons.css' => '23b31a1b', + 'rsrc/css/application/diffusion/diffusion-icons.css' => 'e812add2', 'rsrc/css/application/diffusion/diffusion-readme.css' => 'b68a76e4', 'rsrc/css/application/diffusion/diffusion-repository.css' => 'b89e8c6c', 'rsrc/css/application/diffusion/diffusion.css' => 'e46232d6', @@ -470,7 +470,7 @@ 'rsrc/js/core/behavior-badge-view.js' => '92cdd7b6', 'rsrc/js/core/behavior-bulk-editor.js' => 'aa6d2308', 'rsrc/js/core/behavior-choose-control.js' => '04f8a1e3', - 'rsrc/js/core/behavior-copy.js' => 'cf32921f', + 'rsrc/js/core/behavior-copy.js' => '96b63a02', 'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94', 'rsrc/js/core/behavior-device.js' => 'ac2b1e01', 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '6bc7ccf7', @@ -499,7 +499,7 @@ 'rsrc/js/core/behavior-reveal-content.js' => 'b105a3a6', 'rsrc/js/core/behavior-scrollbar.js' => '92388bae', 'rsrc/js/core/behavior-search-typeahead.js' => '1cb7d027', - 'rsrc/js/core/behavior-select-content.js' => 'e8240b50', + 'rsrc/js/core/behavior-select-content.js' => 'c538cbfc', 'rsrc/js/core/behavior-select-on-click.js' => '66365ee2', 'rsrc/js/core/behavior-setup-check-https.js' => '01384686', 'rsrc/js/core/behavior-time-typeahead.js' => '5803b9e7', @@ -567,7 +567,7 @@ 'differential-revision-list-css' => '93d2df7d', 'differential-table-of-contents-css' => 'bba788b9', 'diffusion-css' => 'e46232d6', - 'diffusion-icons-css' => '23b31a1b', + 'diffusion-icons-css' => 'e812add2', 'diffusion-readme-css' => 'b68a76e4', 'diffusion-repository-css' => 'b89e8c6c', 'diviner-shared-css' => '4bd263b0', @@ -644,7 +644,7 @@ 'javelin-behavior-owners-path-editor' => 'ff688a7a', 'javelin-behavior-passphrase-credential-control' => '48fe33d0', 'javelin-behavior-phabricator-autofocus' => '65bb0011', - 'javelin-behavior-phabricator-clipboard-copy' => 'cf32921f', + 'javelin-behavior-phabricator-clipboard-copy' => '96b63a02', 'javelin-behavior-phabricator-gesture' => 'b58d1a2a', 'javelin-behavior-phabricator-gesture-example' => '242dedd0', 'javelin-behavior-phabricator-keyboard-pager' => '1325b731', @@ -687,7 +687,7 @@ 'javelin-behavior-repository-crossreference' => '44d48cd1', 'javelin-behavior-scrollbar' => '92388bae', 'javelin-behavior-search-reorder-queries' => 'b86f297f', - 'javelin-behavior-select-content' => 'e8240b50', + 'javelin-behavior-select-content' => 'c538cbfc', 'javelin-behavior-select-on-click' => '66365ee2', 'javelin-behavior-setup-check-https' => '01384686', 'javelin-behavior-stripe-payment-form' => '02cb4398', @@ -1770,6 +1770,12 @@ 'javelin-dom', 'javelin-router', ), + '96b63a02' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-notification', + ), '98ef467f' => array( 'javelin-behavior', 'javelin-dom', @@ -2022,6 +2028,11 @@ 'javelin-workboard-card', 'javelin-workboard-header', ), + 'c538cbfc' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + ), 'c687e867' => array( 'javelin-behavior', 'javelin-dom', @@ -2063,11 +2074,6 @@ 'phuix-formation-column-view', 'phuix-formation-flank-view', ), - 'cf32921f' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - ), 'd12d214f' => array( 'javelin-install', 'javelin-dom', @@ -2146,11 +2152,6 @@ 'javelin-dom', 'phabricator-draggable-list', ), - 'e8240b50' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - ), 'e9a2940f' => array( 'javelin-behavior', 'javelin-request', diff --git a/src/applications/diffusion/view/DiffusionCloneURIView.php b/src/applications/diffusion/view/DiffusionCloneURIView.php index c9dae57563..3a79e8d72c 100644 --- a/src/applications/diffusion/view/DiffusionCloneURIView.php +++ b/src/applications/diffusion/view/DiffusionCloneURIView.php @@ -40,6 +40,7 @@ public function render() { require_celerity_resource('diffusion-icons-css'); Javelin::initBehavior('select-content'); + Javelin::initBehavior('phabricator-clipboard-copy'); $uri_id = celerity_generate_unique_node_id(); @@ -53,6 +54,11 @@ public function render() { 'value' => $display, 'class' => 'diffusion-clone-uri', 'readonly' => 'true', + 'sigil' => 'select-content', + 'meta' => array( + 'selectID' => $uri_id, + 'once' => true, + ), )); $uri = $this->getRepositoryURI(); @@ -71,17 +77,30 @@ public function render() { break; } - $io = id(new PHUIButtonView()) + $io = javelin_tag( + 'span', + array( + 'class' => 'diffusion-clone-uri-io', + 'sigil' => 'has-tooltip', + 'meta' => array( + 'tip' => $io_tip, + ), + ), + id(new PHUIIconView())->setIcon($io_icon)); + + $copy = id(new PHUIButtonView()) ->setTag('a') ->setColor(PHUIButtonView::GREY) - ->setIcon($io_icon) + ->setIcon('fa-clipboard') ->setHref('#') - ->addSigil('select-content') + ->addSigil('clipboard-copy') ->addSigil('has-tooltip') ->setMetadata( array( - 'tip' => $io_tip, - 'selectID' => $uri_id, + 'tip' => pht('Copy repository URI'), + 'text' => $display, + 'successMessage' => pht('Repository URI copied.'), + 'errorMessage' => pht('Copy of Repository URI failed.'), )); switch ($uri->getEffectiveIOType()) { @@ -121,19 +140,18 @@ public function render() { ->setHref($auth_uri) ->setDisabled($auth_disabled); - $cells = array(); - $cells[] = phutil_tag('td', array(), $input); - $cells[] = phutil_tag('th', array(), $io); - $cells[] = phutil_tag('th', array(), $credentials); - - $row = phutil_tag('tr', array(), $cells); + $elements = array(); + $elements[] = $io; + $elements[] = $input; + $elements[] = $copy; + $elements[] = $credentials; return phutil_tag( - 'table', + 'div', array( - 'class' => 'diffusion-clone-uri-table', + 'class' => 'diffusion-clone-uri-wrapper', ), - $row); + $elements); } } diff --git a/src/applications/uiexample/examples/PHUIButtonExample.php b/src/applications/uiexample/examples/PHUIButtonExample.php index 89f8e15fe8..7d00290505 100644 --- a/src/applications/uiexample/examples/PHUIButtonExample.php +++ b/src/applications/uiexample/examples/PHUIButtonExample.php @@ -115,6 +115,8 @@ public function renderExample() { $button->setMetadata( array( 'text' => $copy, + 'successMessage' => pht('Text copied into clipboard.'), + 'errorMessage' => pht('Failed to copy text into clipboard.'), )); } diff --git a/webroot/rsrc/css/application/diffusion/diffusion-icons.css b/webroot/rsrc/css/application/diffusion/diffusion-icons.css index 072db01660..1b1887e904 100644 --- a/webroot/rsrc/css/application/diffusion/diffusion-icons.css +++ b/webroot/rsrc/css/application/diffusion/diffusion-icons.css @@ -2,11 +2,6 @@ * @provides diffusion-icons-css */ -input.diffusion-clone-uri { - display: block; - width: 100%; -} - .diffusion-clone-extras { font-size: 11px; text-align: right; @@ -35,21 +30,35 @@ input.diffusion-clone-uri { padding: 0; } -.diffusion-clone-uri-table { - width: 100%; +.diffusion-clone-uri-wrapper { + display: flex; } -.diffusion-clone-uri-table th { - width: 24px; - padding: 0 0 0 4px; +.diffusion-clone-uri-wrapper .diffusion-clone-uri-io { + align-items: center; + justify-content: center; + border: 1px solid {$greyborder}; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + display: flex; + width: 38px; } -.diffusion-clone-uri-table th a.button { +.diffusion-clone-uri-wrapper a.button { width: 12px; height: 19px; + margin-left: 4px; } -.diffusion-clone-uri-table th a.button .phui-icon-view { +.diffusion-clone-uri-wrapper a.button .phui-icon-view { left: 15px; top: 7px; } + +.diffusion-clone-uri-wrapper input.diffusion-clone-uri { + border-left: none; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + display: block; + width: 100%; +} diff --git a/webroot/rsrc/js/core/behavior-copy.js b/webroot/rsrc/js/core/behavior-copy.js index 4457f6e022..210d9ebc56 100644 --- a/webroot/rsrc/js/core/behavior-copy.js +++ b/webroot/rsrc/js/core/behavior-copy.js @@ -3,41 +3,81 @@ * @requires javelin-behavior * javelin-dom * javelin-stratcom + * phabricator-notification * @javelin */ JX.behavior('phabricator-clipboard-copy', function() { - if (!document.queryCommandSupported) { - return; - } + var fallback_working = document.queryCommandSupported && + document.queryCommandSupported('copy'); - if (!document.queryCommandSupported('copy')) { + if (!navigator.clipboard && !fallback_working) { return; } JX.DOM.alterClass(document.body, 'supports-clipboard', true); - JX.Stratcom.listen('click', 'clipboard-copy', function(e) { - e.kill(); - - var data = e.getNodeData('clipboard-copy'); + var copy_fallback = function(text) { var attr = { - value: data.text || '', + value: text || '', className: 'clipboard-buffer' }; var node = JX.$N('textarea', attr); document.body.appendChild(node); - try { - node.select(); - document.execCommand('copy'); - } catch (ignored) { - // Ignore any errors we hit. + node.select(); + document.execCommand('copy'); + + JX.DOM.remove(node); + }; + + var show_success_message = function(message) { + if (!message) { + return; } + new JX.Notification() + .setContent(message) + .alterClassName('jx-notification-done', true) + .setDuration(8000) + .show(); + }; - JX.DOM.remove(node); + var show_error_message = function(message) { + if (!message) { + return; + } + new JX.Notification() + .setContent(message) + .alterClassName('jx-notification-error', true) + .setDuration(8000) + .show(); + }; + + JX.Stratcom.listen('click', 'clipboard-copy', function(e) { + var data = e.getNodeData('clipboard-copy'); + var text = data.text || ''; + + var copy = async function( // jshint ignore:line + text, + successMessage, + errorMessage + ) { + try { + if (navigator.clipboard) { + await navigator.clipboard.writeText(text); + } else { + copy_fallback(text); + } + show_success_message(successMessage); + } catch (ex) { + show_error_message(errorMessage); + } + }; + + e.kill(); + copy(text, data.successMessage, data.errorMessage); }); }); diff --git a/webroot/rsrc/js/core/behavior-select-content.js b/webroot/rsrc/js/core/behavior-select-content.js index b54b6308cf..92e4dcfc0b 100644 --- a/webroot/rsrc/js/core/behavior-select-content.js +++ b/webroot/rsrc/js/core/behavior-select-content.js @@ -16,8 +16,16 @@ JX.behavior('select-content', function() { var node = e.getNode('select-content'); var data = JX.Stratcom.getData(node); + if (data.once && data.selected) { + return; + } + var target = JX.$(data.selectID); JX.DOM.focus(target); target.select(); + + if (data.once) { + JX.Stratcom.addData(node, {selected: true}); + } }); }); From 7d8c84a7bdc8fb43674341b97c36c9d8ae1d894a Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 23 Feb 2024 13:33:44 +0200 Subject: [PATCH 395/425] Update PHP versions in install guide: Remove 5, Add 8. Summary: Ref T15737. Ref T15554. Ref T15064. Ref T15047. Test Plan: Generate diviner, read section. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15737, T15554, T15064, T15047 Differential Revision: https://we.phorge.it/D25537 --- src/docs/user/installation_guide.diviner | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index 135c94adc0..4b10b49405 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -80,10 +80,8 @@ You will also need: - **MySQL**: You need MySQL. We strongly recommend MySQL 5.5 or newer. You will need a server with multiple databases. - **PHP**: You need a PHP engine: - - PHP 5 - 5.5 or newer. - - PHP 7 - 7.1 or newer. - - PHP 8 - **Not yet supported**. We're working on it as fast as we can, - and expect to support PHP 8 Real Soon Now. + - PHP 7 - 7.2 or newer. + - PHP 8 - 8.0 or newer. - **git**: You need git 2.5.0 or newer on the server. No particular version is needed on your clients. From 8667ed45fada1fe39100144d4c4a52a12d78e7de Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 23 Feb 2024 17:05:48 +0200 Subject: [PATCH 396/425] Fix an instance of `strlen(null)` Summary: Ref Q112, ref T15064. Test Plan: Enter the text `[[hello]]` in a remarkup field - without this change, it causes a deprecation warning in the log. This doesn't happen if the content looks like a valid URL - this is the "Wiki Link" syntax. Reviewers: bekay, O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15064 Differential Revision: https://we.phorge.it/D25539 --- src/applications/phriction/markup/PhrictionRemarkupRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/phriction/markup/PhrictionRemarkupRule.php b/src/applications/phriction/markup/PhrictionRemarkupRule.php index 8394b23218..0ea6e07d09 100644 --- a/src/applications/phriction/markup/PhrictionRemarkupRule.php +++ b/src/applications/phriction/markup/PhrictionRemarkupRule.php @@ -191,7 +191,7 @@ public function didMarkupText() { // If the name is something meaningful to humans, we'll render this // in text as: "Title" . Otherwise, we'll just render: . - $is_interesting_name = (bool)strlen($name); + $is_interesting_name = phutil_nonempty_string($name); $target = idx($document_map, $key, null); From 74ec4f7882a32a47803a8d1635041a82c7c9a158 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 28 Feb 2024 09:34:11 +0100 Subject: [PATCH 397/425] Fix typo "pohrge" in "Adding New Classes" documentation Summary: Fix typo "pohrge" in "Adding New Classes" documentation Test Plan: Carefully read letter by letter. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25541 --- src/docs/contributor/adding_new_classes.diviner | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/contributor/adding_new_classes.diviner b/src/docs/contributor/adding_new_classes.diviner index 64ab228ea3..83a19c10fb 100644 --- a/src/docs/contributor/adding_new_classes.diviner +++ b/src/docs/contributor/adding_new_classes.diviner @@ -43,7 +43,7 @@ Extensions Directory ==================== The easiest way to extend Phorge by adding new classes is to drop them -into the extensions directory, at `pohrge/src/extensions/`. +into the extensions directory, at `phorge/src/extensions/`. This is intended as a quick way to add small pieces of functionality, test new features, or get started on a larger project. Extending Phorge like this From 7cee42ca260107615ce2eeb2e427fb8f83ab553b Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Wed, 28 Feb 2024 12:30:32 +0100 Subject: [PATCH 398/425] Replace all phurl.io short URIs with target URIs Summary: As of February 2024, phurl.io (which is not run by Phorge.it) shows HTTP 503 errors instead of redirecting to the target URIs. Thus replace any URIs pointing to phurl.io with the corresponding target URIs, based on the mapping listed on https://secure.phabricator.com/phurl/. Closes T15746 Test Plan: Carefully read the diff. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15746 Differential Revision: https://we.phorge.it/D25543 --- .../config/check/PhabricatorManualActivitySetupCheck.php | 8 ++++---- .../config/check/PhabricatorPHPPreflightSetupCheck.php | 4 ++-- .../controller/PhabricatorApplicationSearchController.php | 2 +- .../user/configuration/configuring_inbound_email.diviner | 4 ++-- .../user/configuration/configuring_outbound_email.diviner | 4 ++-- src/docs/user/userguide/multi_factor_auth.diviner | 2 +- support/startup/PhabricatorStartup.php | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php index 660792ca53..59b04d895f 100644 --- a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php +++ b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php @@ -59,10 +59,10 @@ private function raiseSearchReindexIssue() { phutil_tag( 'a', array( - 'href' => 'https://phurl.io/u/reindex', + 'href' => 'https://secure.phabricator.com/T11932', 'target' => '_blank', ), - 'https://phurl.io/u/reindex')); + 'https://secure.phabricator.com/T11932')); $message[] = pht( 'After rebuilding the index, run this command to clear this setup '. @@ -128,10 +128,10 @@ private function raiseRebuildIdentitiesIssue() { phutil_tag( 'a', array( - 'href' => 'https://phurl.io/u/repoIdentities', + 'href' => 'https://secure.phabricator.com/T12164', 'target' => '_blank', ), - 'https://phurl.io/u/repoIdentities')); + 'https://secure.phabricator.com/T12164')); $message[] = pht( 'After rebuilding repository identities, run this command to clear '. diff --git a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php index 02c1134dc3..322ad26061 100644 --- a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php @@ -18,7 +18,7 @@ protected function executeChecks() { 'You are running PHP version %s. PHP versions between 7.0 and 7.1 '. 'are not supported'. "\n\n". - 'PHP removed reqiured signal handling features in '. + 'PHP removed required signal handling features in '. 'PHP 7.0, and did not restore an equivalent mechanism until PHP 7.1.'. "\n\n". 'Upgrade to PHP 7.1 or newer (recommended) or downgrade to an older '. @@ -30,7 +30,7 @@ protected function executeChecks() { ->setName(pht('PHP 7.0-7.1 Not Supported')) ->setMessage($message) ->addLink( - 'https://phurl.io/u/php7', + 'https://secure.phabricator.com/T12101', pht('PHP 7 Compatibility Information')); return; diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index b7b512156f..d8f53538e4 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -854,7 +854,7 @@ public static function newOverheatedError($has_results) { $overheated_link = phutil_tag( 'a', array( - 'href' => 'https://phurl.io/u/overheated', + 'href' => 'https://secure.phabricator.com/T13274', 'target' => '_blank', ), pht('Learn More')); diff --git a/src/docs/user/configuration/configuring_inbound_email.diviner b/src/docs/user/configuration/configuring_inbound_email.diviner index c7c9818057..75338f149e 100644 --- a/src/docs/user/configuration/configuring_inbound_email.diviner +++ b/src/docs/user/configuration/configuring_inbound_email.diviner @@ -184,8 +184,8 @@ like this: - Configure a mailer in `cluster.mailers` with your Mailgun API key. Use of Mailgun is discouraged because of concerns that they may not be a -trustworthy custodian of sensitive data. See for -discussion and context. +trustworthy custodian of sensitive data. +See for discussion and context. Postmark Setup ============== diff --git a/src/docs/user/configuration/configuring_outbound_email.diviner b/src/docs/user/configuration/configuring_outbound_email.diviner index 07cacb6b25..91c8962b59 100644 --- a/src/docs/user/configuration/configuring_outbound_email.diviner +++ b/src/docs/user/configuration/configuring_outbound_email.diviner @@ -222,8 +222,8 @@ Mailer: Mailgun |---------| Use of Mailgun is discouraged because of concerns that they may not be a -trustworthy custodian of sensitive data. See for -discussion and context. +trustworthy custodian of sensitive data. +See for discussion and context. Mailgun is a third-party email delivery service. You can learn more at . Mailgun is easy to configure and works well. diff --git a/src/docs/user/userguide/multi_factor_auth.diviner b/src/docs/user/userguide/multi_factor_auth.diviner index d445727e6c..9ef92157c9 100644 --- a/src/docs/user/userguide/multi_factor_auth.diviner +++ b/src/docs/user/userguide/multi_factor_auth.diviner @@ -106,7 +106,7 @@ When you're asked to confirm your identity in the future, you'll be texted an authorization code to enter into the prompt. (WARNING) SMS is a very weak factor and can be compromised or intercepted. For -details, see: . +details, see: . Factor: Duo diff --git a/support/startup/PhabricatorStartup.php b/support/startup/PhabricatorStartup.php index b4a4314ac0..10c6295587 100644 --- a/support/startup/PhabricatorStartup.php +++ b/support/startup/PhabricatorStartup.php @@ -577,7 +577,7 @@ private static function verifyPHP() { self::didFatal( 'This HTTP request included a "Proxy:" header, poisoning the '. 'environment (CVE-2016-5385 / httpoxy). Declining to process this '. - 'request. For details, see: https://phurl.io/u/httpoxy'); + 'request. For details, see: https://secure.phabricator.com/T11359'); } } From 6df26b98c10465fe1a0e50c97ba4ab55131cc17a Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Wed, 28 Feb 2024 16:24:25 +0100 Subject: [PATCH 399/425] Comments: avoid confusing warning about "core:file" Summary: If you are reading this commit message, it probably happened to you this situation: 1. Somebody published a comment that shows the file F123 2. You Submit another comment that shows F123 again After you click "Submit", a wild warning may appear: > Post as Comment > ---- > The action you are taking has no effect: > Transaction (of type "core:file") has no effect. > Do you want to post your comment anyway? > [Undo] [Continue] In this case you probably contact your favorite Phorge administrator to translate that stuff. This is an example unofficial translation: > Post as Comment > ---- > You mentioned a file. Phorge loves files. Phorge attach files. But somebody else already > mentioned that file, successfully. So, since Phorge attach Files, Phorge will result in > doing nothing special, since the file is already perfectly attached by Phorge. > Phorge will just post your comment. > Would you like me to just STFU and post your comment? > [OMG I'm scared] [Post comment and STFU] After this change you don't see anymore that warning. So it's even rarer in your Pokedex. Closes T15293 Test Plan: - Create a comment showing F123 (an existing file) - Create another comment showing F123 - No wild warnings Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: 20after4, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15293 Differential Revision: https://we.phorge.it/D25544 --- .../editor/PhabricatorApplicationTransactionEditor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 0e7333b342..b3db97af49 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -2320,6 +2320,7 @@ private function newFileTransaction( } $xaction = $object->getApplicationTransactionTemplate() + ->setIgnoreOnNoEffect(true) ->setTransactionType(PhabricatorTransactions::TYPE_FILE) ->setMetadataValue('attach.implicit', true) ->setNewValue($new_map); From 32b5139d46dc8a4fd3146e03f338664e5c947a38 Mon Sep 17 00:00:00 2001 From: Mukunda Modell Date: Thu, 29 Feb 2024 11:29:12 -0600 Subject: [PATCH 400/425] Diviner: Improve documentation for remarkup code blocks Summary: Improve remarkup reference documentation. * Added a heading for `Code Blocks` * Added a sentence mentioning the ##```lang## syntax which was added in {T15481} Test Plan: * Run `bin/diviner generate` to regen diviner docs. * Check out the code blocks section of [[/book/phorge/article/remarkup/#code-blocks|Remarkup Reference]] Reviewers: valerio.bozzolan, O1 Blessed Committers Reviewed By: valerio.bozzolan, O1 Blessed Committers Subscribers: tobiaswiese, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25547 --- src/docs/user/userguide/remarkup.diviner | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/docs/user/userguide/remarkup.diviner b/src/docs/user/userguide/remarkup.diviner index fa0128127c..46e732c386 100644 --- a/src/docs/user/userguide/remarkup.diviner +++ b/src/docs/user/userguide/remarkup.diviner @@ -196,6 +196,9 @@ When rendered, this produces: - [X] Preheat oven to 450 degrees. - [ ] Zest 35 lemons. +Code Blocks +=========== + Make **code blocks** by indenting two spaces: f(x, y); @@ -211,6 +214,9 @@ You can specify a language for syntax highlighting with `lang=xxx`: lang=html ... +When using fenced code blocks (triple backticks) you can simply append the +language right after the backticks, like this: ##```html## + This will highlight the block using a highlighter for that language, if one is available (in most cases, this means you need to configure Pygments): From bf15a2d98ca3c67bf46b2b3b7c98cd99a3333118 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sun, 3 Mar 2024 12:17:08 +0100 Subject: [PATCH 401/425] Trigger: Add Sound "Coin" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: One preview is better than a thousand of words: {F270656} This Sound is probably universally recognized thanks to videogames like Super Mario® or other popular platforms; but this specific Coin Sound is not proprietary: it's released with a Free as in Freedom of Speach license: Creative Commons 0. As if that were not enough, this Coin sound, instead of potentially creating frustration like many others - it positively stimulates part of the human cerebral cortex involved in making humans more addicted to Phorge. To be precise, this Coin sound is particularly suitable to be associated with Trigger actions such as: - Mark as Resolved - Move on to the next Milestone - etc. But the only limitation here, as usual in Phorge, is your imagination. Insert a Coin and have fun with Phorge. Complete sound credits: https://freesound.org/people/Jocabundus/sounds/678385/ 2023, Jocabundus, Creative Commons Zero (public domain dedication) This change also introduces a Credits page in Diviner to mentions this kind of things. Future contributions in that page are encouraged. Insert a Coin. Play again with Phorge. Ref T15178 Ref T15248 Test Plan: - Create a Project with a Workboard - Create a Column - Column > Gear > New Trigger... - Action > Play Sound > "Coin" - Create a Task on the Backlog column, and drop it on your new Column - Turn on audio, have fun! Reviewers: O1 Blessed Committers, Cigaryno, avivey, aklapper Reviewed By: O1 Blessed Committers, Cigaryno, avivey, aklapper Subscribers: avivey, aklapper, chris, speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15178, T15248 Differential Revision: https://we.phorge.it/D25079 --- resources/celerity/map.php | 1 + .../PhabricatorProjectTriggerPlaySoundRule.php | 4 ++++ webroot/rsrc/audio/basic/coin.license.txt | 3 +++ webroot/rsrc/audio/basic/coin.mp3 | Bin 0 -> 4302 bytes 4 files changed, 8 insertions(+) create mode 100644 webroot/rsrc/audio/basic/coin.license.txt create mode 100644 webroot/rsrc/audio/basic/coin.mp3 diff --git a/resources/celerity/map.php b/resources/celerity/map.php index a9919fc998..c52155b14e 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -20,6 +20,7 @@ 'maniphest.pkg.js' => 'c9308721', 'rsrc/audio/basic/alert.mp3' => '17889334', 'rsrc/audio/basic/bing.mp3' => 'a817a0c3', + 'rsrc/audio/basic/coin.mp3' => '44ef365f', 'rsrc/audio/basic/pock.mp3' => '0fa843d0', 'rsrc/audio/basic/tap.mp3' => '02d16994', 'rsrc/audio/basic/ting.mp3' => 'a6b6540e', diff --git a/src/applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php b/src/applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php index 9e99eb070f..e67e1f1a64 100644 --- a/src/applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php +++ b/src/applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php @@ -99,6 +99,10 @@ private static function getSoundMap() { 'name' => pht('Bing'), 'uri' => celerity_get_resource_uri('/rsrc/audio/basic/bing.mp3'), ), + 'coin' => array( + 'name' => pht('Coin'), + 'uri' => celerity_get_resource_uri('/rsrc/audio/basic/coin.mp3'), + ), 'glass' => array( 'name' => pht('Glass'), 'uri' => celerity_get_resource_uri('/rsrc/audio/basic/ting.mp3'), diff --git a/webroot/rsrc/audio/basic/coin.license.txt b/webroot/rsrc/audio/basic/coin.license.txt new file mode 100644 index 0000000000..5db113800d --- /dev/null +++ b/webroot/rsrc/audio/basic/coin.license.txt @@ -0,0 +1,3 @@ +https://freesound.org/people/Jocabundus/sounds/678385/ + +2023, Jocabundus, Creative Commons Zero (public domain dedication) diff --git a/webroot/rsrc/audio/basic/coin.mp3 b/webroot/rsrc/audio/basic/coin.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5604194a4e38534e4c1c11254d847951c3f56a80 GIT binary patch literal 4302 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 literal 0 HcmV?d00001 From 892f527b9f6330a1131b564d2aef0b3f8e8aafa5 Mon Sep 17 00:00:00 2001 From: Merula Turdus Date: Tue, 9 Jan 2024 22:44:56 +0100 Subject: [PATCH 402/425] Improve remarkup codeblock syntax highlighting when using Dark Mode Summary: PhabricatorDefaultSyntaxStyle works now with 'CSS-color-aliases' instead of fixed colors. The aliases are translated in the corresponding themes CelerityPostprocessor. | Before | After | |-----------|-----------| | {F709419} | {F709421} | Ref T15056 Test Plan: 1) bin/celerity syntax 2) bin/celerity map 3) bin/cache purge --all 4) sign in 5) go to user's Settings > Display Preferences and select the Accessibility (user interface) "Dark Mode". 6) go to Phriction or Maniphest and add a codeblock. Example: ``` lang=php '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', 'core.pkg.css' => '7f47b2ac', + 'core.pkg.css' => '41cc201d', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '6d3700f0', @@ -186,7 +187,7 @@ 'rsrc/css/phui/workboards/phui-workpanel.css' => 'bc06f022', 'rsrc/css/sprite-login.css' => '07052ee0', 'rsrc/css/sprite-tokens.css' => 'f1896dc5', - 'rsrc/css/syntax/syntax-default.css' => '055fc231', + 'rsrc/css/syntax/syntax-default.css' => 'c0307dc6', 'rsrc/externals/d3/d3.min.js' => '9d068042', 'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '23f8c698', 'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '70983df0', @@ -907,7 +908,7 @@ 'setup-issue-css' => '93231115', 'sprite-login-css' => '07052ee0', 'sprite-tokens-css' => 'f1896dc5', - 'syntax-default-css' => '055fc231', + 'syntax-default-css' => 'c0307dc6', 'syntax-highlighting-css' => '548567f6', 'tokens-css' => 'ce5a50bd', 'trigger-rule' => '41b7b4f6', diff --git a/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php b/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php index 39ee4be57a..b1298e35a7 100644 --- a/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php @@ -218,6 +218,67 @@ public function buildVariables() { 'grey.button.gradient' => 'linear-gradient(to bottom, #223246, #223246)', 'grey.button.hover' => 'linear-gradient(to bottom, #1c293b, #1c293b)', + // Codeblock syntax highlighting + 'syntax.highlighted-line' => '#fa8', + 'syntax.comment' => '#6d6', + 'syntax.comment-multiline' => '#6d6', + 'syntax.comment-single' => '#6d6', + 'syntax.comment-special' => '#6d6', + 'syntax.string-doc' => '#fff', + 'syntax.string-heredoc' => '#fff', + 'syntax.string' => '#f88', + 'syntax.string-backtick' => '#f88', + 'syntax.literal-string-char' => '#f88', + 'syntax.string-double' => '#f88', + 'syntax.string-single' => '#f88', + 'syntax.string-other' => '#f88', + 'syntax.string-regex' => '#f88', + 'syntax.name-variable' => '#8ff', + 'syntax.variable-instance' => '#8ff', + 'syntax.variable-global' => '#8ff', + 'syntax.name-attribute' => '#4cf', + 'syntax.keyword-constant' => '#0cf', + 'syntax.name-operator' => '#0cf', + 'syntax.keyword' => '#e8e', + 'syntax.keyword-declaration' => '#e8e', + 'syntax.keyword-namespace' => '#e8e', + 'syntax.keyword-type' => '#e8e', + 'syntax.comment-preproc' => '#08f', + 'syntax.keyword-preproc' => '#08f', + 'syntax.keyword-reserved' => '#08f', + 'syntax.name-builtin' => '#08f', + 'syntax.builtin-pseudo' => '#08f', + 'syntax.name-class' => '#4ff', + 'syntax.name-tag' => '#dc0', + 'syntax.name-variable-class' => '#4ff', + 'syntax.name-function' => '#8af', + 'syntax.name-exception' => '#ed8', + 'syntax.operator' => '#aaa', + 'syntax.punctuation' => '#aaa', + 'syntax.literal-string-symbol' => '#aaa', + 'syntax.literal-number' => '#fa4', + 'syntax.literal-number-float' => '#fa4', + 'syntax.literal-number-hex' => '#fa4', + 'syntax.literal-number-integer' => '#fa4', + 'syntax.literal-number-octal' => '#fa4', + 'syntax.literal-number-integer-long' => '#fa4', + 'syntax.generic-deleted' => '#f55', + 'syntax.generic-red' => '#f52', + 'syntax.generic-heading' => '#fff', + 'syntax.generic-inserted' => '#4f4', + 'syntax.generic-output' => '#ccc', + 'syntax.generic-prompt' => '#fff', + 'syntax.generic-underline' => '#f4f', + 'syntax.generic-traceback' => '#07f', + 'syntax.name-decorator' => '#c7f', + 'syntax.name-identifier' => '#92969d', + 'syntax.name-entity' => '#f44', + 'syntax.name-label' => '#aa0', + 'syntax.name-namespace' => '#48f', + 'syntax.operator-word' => '#c7f', + 'syntax.text-whitespace' => '#bbb', + 'syntax.literal-string-escape' => '#d84', + 'syntax.literal-string-interpol' => '#b6b', ); } diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php index be039772c1..a4668775cc 100644 --- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php @@ -247,6 +247,67 @@ public function buildVariables() { 'delete-color' => '#c0392b', 'create-color' => '#139543', + // Codeblock syntax highlighting + 'syntax.highlighted-line' => '#ffffcc', + 'syntax.comment' => '#74777d', + 'syntax.comment-multiline' => '#74777d', + 'syntax.comment-single' => '#74777d', + 'syntax.comment-special' => '#74777d', + 'syntax.string-doc' => '#000000', + 'syntax.string-heredoc' => '#000000', + 'syntax.string' => '#766510', + 'syntax.string-backtick' => '#766510', + 'syntax.literal-string-char' => '#766510', + 'syntax.string-double' => '#766510', + 'syntax.string-single' => '#766510', + 'syntax.string-other' => '#766510', + 'syntax.string-regex' => '#bb6688', + 'syntax.name-variable' => '#001294', + 'syntax.variable-instance' => '#001294', + 'syntax.variable-global' => '#001294', + 'syntax.name-attribute' => '#354bb3', + 'syntax.keyword-constant' => '#000a65', + 'syntax.name-operator' => '#000a65', + 'syntax.keyword' => '#aa4000', + 'syntax.keyword-declaration' => '#aa4000', + 'syntax.keyword-namespace' => '#aa4000', + 'syntax.keyword-type' => '#aa4000', + 'syntax.comment-preproc' => '#304a96', + 'syntax.keyword-preproc' => '#304a96', + 'syntax.keyword-reserved' => '#304a96', + 'syntax.name-builtin' => '#304a96', + 'syntax.builtin-pseudo' => '#304a96', + 'syntax.name-class' => '#00702a', + 'syntax.name-tag' => '#00702a', + 'syntax.name-variable-class' => '#00702a', + 'syntax.name-function' => '#004012', + 'syntax.name-exception' => '#004012', + 'syntax.operator' => '#aa2211', + 'syntax.punctuation' => '#000000', + 'syntax.literal-string-symbol' => '#aa2211', + 'syntax.literal-number' => '#601200', + 'syntax.literal-number-float' => '#601200', + 'syntax.literal-number-hex' => '#601200', + 'syntax.literal-number-integer' => '#601200', + 'syntax.literal-number-octal' => '#601200', + 'syntax.literal-number-integer-long' => '#601200', + 'syntax.generic-deleted' => '#a00000', + 'syntax.generic-red' => '#ff0000', + 'syntax.generic-heading' => '#000080', + 'syntax.generic-inserted' => '#00a000', + 'syntax.generic-output' => '#808080', + 'syntax.generic-prompt' => '#000080', + 'syntax.generic-underline' => '#800080', + 'syntax.generic-traceback' => '#0040d0', + 'syntax.name-decorator' => '#aa22ff', + 'syntax.name-identifier' => '#92969d', + 'syntax.name-entity' => '#d2413a', + 'syntax.name-label' => '#a0a000', + 'syntax.name-namespace' => '#0000ff', + 'syntax.operator-word' => '#aa22ff', + 'syntax.text-whitespace' => '#bbbbbb', + 'syntax.literal-string-escape' => '#bb6622', + 'syntax.literal-string-interpol' => '#bb66bb', ); } diff --git a/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php b/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php index 72b6dfb4b0..35c9df7857 100644 --- a/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php +++ b/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php @@ -11,65 +11,67 @@ public function getStyleName() { public function getStyleMap() { return array( - 'hll' => 'color: #ffffcc', - 'c' => 'color: #74777d', - 'cm' => 'color: #74777d', - 'c1' => 'color: #74777d', - 'cs' => 'color: #74777d', - 'sd' => 'color: #000000', - 'sh' => 'color: #000000', - 's' => 'color: #766510', - 'sb' => 'color: #766510', - 'sc' => 'color: #766510', - 's2' => 'color: #766510', - 's1' => 'color: #766510', - 'sx' => 'color: #766510', - 'sr' => 'color: #bb6688', - 'nv' => 'color: #001294', - 'vi' => 'color: #001294', - 'vg' => 'color: #001294', - 'na' => 'color: #354bb3', - 'kc' => 'color: #000a65', - 'no' => 'color: #000a65', - 'k' => 'color: #aa4000', - 'kd' => 'color: #aa4000', - 'kn' => 'color: #aa4000', - 'kt' => 'color: #aa4000', - 'cp' => 'color: #304a96', - 'kp' => 'color: #304a96', - 'kr' => 'color: #304a96', - 'nb' => 'color: #304a96', - 'bp' => 'color: #304a96', - 'nc' => 'color: #00702a', - 'nt' => 'color: #00702a', - 'vc' => 'color: #00702a', - 'nf' => 'color: #004012', - 'nx' => 'color: #004012', - 'o' => 'color: #aa2211', - 'ss' => 'color: #aa2211', - 'm' => 'color: #601200', - 'mf' => 'color: #601200', - 'mh' => 'color: #601200', - 'mi' => 'color: #601200', - 'mo' => 'color: #601200', - 'il' => 'color: #601200', - 'gd' => 'color: #a00000', - 'gr' => 'color: #ff0000', - 'gh' => 'color: #000080', - 'gi' => 'color: #00a000', - 'go' => 'color: #808080', - 'gp' => 'color: #000080', - 'gu' => 'color: #800080', - 'gt' => 'color: #0040d0', - 'nd' => 'color: #aa22ff', - 'ni' => 'color: #92969d', - 'ne' => 'color: #d2413a', - 'nl' => 'color: #a0a000', - 'nn' => 'color: #0000ff', - 'ow' => 'color: #aa22ff', - 'w' => 'color: #bbbbbb', - 'se' => 'color: #bb6622', - 'si' => 'color: #bb66bb', + 'hll' => 'color: {$syntax.highlighted-line}', + 'c' => 'color: {$syntax.comment}', + 'cm' => 'color: {$syntax.comment-multiline}', + 'dc' => 'color: {$syntax.comment-multiline}', + 'c1' => 'color: {$syntax.comment-single}', + 'cs' => 'color: {$syntax.comment-special}', + 'sd' => 'color: {$syntax.string-doc}', + 'sh' => 'color: {$syntax.string-heredoc}', + 's' => 'color: {$syntax.string}', + 'sb' => 'color: {$syntax.string-backtick}', + 'sc' => 'color: {$syntax.literal-string-char}', + 's2' => 'color: {$syntax.string-double}', + 's1' => 'color: {$syntax.string-single}', + 'sx' => 'color: {$syntax.string-other}', + 'sr' => 'color: {$syntax.string-regex}', + 'nv' => 'color: {$syntax.name-variable}', + 'vi' => 'color: {$syntax.variable-instance}', + 'vg' => 'color: {$syntax.variable-global}', + 'na' => 'color: {$syntax.name-attribute}', + 'kc' => 'color: {$syntax.keyword-constant}', + 'no' => 'color: {$syntax.name-operator}', + 'k' => 'color: {$syntax.keyword}', + 'kd' => 'color: {$syntax.keyword-declaration}', + 'kn' => 'color: {$syntax.keyword-namespace}', + 'kt' => 'color: {$syntax.keyword-type}', + 'cp' => 'color: {$syntax.comment-preproc}', + 'kp' => 'color: {$syntax.keyword-preproc}', + 'kr' => 'color: {$syntax.keyword-reserved}', + 'nb' => 'color: {$syntax.name-builtin}', + 'bp' => 'color: {$syntax.builtin-pseudo}', + 'nc' => 'color: {$syntax.name-class}', + 'nt' => 'color: {$syntax.name-tag}', + 'vc' => 'color: {$syntax.name-variable-class}', + 'nf' => 'color: {$syntax.name-function}', + 'nx' => 'color: {$syntax.name-exception}', + 'o' => 'color: {$syntax.operator}', + 'p' => 'color: {$syntax.punctuation}', + 'ss' => 'color: {$syntax.literal-string-symbol}', + 'm' => 'color: {$syntax.literal-number}', + 'mf' => 'color: {$syntax.literal-number-float}', + 'mh' => 'color: {$syntax.literal-number-hex}', + 'mi' => 'color: {$syntax.literal-number-integer}', + 'mo' => 'color: {$syntax.literal-number-octal}', + 'il' => 'color: {$syntax.literal-number-integer-long}', + 'gd' => 'color: {$syntax.generic-deleted}', + 'gr' => 'color: {$syntax.generic-red}', + 'gh' => 'color: {$syntax.generic-heading}', + 'gi' => 'color: {$syntax.generic-inserted}', + 'go' => 'color: {$syntax.generic-output}', + 'gp' => 'color: {$syntax.generic-prompt}', + 'gu' => 'color: {$syntax.generic-underline}', + 'gt' => 'color: {$syntax.generic-traceback}', + 'nd' => 'color: {$syntax.name-decorator}', + 'ni' => 'color: {$syntax.name-identifier}', + 'ne' => 'color: {$syntax.name-entity}', + 'nl' => 'color: {$syntax.name-label}', + 'nn' => 'color: {$syntax.name-namespace}', + 'ow' => 'color: {$syntax.operator-word}', + 'w' => 'color: {$syntax.text-whitespace}', + 'se' => 'color: {$syntax.literal-string-escape}', + 'si' => 'color: {$syntax.literal-string-interpol}', ); } diff --git a/webroot/rsrc/css/syntax/syntax-default.css b/webroot/rsrc/css/syntax/syntax-default.css index 687698b1e6..877edac6f8 100644 --- a/webroot/rsrc/css/syntax/syntax-default.css +++ b/webroot/rsrc/css/syntax/syntax-default.css @@ -3,148 +3,243 @@ * @generated */ -.remarkup-code .bp, -.remarkup-code .cp, -.remarkup-code .kp, -.remarkup-code .kr, -.remarkup-code .nb { - color: #304a96 +.remarkup-code .bp { + color: {$syntax.builtin-pseudo} +} + +.remarkup-code .c { + color: {$syntax.comment} +} + +.remarkup-code .c1 { + color: {$syntax.comment-single} } -.remarkup-code .c, -.remarkup-code .c1, .remarkup-code .cm, +.remarkup-code .dc { + color: {$syntax.comment-multiline} +} + +.remarkup-code .cp { + color: {$syntax.comment-preproc} +} + .remarkup-code .cs { - color: #74777d + color: {$syntax.comment-special} } .remarkup-code .gd { - color: #a00000 + color: {$syntax.generic-deleted} } -.remarkup-code .gh, -.remarkup-code .gp { - color: #000080 +.remarkup-code .gh { + color: {$syntax.generic-heading} } .remarkup-code .gi { - color: #00a000 + color: {$syntax.generic-inserted} } .remarkup-code .go { - color: #808080 + color: {$syntax.generic-output} +} + +.remarkup-code .gp { + color: {$syntax.generic-prompt} } .remarkup-code .gr { - color: #ff0000 + color: {$syntax.generic-red} } .remarkup-code .gt { - color: #0040d0 + color: {$syntax.generic-traceback} } .remarkup-code .gu { - color: #800080 + color: {$syntax.generic-underline} } .remarkup-code .hll { - color: #ffffcc + color: {$syntax.highlighted-line} } -.remarkup-code .il, -.remarkup-code .m, -.remarkup-code .mf, -.remarkup-code .mh, -.remarkup-code .mi, -.remarkup-code .mo { - color: #601200 +.remarkup-code .il { + color: {$syntax.literal-number-integer-long} +} + +.remarkup-code .k { + color: {$syntax.keyword} +} + +.remarkup-code .kc { + color: {$syntax.keyword-constant} +} + +.remarkup-code .kd { + color: {$syntax.keyword-declaration} +} + +.remarkup-code .kn { + color: {$syntax.keyword-namespace} +} + +.remarkup-code .kp { + color: {$syntax.keyword-preproc} +} + +.remarkup-code .kr { + color: {$syntax.keyword-reserved} } -.remarkup-code .k, -.remarkup-code .kd, -.remarkup-code .kn, .remarkup-code .kt { - color: #aa4000 + color: {$syntax.keyword-type} } -.remarkup-code .kc, -.remarkup-code .no { - color: #000a65 +.remarkup-code .m { + color: {$syntax.literal-number} +} + +.remarkup-code .mf { + color: {$syntax.literal-number-float} +} + +.remarkup-code .mh { + color: {$syntax.literal-number-hex} +} + +.remarkup-code .mi { + color: {$syntax.literal-number-integer} +} + +.remarkup-code .mo { + color: {$syntax.literal-number-octal} } .remarkup-code .na { - color: #354bb3 + color: {$syntax.name-attribute} } -.remarkup-code .nc, -.remarkup-code .nt, -.remarkup-code .vc { - color: #00702a +.remarkup-code .nb { + color: {$syntax.name-builtin} } -.remarkup-code .nd, -.remarkup-code .ow { - color: #aa22ff +.remarkup-code .nc { + color: {$syntax.name-class} +} + +.remarkup-code .nd { + color: {$syntax.name-decorator} } .remarkup-code .ne { - color: #d2413a + color: {$syntax.name-entity} } -.remarkup-code .nf, -.remarkup-code .nx { - color: #004012 +.remarkup-code .nf { + color: {$syntax.name-function} } .remarkup-code .ni { - color: #92969d + color: {$syntax.name-identifier} } .remarkup-code .nl { - color: #a0a000 + color: {$syntax.name-label} } .remarkup-code .nn { - color: #0000ff + color: {$syntax.name-namespace} } -.remarkup-code .nv, -.remarkup-code .vg, -.remarkup-code .vi { - color: #001294 +.remarkup-code .no { + color: {$syntax.name-operator} } -.remarkup-code .o, -.remarkup-code .ss { - color: #aa2211 +.remarkup-code .nt { + color: {$syntax.name-tag} } -.remarkup-code .s, -.remarkup-code .s1, -.remarkup-code .s2, -.remarkup-code .sb, -.remarkup-code .sc, -.remarkup-code .sx { - color: #766510 +.remarkup-code .nv { + color: {$syntax.name-variable} } -.remarkup-code .sd, -.remarkup-code .sh { - color: #000000 +.remarkup-code .nx { + color: {$syntax.name-exception} +} + +.remarkup-code .o { + color: {$syntax.operator} +} + +.remarkup-code .ow { + color: {$syntax.operator-word} +} + +.remarkup-code .p { + color: {$syntax.punctuation} +} + +.remarkup-code .s { + color: {$syntax.string} +} + +.remarkup-code .s1 { + color: {$syntax.string-single} +} + +.remarkup-code .s2 { + color: {$syntax.string-double} +} + +.remarkup-code .sb { + color: {$syntax.string-backtick} +} + +.remarkup-code .sc { + color: {$syntax.literal-string-char} +} + +.remarkup-code .sd { + color: {$syntax.string-doc} } .remarkup-code .se { - color: #bb6622 + color: {$syntax.literal-string-escape} +} + +.remarkup-code .sh { + color: {$syntax.string-heredoc} } .remarkup-code .si { - color: #bb66bb + color: {$syntax.literal-string-interpol} } .remarkup-code .sr { - color: #bb6688 + color: {$syntax.string-regex} +} + +.remarkup-code .ss { + color: {$syntax.literal-string-symbol} +} + +.remarkup-code .sx { + color: {$syntax.string-other} +} + +.remarkup-code .vc { + color: {$syntax.name-variable-class} +} + +.remarkup-code .vg { + color: {$syntax.variable-global} +} + +.remarkup-code .vi { + color: {$syntax.variable-instance} } .remarkup-code .w { - color: #bbbbbb + color: {$syntax.text-whitespace} } From 179f866deb74ca1dabbea4af00ca6c73a1965541 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 29 Feb 2024 16:22:08 +0100 Subject: [PATCH 403/425] Make lipsum project generator also fill in project description Summary: `./bin/lipsum` allows to fill up a Phorge instance with bogus data for testing. When creating a project, the code creates a project title, project status, random subscribers/members and picks a random project creator, but does not set a project description. Make this code also set a project description (which is implemented as a custom field). Closes T15748 Test Plan: * On the CLI, use `./bin/lipsum generate projects` to create a bogus project. * In the web browser, go to `/project/query/all/` and open the project page of the recently created bogus project: See no project description. * Apply this change. * On the CLI, use `./bin/lipsum generate projects` to create another bogus project. * In the web browser, go to `/project/query/all/` and open the project page of the recently created bogus project: See a random project description. Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15748 Differential Revision: https://we.phorge.it/D25545 --- .../project/lipsum/PhabricatorProjectTestDataGenerator.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php b/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php index e29ee13f2d..771b6a8169 100644 --- a/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php +++ b/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php @@ -19,6 +19,12 @@ public function generateObject() { PhabricatorProjectNameTransaction::TRANSACTIONTYPE, $this->newProjectTitle()); + $xactions[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD) + ->setOldValue('') + ->setMetadataValue('customfield:key', 'std:project:internal:description') + ->setNewValue($this->newProjectTitle()); + $xactions[] = $this->newTransaction( PhabricatorProjectStatusTransaction::TRANSACTIONTYPE, $this->newProjectStatus()); From 10a3f4fa192deb08994479cea4eff97bfe02f32d Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Tue, 29 Aug 2023 14:25:33 -0400 Subject: [PATCH 404/425] Use predictable filenames when downloading raw diffs from a revision Summary: To prevent spammers from abusing this feature on a public server, do not include query parameters in the generated filenames. See . Ref T15665. Test Plan: Download raw diff from a revision and check filename in URL. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15665 Differential Revision: https://we.phorge.it/D25478 Signed-off-by: Zero King --- .../DifferentialRevisionViewController.php | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 102c7f560b..ff5cd7373f 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -1090,20 +1090,13 @@ private function buildRawDiffResponse( $request_uri = $this->getRequest()->getRequestURI(); - // this ends up being something like - // D123.diff - // or the verbose - // D123.vs123.id123.highlightjs.diff - // lame but nice to include these options - $file_name = ltrim($request_uri->getPath(), '/').'.'; - foreach ($request_uri->getQueryParamsAsPairList() as $pair) { - list($key, $value) = $pair; - if ($key == 'download') { - continue; - } - $file_name .= $key.$value.'.'; - } - $file_name .= 'diff'; + // Filename ends up being something like D123.1692295858.diff + // This discards some options in the query string that may affect the diff + // response, but is intentional to avoid spammy titles from bot requests. + $timestamp = + PhabricatorTime::getNow() + + phutil_units('24 hours in seconds'); + $file_name = ltrim($request_uri->getPath(), '/').'.'.$timestamp.'.diff'; $iterator = new ArrayIterator(array($raw_diff)); From faf43d7edf7b2da743cc900e4ecdca10f64a973f Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Thu, 7 Mar 2024 11:04:15 +0100 Subject: [PATCH 405/425] Celerity map late update Summary: This change is probably needed after 892f527b9f6330a1131b564d2aef0b3f8e8aafa5. At that time, the unit test was successfully completed probably because of a lack of git rebase master. Test Plan: The unit test PhabricatorCelerityTestCase::testCelerityMaps is not screaming. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25553 --- resources/celerity/map.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 0a99d6f39e..300a2a59db 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,8 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '7f47b2ac', - 'core.pkg.css' => '41cc201d', + 'core.pkg.css' => 'cf2d74fd', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '6d3700f0', From 8fe3d68577b9d4bc0bab2ddf4f6109ff06c9fedc Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 15 Mar 2024 13:50:13 +0100 Subject: [PATCH 406/425] Avoid "Action with no effect" for auto-claim statuses after manually removing assignee Summary: Sometime you just want to flag something as Resolved and keep that task claimed by nobody. But, there are some task statuses that can auto-claim, and "Resolved" is one of these. So, if you "Resolved", Phorge tries to set yourself as claimer. Keeping that "claimed by nobody" is a bit tricky and also generates a confusing warning. In fact, after you "Resolved", you can override the defaults with: - Add Action > Assign / Claim > (nobody) The problem is, on saving, the above action causes this warning: > **Action With No Effect** > One of your actions has no effect: > The task already has the selected owner. > Apply remaining actions? > [ Cancel ] [ Apply Remaining Actions ] That warning "The task already has the selected owner" really means "The task is already claimed by nobody" and, indeed, that is exactly what the user wants. This patch intercepts the above action, and prevents the related confusing "non-effect" warning. Thanks to hard troubleshooting from user https://we.phorge.it/p/aklapper/ See also https://we.phorge.it/D25476 Closes T15164 Test Plan: Task 1 open unassigned: 1. Change Status to Resolved 2. Preview yourself as Claimer 3. Add Action > Assign / Claim, and **set ** 4. Save and, instead of any confusing warning, only the Status changes. Task 2 open unassigned: 1. Change Status to Resolved 2. Preview yourself as Claimer 3. Add Action > Assign / Claim, keep it as-is 4. Save and, it works as expected (just like before) Task 3 open unassigned: 1. Change Status to Resolved 2. Preview yourself as Claimer 3. Add Action > Assign / Claim, set to somebody else 4. Save and, it works as expected (just like before) Reviewers: O1 Blessed Committers, aklapper Reviewed By: O1 Blessed Committers, aklapper Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15164 Differential Revision: https://we.phorge.it/D25550 --- .../editor/ManiphestTransactionEditor.php | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index a6a10b33ed..fc2251e806 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -327,11 +327,11 @@ protected function expandTransactions( $is_unassigned = ($object->getOwnerPHID() === null); - $any_assign = false; + $any_xassign = null; foreach ($xactions as $xaction) { if ($xaction->getTransactionType() == ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) { - $any_assign = true; + $any_xassign = $xaction; break; } } @@ -355,15 +355,22 @@ protected function expandTransactions( // If the task is not assigned, not being assigned, currently open, and // being closed, try to assign the actor as the owner. - if ($is_unassigned && !$any_assign && $is_open && $is_closing) { - $is_claim = ManiphestTaskStatus::isClaimStatus($new_status); - - // Don't assign the actor if they aren't a real user. - // Don't claim the task if the status is configured to not claim. - if ($actor_phid && $is_claim) { - $results[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) - ->setNewValue($actor_phid); + // Don't assign the actor if they aren't a real user. + if ($is_unassigned && $is_open && $is_closing && $actor_phid) { + $is_autoclaim = ManiphestTaskStatus::isClaimStatus($new_status); + if ($is_autoclaim) { + if ($any_xassign === null) { + $results[] = id(new ManiphestTransaction()) + ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) + ->setNewValue($actor_phid); + } else if ($any_xassign->getNewValue() === null) { + // We have an explicit "Assign / Claim" = nothing in the frontend. + // The user is trying to "undo" the above automatic auto-claim. + // When saving, this would cause the "no effect" warning. + // So we suppress that confusing warning. + // https://we.phorge.it/T15164 + $any_xassign->setIgnoreOnNoEffect(true); + } } } From 328aee033fbdc704620e2facae4aa68b836217bb Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 19 Mar 2024 17:34:07 +0100 Subject: [PATCH 407/425] isSelfURI: fix anchors and relative URIs (that are "self" indeed) Summary: Example cases that were wrongly considered external URLs: - "#foo" - "/foo" Note that Phorge usually does not change stable things. In fact this thing was not documented even inline, and was not even in the unit tests. So this is a fix, and not a breaking change. Probably no one had ever tried it. Closes T15182 Test Plan: The already existing unit tests still work. The added ones make sense. There are no problems doing other random things. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15182 Differential Revision: https://we.phorge.it/D25555 --- src/infrastructure/env/PhabricatorEnv.php | 2 +- src/infrastructure/env/__tests__/PhabricatorEnvTestCase.php | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index 617fb57af9..a471874e43 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -433,7 +433,7 @@ public static function isSelfURI($raw_uri) { $host = $uri->getDomain(); if (!phutil_nonempty_string($host)) { - return false; + return true; } $host = phutil_utf8_strtolower($host); diff --git a/src/infrastructure/env/__tests__/PhabricatorEnvTestCase.php b/src/infrastructure/env/__tests__/PhabricatorEnvTestCase.php index f73299aa12..59e925f15c 100644 --- a/src/infrastructure/env/__tests__/PhabricatorEnvTestCase.php +++ b/src/infrastructure/env/__tests__/PhabricatorEnvTestCase.php @@ -243,6 +243,12 @@ public function testSelfURI() { 'https://old.example.com/path/to/resource.png' => true, 'https://other.example.com/' => false, + + '/' => true, + '/self' => true, + '#self' => true, + '/#self' => true, + '/self/#self' => true, ); foreach ($map as $input => $expect) { From d42b3eb0b17a55b6691e7b064552bc317f367fb8 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 4 Jan 2024 10:16:15 +0100 Subject: [PATCH 408/425] Fix "Map omits required key" exception by comparing result order against modern field keys Summary: Sorting a Maniphest search query by custom fields throws a "Map returned omits required key" exception. The `isCustomFieldOrderKey()` check still tested against legacy field key format (for example `[custom:]std:maniphest:deadline.due`) while the code passes modern field key format (for example `custom.deadline.due`). After fixing this, `PhutilTypeSpec::checkMap()` throws an exception when a non-optional (extra) key `$column` is in `$columns` but not in the array of type parameters below to check against: `"Got unexpected parameters: customfield, customfield.index.table, customfield.index.key"` Thus add optional types for customfields in `buildPagingClauseFromMultipleColumns()` to allow them instead of throwing another exception. Closes T15631 Test Plan: * Have a custom field (with search: true) defined via http://phorge.localhost/config/edit/maniphest.custom-field-definitions/ * Have two tasks have the custom field set * Go to http://phorge.localhost/maniphest/query/ , select the custom field (e.g. "Due Date (Reversed)") in the "Order By" field, and click the "Search" button. Iterate through the results via the "Next" button if needed Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15631 Differential Revision: https://we.phorge.it/D25504 --- .../policy/PhabricatorCursorPagedPolicyAwareQuery.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 6ad91b52ea..cdd941a5f4 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -735,6 +735,9 @@ final protected function buildPagingClauseFromMultipleColumns( array( 'table' => 'optional string|null', 'column' => 'string', + 'customfield' => 'optional bool', + 'customfield.index.key' => 'optional string', + 'customfield.index.table' => 'optional string', 'value' => 'wild', 'type' => 'string', 'reverse' => 'optional bool', @@ -1747,7 +1750,7 @@ protected function getPagingValueMapForCustomFields( $map = array(); foreach ($fields->getFields() as $field) { - $map['custom:'.$field->getFieldKey()] = $field->getValueForStorage(); + $map[$field->getModernFieldKey()] = $field->getValueForStorage(); } return $map; @@ -1758,7 +1761,7 @@ protected function getPagingValueMapForCustomFields( * @task customfield */ protected function isCustomFieldOrderKey($key) { - $prefix = 'custom:'; + $prefix = 'custom.'; return !strncmp($key, $prefix, strlen($prefix)); } From 9ebe85621919390a3e4daa56706993f203e02b2e Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Mon, 25 Mar 2024 09:27:53 +0100 Subject: [PATCH 409/425] Update unit test of InterpreterBlockRule for "a {{{a}}}" Summary: This is supposed to render in plain text instead of crashing or giving an error, after D25415: a {{{a}}} The related unit test is updated accordingly. We have unnoticed this, because of the well-known bug T15500. Ref T15372 See D25415 Test Plan: This makes sense with the introduced functionality in D25415. Unit tests are happy again. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15372 Differential Revision: https://we.phorge.it/D25559 --- .../remarkup/__tests__/remarkup/interpreter-test.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/interpreter-test.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/interpreter-test.txt index 477f3eeea0..edbd49208c 100644 --- a/src/infrastructure/markup/remarkup/__tests__/remarkup/interpreter-test.txt +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/interpreter-test.txt @@ -30,9 +30,7 @@ Argv: () Content: (content) Argv: (x=y) - - -
No interpreter found: phutil_fake_test_block_interpreter
+

phutil_fake_test_block_interpreter {{{ content }}}

~~~~~~~~~~ Content: (content) Argv: (foo=bar) @@ -53,6 +51,4 @@ Argv: () Content: (content) Argv: (x=y) - - -(No interpreter found: phutil_fake_test_block_interpreter) +phutil_fake_test_block_interpreter {{{ content }}} From 27f4b8321a5e39bdf6f869192d817c94aa2c7515 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 26 Mar 2024 07:29:53 +0100 Subject: [PATCH 410/425] Fix important regression in search engine Summary: Interestingly Phorge allows to paste a Phorge URL in the search bar to be redirected there... Now this interesting feature was crashing the whole search engine for simple queries like "asdasd" with the following exception message: Refusing to redirect to local resource "asdasd". This URI is not formatted in a recognizable way. You are affected by this crash after this commit: 328aee033fbdc704620e2facae4aa68b836217bb This specific commit is probably legitimate since "#asdasd" and "/asdasd" are indeed internal URIs, plus, yes, there are some undocumented and untested cases like "asdasd" that may be considered internal URIs as well. Or not. But this is just an undocumented corner-case. In short: PhabricatorDatasourceURIEngineExtension should not be built over undocumented corner-cases and should require an absolute URI, with at least a domain. Note that PhutilURI#getDomain() is always a string and never null, so we can do a strict check. This fix also involves a micro-optimization to avoid duplicate URI parsing. Since the method was calling the constructor PhutilURI(string) twice (line 13, line 24). Now just once. Closes T15763 Test Plan: Search "lol", no crash anymore. Copy a random Phorge URL and paste that in your search bar. It still redirects there. Reviewers: zhe, avivey, O1 Blessed Committers, speck Reviewed By: O1 Blessed Committers, speck Subscribers: speck, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15763 Differential Revision: https://we.phorge.it/D25561 --- ...habricatorDatasourceURIEngineExtension.php | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/applications/meta/engineextension/PhabricatorDatasourceURIEngineExtension.php b/src/applications/meta/engineextension/PhabricatorDatasourceURIEngineExtension.php index 16d8dd9315..76d60af1e6 100644 --- a/src/applications/meta/engineextension/PhabricatorDatasourceURIEngineExtension.php +++ b/src/applications/meta/engineextension/PhabricatorDatasourceURIEngineExtension.php @@ -10,35 +10,37 @@ public function newQuickSearchDatasources() { public function newJumpURI($query) { // If you search for a URI on the local install, just redirect to that // URI as though you had pasted it into the URI bar. - if (PhabricatorEnv::isSelfURI($query)) { - // Strip off the absolute part of the URI. If we don't, the URI redirect - // validator will get upset that we're performing an unmarked external - // redirect. - - // The correct host and protocol may also differ from the host and - // protocol used in the search: for example, if you search for "http://" - // we want to redirect to "https://" if an install is HTTPS, and - // the "isSelfURI()" check includes alternate domains in addition to the - // canonical domain. - - $uri = id(new PhutilURI($query)) - ->setDomain(null) - ->setProtocol(null) - ->setPort(null); - - $uri = phutil_string_cast($uri); - - // See T13412. If the URI was in the form "http://dev.example.com" with - // no trailing slash, there may be no path. Redirecting to the empty - // string is considered an error by safety checks during redirection, - // so treat this like the user entered the URI with a trailing slash. - if (!strlen($uri)) { - $uri = '/'; - } - - return $uri; + // Skip things that are really not full URLs, like "asdasd". + // Note that the backend of "isSelfURI" is faster with a PhutilURI. + $uri = new PhutilURI($query); + if ($uri->getDomain() === '' || !PhabricatorEnv::isSelfURI($uri)) { + return null; } - return null; + // Strip off the absolute part of the URI. If we don't, the URI redirect + // validator will get upset that we're performing an unmarked external + // redirect. + + // The correct host and protocol may also differ from the host and + // protocol used in the search: for example, if you search for "http://" + // we want to redirect to "https://" if an install is HTTPS, and + // the "isSelfURI()" check includes alternate domains in addition to the + // canonical domain. + $uri = $uri + ->setDomain(null) + ->setProtocol(null) + ->setPort(null); + + $uri = phutil_string_cast($uri); + + // See T13412. If the URI was in the form "http://dev.example.com" with + // no trailing slash, there may be no path. Redirecting to the empty + // string is considered an error by safety checks during redirection, + // so treat this like the user entered the URI with a trailing slash. + if (!strlen($uri)) { + $uri = '/'; + } + + return $uri; } } From b1ae1b1a678982e5ba12cb89ea592e9c19cb4cf0 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Tue, 26 Mar 2024 11:19:53 +0100 Subject: [PATCH 411/425] Remarkup code blocks: guess language from "name=" Summary: The file name is a sufficient source of information, if available. Closes T15729 Test Plan: The new unit test is green. Old unit tests are green. Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15729 Differential Revision: https://we.phorge.it/D25560 --- .../blockrule/PhutilRemarkupCodeBlockRule.php | 19 +++++++++++++++++++ .../remarkup/code-block-guess-from-name.txt | 7 +++++++ 2 files changed, 26 insertions(+) create mode 100644 src/infrastructure/markup/remarkup/__tests__/remarkup/code-block-guess-from-name.txt diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php index 8b23fe6541..8763111dd1 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php @@ -153,6 +153,11 @@ public function markupText($text, $children) { return implode("\n", $out); } + // The name is usually a sufficient source of information for file ext. + if (empty($options['lang']) && isset($options['name'])) { + $options['lang'] = $this->guessFilenameExtension($options['name']); + } + if (empty($options['lang'])) { // If the user hasn't specified "lang=..." explicitly, try to guess the // language. If we fail, fall back to configured defaults. @@ -343,4 +348,18 @@ private static function knownLanguageCodes() { return $map; } + /** + * Get the extension from a filename. + * @param string "/path/to/something.name" + * @return null|string ".name" + */ + private function guessFilenameExtension($name) { + $name = basename($name); + $pos = strrpos($name, '.'); + if ($pos !== false) { + return substr($name, $pos + 1); + } + return null; + } + } diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/code-block-guess-from-name.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/code-block-guess-from-name.txt new file mode 100644 index 0000000000..5a2c6a7c50 --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/code-block-guess-from-name.txt @@ -0,0 +1,7 @@ + name=/etc/phpmyadmin/config.txt + $lol = 1; +~~~~~~~~~~ +
/etc/phpmyadmin/config.txt
$lol = 1;
+~~~~~~~~~~ +name=/etc/phpmyadmin/config.txt + $lol = 1; From 216d308507edd54a68cb392df9f47011265c64e9 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Tue, 26 Mar 2024 20:07:14 +0200 Subject: [PATCH 412/425] Fix Project Hovercard rendering @mention Summary: Fix T15762. There's a complex interaction with project's AutoCapability requiring the membership info to be already loaded, and the Mention rule assuming any project is fully loaded before trying to check permissions. As far as I can tell, the "contextObject" is currently used only to make the mention handle gray if the mentioned user can't see it. Test Plan: Load a Project's hovercard that @mentions a user that can't see the project - no crash. Reviewers: aklapper, O1 Blessed Committers Reviewed By: aklapper, O1 Blessed Committers Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15762 Differential Revision: https://we.phorge.it/D25562 --- src/applications/project/view/PhabricatorProjectCardView.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/applications/project/view/PhabricatorProjectCardView.php b/src/applications/project/view/PhabricatorProjectCardView.php index 03d342b666..01a32c1208 100644 --- a/src/applications/project/view/PhabricatorProjectCardView.php +++ b/src/applications/project/view/PhabricatorProjectCardView.php @@ -80,8 +80,7 @@ protected function getTagContent() { $description = $description_field->getFieldValue(); if (phutil_nonempty_string($description)) { $description = PhabricatorMarkupEngine::summarizeSentence($description); - $description = id(new PHUIRemarkupView($viewer, $description)) - ->setContextObject($project); + $description = id(new PHUIRemarkupView($viewer, $description)); $description = phutil_tag( 'div', From 48e121c485a260c35ae924c9f225f44c98821f3b Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Fri, 29 Mar 2024 08:26:11 +0100 Subject: [PATCH 413/425] Config Projects Extra Fields: link to the doc (like User) Summary: Add a documentation link in a specific Config page, that is: Config > Setting > projects.custom-field-definitions Comparison example: | User | People | |-----------|------------| |{F1633919} | {F1633920} | This change is a boring follow-up of this one: https://we.phorge.it/D25507 Test Plan: Visit the page and click on the new link: - /config/edit/projects.custom-field-definitions/ Visit this page that was the inspirational page: - /config/edit/user.custom-field-definitions/ Check that no nuclear implosion is raised. Reviewers: O1 Blessed Committers, aklapper Reviewed By: O1 Blessed Committers, aklapper Subscribers: aklapper, tobiaswiese, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25552 --- .../config/PhabricatorProjectConfigOptions.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/applications/project/config/PhabricatorProjectConfigOptions.php b/src/applications/project/config/PhabricatorProjectConfigOptions.php index 2d87bf159f..4767a4dccc 100644 --- a/src/applications/project/config/PhabricatorProjectConfigOptions.php +++ b/src/applications/project/config/PhabricatorProjectConfigOptions.php @@ -105,6 +105,16 @@ public function getOptions() { ), ); + $fields_description = $this->deformat(pht(<<deformat(pht(<<newOption('projects.custom-field-definitions', 'wild', array()) ->setSummary(pht('Custom Projects fields.')) - ->setDescription( - pht( - 'Array of custom fields for Projects.')) + ->setDescription($fields_description) ->addExample( '{"mycompany:motto": {"name": "Project Motto", '. '"type": "text"}}', From 5263d5d590302d5b3cad72cf919bf6a3ea068fe9 Mon Sep 17 00:00:00 2001 From: Valerio Bozzolan Date: Sat, 30 Mar 2024 12:14:08 +0100 Subject: [PATCH 414/425] Custom integer fields: fix search by array of possible values Summary: This minimal changes seems the natural expansion of this search function, that "seems" designed to only search with a single value, but the backend is designed to receive an array of possible values. Look at the same method in PhabricatorStandardCustomFieldLink that already works also for array inputs. To get extra confidence look at the method withApplicationSearchContainsConstraint() that works perfectly with arrays (to be honest it only works with arrays in mind...) This feature allows to avoid crashes when extension developers tries to run "IN" queries because for example they try to follow our performance guidelines: https://we.phorge.it/book/contrib/article/n_plus_one/ Closes T15752 Test Plan: Have a nice custom integer field, like this one for Maniphest: { "mycompany.estimated-hours": { "name": "Estimated Hours", "type": "int", "caption": "Estimated number of hours this will take.", "search": true } } You can reproduce T15752 before the change. It just works after the change. Or, just trust your instincts by looking at the same method in PhabricatorStandardCustomFieldLink. Also, use the "normal" frontend search page. Still works as usual. Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T15752 Differential Revision: https://we.phorge.it/D25554 --- .../PhabricatorStandardCustomFieldInt.php | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php index 3ee42a531c..f56f958a62 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php @@ -48,15 +48,28 @@ public function readApplicationSearchValueFromRequest( return $request->getStr($this->getFieldKey()); } + /** + * Apply an application search constraint to a query. + * If you have a field of type integer, and a value (or an array of values), + * the result set will be limited to the rows with these values. + * @param PhabricatorApplicationSearchEngine $engine + * @param PhabricatorCursorPagedPolicyAwareQuery $query + * @param mixed $value Single value or array of values (IN query). + */ public function applyApplicationSearchConstraintToQuery( PhabricatorApplicationSearchEngine $engine, PhabricatorCursorPagedPolicyAwareQuery $query, $value) { - if (phutil_nonempty_scalar($value)) { - $query->withApplicationSearchContainsConstraint( - $this->newNumericIndex(null), - $value); + // The basic use case is with a single value. + // The backend really works with an array. So also array allowed. + if (is_array($value) || phutil_nonempty_scalar($value)) { + $value = (array)$value; + if ($value) { + $query->withApplicationSearchContainsConstraint( + $this->newNumericIndex(null), + $value); + } } } From 186768ccfd8895af80137bc3c6f41281ed744cfc Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 31 Mar 2024 13:54:49 +0200 Subject: [PATCH 415/425] Fix PHP 8.1 "strlen(null)" exception when received email lacks From header Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` ERROR 8192: strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/var/www/html/phorge/phorge/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php:527] ``` Closes T15767 Test Plan: See T15767 Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15767 Differential Revision: https://we.phorge.it/D25564 --- .../metamta/storage/PhabricatorMetaMTAReceivedMail.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php index d3289bbc69..04b5d9dc22 100644 --- a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php @@ -524,7 +524,7 @@ public function newContentSource() { public function newFromAddress() { $raw_from = $this->getHeader('From'); - if (strlen($raw_from)) { + if (phutil_nonempty_string($raw_from)) { return new PhutilEmailAddress($raw_from); } From a69db10c5e15be4b7d851099b48c30f7cfe11046 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 31 Mar 2024 10:32:23 +0200 Subject: [PATCH 416/425] Fix "Creation of dynamic property is deprecated" PHP 8.2 error in MimeMailParser Summary: Add `#[AllowDynamicProperties]` attribute to avoid the deprecation notice. See https://php.watch/versions/8.2/dynamic-properties-deprecated ``` # ERROR 8192: Creation of dynamic property MimeMailParser::$parts is deprecated at [/var/www/html/phorge/phorge/externals/mimemailparser/MimeMailParser.class.php:137] # #0 MimeMailParser::parse() called at [/externals/mimemailparser/MimeMailParser.class.php:126] ``` Closes T15765 Test Plan: See T15765 Reviewers: O1 Blessed Committers, avivey Reviewed By: O1 Blessed Committers, avivey Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15765 Differential Revision: https://we.phorge.it/D25563 --- externals/mimemailparser/MimeMailParser.class.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/externals/mimemailparser/MimeMailParser.class.php b/externals/mimemailparser/MimeMailParser.class.php index 914f50888e..40ab1640f5 100644 --- a/externals/mimemailparser/MimeMailParser.class.php +++ b/externals/mimemailparser/MimeMailParser.class.php @@ -31,6 +31,11 @@ class MimeMailParser { */ public $attachment_streams; + /** + * Parts of an email + */ + private $parts = array(); + /** * Inialize some stuff * @return From 713188bf1710fdd1328d58b394ea5ccba52dfb3c Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Thu, 21 Mar 2024 18:35:40 +0100 Subject: [PATCH 417/425] Fix PHP 8.1 "strlen(null)" exception querying project tags for autocomplete without additional hashtag slugs defined Summary: `strlen()` was used in Phabricator to check if a generic value is a non-empty string. This behavior is deprecated since PHP 8.1. Phorge adopts `phutil_nonempty_string()` as a replacement. Note: this may highlight other absurd input values that might be worth correcting instead of just ignoring. If phutil_nonempty_string() throws an exception in your instance, report it to Phorge to evaluate and fix that specific corner case. ``` ERROR 8192: strlen(): Passing null to parameter #1 ($string) of type string is deprecated at [/var/www/html/phorge/phorge/src/applications/project/typeahead/PhabricatorProjectDatasource.php:99] ``` Closes T15761 Test Plan: Go to a task, enter `#` and start typing a project tag of a project which has no additional slugs defined. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15761 Differential Revision: https://we.phorge.it/D25556 --- .../project/typeahead/PhabricatorProjectDatasource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/project/typeahead/PhabricatorProjectDatasource.php b/src/applications/project/typeahead/PhabricatorProjectDatasource.php index acb6999018..ea2ba48b09 100644 --- a/src/applications/project/typeahead/PhabricatorProjectDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectDatasource.php @@ -96,7 +96,7 @@ public function loadResults() { // If we're building results for the autocompleter and this project // doesn't have any usable slugs, don't return it as a result. - if ($for_autocomplete && !strlen($slug)) { + if ($for_autocomplete && !phutil_nonempty_string($slug)) { continue; } From ba835fe0eced9c36b3793654d77b4289d59cf228 Mon Sep 17 00:00:00 2001 From: Benjamin Kausch Date: Tue, 2 Apr 2024 11:55:29 +0200 Subject: [PATCH 418/425] Seperate repository home and browse view in tab menu Summary: The tab menu in a repository can be a bit confusing. The first tab will be rendered active for two distinctive states: - the home view in the currently selected branch - the browse view in the currently selected branch This diff will give the home view a dedicated extra tab. When switching the branches, we won't switch between home and browse view like it is done now. And I have changed the crumb text for the browse view to "Code" to match it up with the tab name. Additionally the readme is now the first content on the home view. Fixes T15728 Test Plan: Browse in your favorite repos and feel an UX elevation. Reviewers: O1 Blessed Committers, avivey, valerio.bozzolan Reviewed By: O1 Blessed Committers, avivey, valerio.bozzolan Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15728 Differential Revision: https://we.phorge.it/D25538 --- .../diffusion/controller/DiffusionController.php | 14 +++++++++++++- .../controller/DiffusionRepositoryController.php | 12 ++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index dc4c8e8699..1f77f28d77 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -211,7 +211,7 @@ private function buildCrumbList(array $spec = array()) { $view_name = pht('History'); break; case 'browse': - $view_name = pht('Browse'); + $view_name = pht('Code'); break; case 'lint': $view_name = pht('Lint'); @@ -502,6 +502,18 @@ protected function buildTabsView($key) { $view = new PHUIListView(); + $view->addMenuItem( + id(new PHUIListItemView()) + ->setKey('home') + ->setName(pht('Home')) + ->setIcon('fa-home') + ->setHref($drequest->generateURI( + array( + 'action' => 'branch', + 'path' => '', + ))) + ->setSelected($key == 'home')); + $view->addMenuItem( id(new PHUIListItemView()) ->setKey('code') diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index d111d52c4e..7a523d799a 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -109,7 +109,7 @@ public function handleRequest(AphrontRequest $request) { ->setErrors(array($empty_message)); } - $tabs = $this->buildTabsView('code'); + $tabs = $this->buildTabsView('home'); $clone_uri = $drequest->generateURI( array( @@ -243,16 +243,16 @@ private function buildNormalContent(DiffusionRequest $drequest) { $readme = null; } + if ($readme) { + $content[] = $readme; + } + $content[] = $this->buildBrowseTable( $browse_results, $browse_paths, $browse_exception, $browse_pager); - if ($readme) { - $content[] = $readme; - } - try { $branch_button = $this->buildBranchList($drequest); $this->branchButton = $branch_button; @@ -396,7 +396,7 @@ private function buildBranchList(DiffusionRequest $drequest) { foreach ($branches as $branch) { $branch_uri = $drequest->generateURI( array( - 'action' => 'browse', + 'action' => 'branch', 'branch' => $branch->getShortname(), )); $actions->addAction( From d4af32c1d4120a5804e3d8c4b9d9c37476aa272e Mon Sep 17 00:00:00 2001 From: Katie Date: Mon, 1 Apr 2024 20:39:23 +0300 Subject: [PATCH 419/425] Fix setup check for gzip request mangling being sent as GET Summary: Certain web servers (or cache layers such as Varnish) strip the body of GET requests, resulting in the setup check failing when it would otherwise function normally if a request is sent as POST. Test Plan: Phorge will no-longer mark the test as failing due to the body of a GET request being dropped. As per RFC7231, certain implementations of HTTP/1.1 may reject the request or drop the payload due to a previous revision of the spec where the body was supposed to be ignored (RFC2616, section 4.3) Reviewers: O1 Blessed Committers, 20after4, speck, avivey Reviewed By: O1 Blessed Committers, 20after4, speck, avivey Subscribers: speck, 20after4, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25566 --- src/applications/config/check/PhabricatorWebServerSetupCheck.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/applications/config/check/PhabricatorWebServerSetupCheck.php b/src/applications/config/check/PhabricatorWebServerSetupCheck.php index d46f808f58..1bbe3a3f2c 100644 --- a/src/applications/config/check/PhabricatorWebServerSetupCheck.php +++ b/src/applications/config/check/PhabricatorWebServerSetupCheck.php @@ -58,6 +58,7 @@ protected function executeChecks() { $gzip_future = id(new HTTPSFuture($base_uri)) ->addHeader('X-Setup-SelfCheck', 1) ->addHeader('Content-Encoding', 'gzip') + ->setMethod('POST') ->setTimeout(5) ->setData($gzip_compressed); From c0bc453405841b2f1f37a344792a4172cc2708b4 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Tue, 2 Apr 2024 22:30:41 +0200 Subject: [PATCH 420/425] Fix PHP 8.1 "preg_match(null)" exception exporting task list to CSV Summary: When a column value to export to CSV is empty, `null` is passed to `preg_match()` which is deprecated behavior since PHP 8.1. Thus only call `preg_match()` when the value is set. ``` ERROR 8192: preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated at [/var/www/html/phorge/phorge/src/infrastructure/export/format/PhabricatorCSVExportFormat.php:51] ``` ``` ERROR 8192: preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated at [/var/www/html/phorge/phorge/src/infrastructure/export/format/PhabricatorCSVExportFormat.php:55] ``` Closes T15770 Test Plan: Export a Maniphest task list of query results to CSV. Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15770 Differential Revision: https://we.phorge.it/D25567 --- .../export/format/PhabricatorCSVExportFormat.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/export/format/PhabricatorCSVExportFormat.php b/src/infrastructure/export/format/PhabricatorCSVExportFormat.php index 8f3879d5fb..f9d8bf1ad3 100644 --- a/src/infrastructure/export/format/PhabricatorCSVExportFormat.php +++ b/src/infrastructure/export/format/PhabricatorCSVExportFormat.php @@ -48,11 +48,11 @@ private function addRow(array $values) { // like it might be too tempting for Excel to ignore, mangle the value // to dissuade remote code execution. See T12800. - if (preg_match('/^\s*[+=@-]/', $value)) { + if ($value && preg_match('/^\s*[+=@-]/', $value)) { $value = '(!) '.$value; } - if (preg_match('/\s|,|\"/', $value)) { + if ($value && preg_match('/\s|,|\"/', $value)) { $value = str_replace('"', '""', $value); $value = '"'.$value.'"'; } From 644f179dd27a78b91c88366b9432f5faaeb2bbb4 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 1 Apr 2024 12:32:50 +0200 Subject: [PATCH 421/425] Make incoming mail handling more robust / correct Summary: * Properly handle when no mail headers at all can be parsed * Properly handle when mail headers can be parsed but no subject line can be found ``` EXCEPTION: (RuntimeException) Undefined index: subject ``` Closes T15769 Test Plan: See T15769 Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: 20after4, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15769 Differential Revision: https://we.phorge.it/D25565 --- externals/mimemailparser/MimeMailParser.class.php | 4 ++-- scripts/mail/mail_handler.php | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/externals/mimemailparser/MimeMailParser.class.php b/externals/mimemailparser/MimeMailParser.class.php index 40ab1640f5..dfd7a2fa11 100644 --- a/externals/mimemailparser/MimeMailParser.class.php +++ b/externals/mimemailparser/MimeMailParser.class.php @@ -308,10 +308,10 @@ public function getAttachments() { * @param $part Array */ private function getPartHeaders($part) { - if (isset($part['headers'])) { + if (isset($part['headers']) && $part['headers']) { return $part['headers']; } - return false; + throw new Exception('MimeMailParser::getHeaders() could not parse any email headers.'); } /** diff --git a/scripts/mail/mail_handler.php b/scripts/mail/mail_handler.php index bf6f315f3a..5631310a2b 100755 --- a/scripts/mail/mail_handler.php +++ b/scripts/mail/mail_handler.php @@ -55,7 +55,9 @@ } $headers = $parser->getHeaders(); -$headers['subject'] = phutil_decode_mime_header($headers['subject']); +if (array_key_exists('subject', $headers)) { + $headers['subject'] = phutil_decode_mime_header($headers['subject']); +} $headers['from'] = phutil_decode_mime_header($headers['from']); if ($args->getArg('process-duplicates')) { From ecbef3fdf4b685802ee2a8c6c1ad8b28d7f6756f Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Fri, 5 Apr 2024 10:28:42 +0300 Subject: [PATCH 422/425] Show rest of task page when graph errors out Summary: T15772. Test Plan: Add a `throw` statement in graph calculation, load a task that has a graph, see message. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25570 --- .../ManiphestTaskDetailController.php | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php index 8916ad26cf..af23126f9b 100644 --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -81,7 +81,7 @@ public function handleRequest(AphrontRequest $request) { $graph_menu = null; $graph_limit = 200; - $overflow_message = null; + $graph_error_message = null; $task_graph = id(new ManiphestTaskGraph()) ->setViewer($viewer) ->setSeedPHID($task->getPHID()) @@ -102,8 +102,9 @@ public function handleRequest(AphrontRequest $request) { // the search button to browse tasks with the search UI instead. $direct_count = count($parent_list) + count($subtask_list); + $graph_table = null; if ($direct_count > $graph_limit) { - $overflow_message = pht( + $graph_error_message = pht( 'This task is directly connected to more than %s other tasks. '. 'Use %s to browse parents or subtasks, or %s to show more of the '. 'graph.', @@ -111,14 +112,13 @@ public function handleRequest(AphrontRequest $request) { phutil_tag('strong', array(), pht('Search...')), phutil_tag('strong', array(), pht('View Standalone Graph'))); - $graph_table = null; } else { // If there aren't too many direct tasks, but there are too many total // tasks, we'll only render directly connected tasks. if ($task_graph->isOverLimit()) { $task_graph->setRenderOnlyAdjacentNodes(true); - $overflow_message = pht( + $graph_error_message = pht( 'This task is connected to more than %s other tasks. '. 'Only direct parents and subtasks are shown here. Use '. '%s to show more of the graph.', @@ -126,13 +126,22 @@ public function handleRequest(AphrontRequest $request) { phutil_tag('strong', array(), pht('View Standalone Graph'))); } - $graph_table = $task_graph->newGraphTable(); + try { + $graph_table = $task_graph->newGraphTable(); + } catch (Throwable $ex) { + phlog($ex); + $graph_error_message = pht( + 'There was an unexpected error displaying the task graph. '. + 'Use %s to browse parents or subtasks, or %s to show the graph.', + phutil_tag('strong', array(), pht('Search...')), + phutil_tag('strong', array(), pht('View Standalone Graph'))); + } } - if ($overflow_message) { + if ($graph_error_message) { $overflow_view = $this->newTaskGraphOverflowView( $task, - $overflow_message, + $graph_error_message, true); $graph_table = array( From 5a5c456264af6e56fdb99167d7c76b4f52a5ee99 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 7 Apr 2024 21:59:25 +0200 Subject: [PATCH 423/425] Correct docs of PhabricatorPHIDType::getAllTypesForApplication() Summary: The signature and return type of the `getAllTypesForApplication()` function differed from its docs. Test Plan: Carefully read the code of the function and compare it to the `getAllInstalledTypes()` function above from which the docs were incorrectly copied. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25572 --- src/applications/phid/type/PhabricatorPHIDType.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/applications/phid/type/PhabricatorPHIDType.php b/src/applications/phid/type/PhabricatorPHIDType.php index f15df7f886..6a283a7d08 100644 --- a/src/applications/phid/type/PhabricatorPHIDType.php +++ b/src/applications/phid/type/PhabricatorPHIDType.php @@ -207,11 +207,10 @@ public static function getAllInstalledTypes(PhabricatorUser $viewer) { /** - * Get all PHID types of applications installed for a given viewer. + * Get all PHID types of an application. * - * @param PhabricatorUser Viewing user. - * @return dict Map of constants to installed - * types. + * @param string Class name of an application + * @return dict Map of constants of application */ public static function getAllTypesForApplication( string $application) { From 603c877fa0439685641987b7e94b8f7dc1abec1f Mon Sep 17 00:00:00 2001 From: Yongmin Date: Thu, 11 Apr 2024 17:37:01 +0000 Subject: [PATCH 424/425] Fix phacility link for arcanist guides Summary: Signed-off-by: Yongmin Hong Test Plan: Checkout https://we.phorge.it/book/phorge/article/arcanist and https://we.phorge.it/book/phorge/article/arcanist_quick_start/ and verify the new clone URL. Reviewers: O1 Blessed Committers, aklapper Reviewed By: O1 Blessed Committers, aklapper Subscribers: aklapper, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Differential Revision: https://we.phorge.it/D25573 --- src/docs/user/userguide/arcanist.diviner | 2 +- src/docs/user/userguide/arcanist_quick_start.diviner | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docs/user/userguide/arcanist.diviner b/src/docs/user/userguide/arcanist.diviner index 943c8a2193..74d3246bbd 100644 --- a/src/docs/user/userguide/arcanist.diviner +++ b/src/docs/user/userguide/arcanist.diviner @@ -90,7 +90,7 @@ have PHP installed, you can download it from . To install Arcanist, pick an install directory and clone the code from GitHub: - some_install_path/ $ git clone https://github.com/phacility/arcanist.git + some_install_path/ $ git clone https://github.com/phorgeit/arcanist.git Now add `some_install_path/arcanist/bin/` to your PATH environment variable. When you type "arc", you should see something like this: diff --git a/src/docs/user/userguide/arcanist_quick_start.diviner b/src/docs/user/userguide/arcanist_quick_start.diviner index 2326add155..a48b93de12 100644 --- a/src/docs/user/userguide/arcanist_quick_start.diviner +++ b/src/docs/user/userguide/arcanist_quick_start.diviner @@ -20,7 +20,7 @@ First, install dependencies: Then install Arcanist itself: - somewhere/ $ git clone https://github.com/phacility/arcanist.git + somewhere/ $ git clone https://github.com/phorgeit/arcanist.git Add `arc` to your path: From fa2376c92b1b80d64e317fbcb5c89890a0766300 Mon Sep 17 00:00:00 2001 From: Minsoo Choo Date: Fri, 12 Apr 2024 18:24:20 -0400 Subject: [PATCH 425/425] Initialize fbsd_username --- .../auth/controller/PhabricatorAuthRegisterController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/applications/auth/controller/PhabricatorAuthRegisterController.php b/src/applications/auth/controller/PhabricatorAuthRegisterController.php index fa0181ca9e..be310e6268 100644 --- a/src/applications/auth/controller/PhabricatorAuthRegisterController.php +++ b/src/applications/auth/controller/PhabricatorAuthRegisterController.php @@ -73,6 +73,7 @@ public function handleRequest(AphrontRequest $request) { $default_username = null; $default_realname = null; $default_email = null; + $fbsd_username = ''; } else { $default_realname = $account->getRealName(); $default_email = $account->getEmail();

KT^Ih^!~ZeSeNteK5;0Z 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 beL{e0aNa4agq*=oS_a5SDoWIf83+Fl%?f@e literal 37744 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 diff --git a/webroot/rsrc/image/sprite-login.png b/webroot/rsrc/image/sprite-login.png index 4a07583c0d96ba33a725ea1aab13adaf3f32aa1e..58a2dcf5ec0da01b47e9b8b6c13579cd83a77857 100644 GIT binary patch literal 13862 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 literal 16010 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 xqx$v@Fb(>2-dEjUpMQmY%KpDC8`u}=W{Wd4WR Date: Thu, 15 Jun 2023 12:10:23 +0200 Subject: [PATCH 204/425] Update Google icon on Auth Provider page Summary: Closes T15479 Test Plan: Go to `/auth/config/new/` in browser and look at icons. Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: speck, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15479 Differential Revision: https://we.phorge.it/D25297 --- resources/celerity/map.php | 10 +++++----- resources/sprite/login_1x/Google.png | Bin 899 -> 894 bytes resources/sprite/login_2x/Google.png | Bin 2272 -> 1446 bytes resources/sprite/manifest/login.json | 2 +- webroot/rsrc/image/sprite-login-X2.png | Bin 31127 -> 29663 bytes webroot/rsrc/image/sprite-login.png | Bin 13862 -> 13623 bytes 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index aa1d6b8014..a7b8a41aa3 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '76ed87e3', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '051ab1ff', + 'core.pkg.css' => 'f3d35ae0', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => '525f9a1d', @@ -184,7 +184,7 @@ 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', 'rsrc/css/phui/workboards/phui-workcard.css' => '62056e3b', 'rsrc/css/phui/workboards/phui-workpanel.css' => 'bc06f022', - 'rsrc/css/sprite-login.css' => '8d16d643', + 'rsrc/css/sprite-login.css' => '0a370010', 'rsrc/css/sprite-tokens.css' => 'f1896dc5', 'rsrc/css/syntax/syntax-default.css' => '055fc231', 'rsrc/externals/d3/d3.min.js' => '9d068042', @@ -342,8 +342,8 @@ 'rsrc/image/phrequent_active.png' => 'de66dc50', 'rsrc/image/phrequent_inactive.png' => '79c61baf', 'rsrc/image/resize.png' => '9cc83373', - 'rsrc/image/sprite-login-X2.png' => '2f61c306', - 'rsrc/image/sprite-login.png' => '7031ab39', + 'rsrc/image/sprite-login-X2.png' => 'c1458879', + 'rsrc/image/sprite-login.png' => 'f11dad90', 'rsrc/image/sprite-tokens-X2.png' => '21621dd9', 'rsrc/image/sprite-tokens.png' => 'bede2580', 'rsrc/image/texture/card-gradient.png' => 'e6892cb4', @@ -906,7 +906,7 @@ 'project-triggers-css' => 'cd9c8bb9', 'project-view-css' => '2f7caa20', 'setup-issue-css' => '5eed85b2', - 'sprite-login-css' => '8d16d643', + 'sprite-login-css' => '0a370010', 'sprite-tokens-css' => 'f1896dc5', 'syntax-default-css' => '055fc231', 'syntax-highlighting-css' => '548567f6', diff --git a/resources/sprite/login_1x/Google.png b/resources/sprite/login_1x/Google.png index e0989b9c563d90b4f5c26c5ad2f6f7ebc020f269..4e476c811db4a87d9daa07ebf4c6119954eed92b 100644 GIT binary patch delta 871 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 diff --git a/resources/sprite/manifest/login.json b/resources/sprite/manifest/login.json index 8770cbe3b1..05f47947e7 100644 --- a/resources/sprite/manifest/login.json +++ b/resources/sprite/manifest/login.json @@ -39,7 +39,7 @@ "login-Google": { "name": "login-Google", "rule": ".login-Google", - "hash": "72e7b0e1005c92f059f4d5881592ee72" + "hash": "9a25ee35dfde4135db2e321e01b82c61" }, "login-HTTP": { "name": "login-HTTP", diff --git a/webroot/rsrc/image/sprite-login-X2.png b/webroot/rsrc/image/sprite-login-X2.png index 5a162a8538477459352a86c512ea8e4bd621d935..136a256c8baed89861cb97b705102b3e81cf59c1 100644 GIT binary patch literal 29663 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