328 Commits

Author SHA1 Message Date
Nathan Letwory
bced48185b Fix subtype transaction data missing.
Test plan: change type of a task, check
with transaction.search the transaction
data exists.
2020-02-27 23:35:30 +02:00
Nathan Letwory
9da2ec1e8e Merge branch 'master' into blender-tweaks-mrg
# Conflicts:
#	resources/celerity/map.php
2020-02-03 22:03:37 +02:00
Nathan Letwory
1307f0ebbe Merge branch 'master' into blender-tweaks-mrg
# Conflicts:
#	resources/celerity/map.php
2020-01-27 22:20:39 +02:00
5ba0687123 Show number of query result for advanced queries 2019-12-24 11:18:19 +01:00
fef207bf00 Fix task counter query
Was doing wrong count when multiple projects were involved.
Also pagination was not ignored as it should have.

This technically increases load on the database, so maybe
we need to upgrade server to handle real thing we want to be
displayed.
2019-12-23 18:15:35 +01:00
5ef3552549 Correct link to new addons 2019-12-23 11:52:02 +01:00
0f6ca4be21 Tweaked wording and links on the front page 2019-12-23 10:42:55 +01:00
4653348b13 Maniphest: Switch from custom type field to task subtype 2019-12-18 10:33:12 +01:00
6826284108 Maniphest: Remove blender-specific type rendering
Will be rendered using task's subtype.
2019-12-18 09:53:53 +01:00
c93fdca80d Merge branch 'master' into blender-tweaks 2019-12-17 13:02:09 +01:00
432b6d61a6 Show correct command to destroy users 2019-10-17 11:25:53 +02:00
4c2b7d7573 Merge branch 'master' into blender-tweaks 2019-10-17 11:23:01 +02:00
d8e25a6226 Correct shebang to be usable in the deployed environment 2019-10-03 15:27:02 +02:00
db6f4eaab1 Merge branch 'master' into blender-tweaks 2019-10-01 14:48:19 +02:00
4555f18c41 Add auth provider which uses Phabricator
The idea is to use it instead of a custom field approach.
2019-09-30 17:23:36 +02:00
788d8a8b98 Remove local hack preserving line breaks
This seems to be addressed by upstream nowadays.
2019-09-30 17:21:03 +02:00
55f22f74f5 Revert "Fix for missing updates of hidden properties"
More correct fix is being worked on.

Reverting to an older state, to be able to deploy other changes.

This reverts commit 8efe191897.
2019-09-30 17:17:19 +02:00
de2f7e857d Fix wrong method visibility
Was discovered by `arc unit`, caused by wrong merge conflict resolution.
2019-09-30 16:59:37 +02:00
7c869ab033 Update arc libreate
Seems there is no need to run it for extensions.

Solves error reported by `arc unit`.
2019-09-30 16:59:29 +02:00
f9c680ddc0 Merge branch 'upstream_master' into blender-tweaks 2019-09-30 10:52:47 +02:00
8efe191897 Fix for missing updates of hidden properties 2019-09-30 10:50:51 +02:00
8d6f1a6128 Proper commenting style. 2019-09-24 00:44:08 +03:00
81bae35d2f Render milestones as original getDisplayName. 2019-09-24 00:44:08 +03:00
fc5744c7f0 Show entire path for all projects. 2019-09-24 00:44:08 +03:00
7aea82d526 Clean up ancestor path implementation.
* mpull for cleaner ancestor access
* move implementation to PhabricatorProject
  * as new function getDisplayNameWithAncestorPath
  * no pht usage, since there are no localisable items
2019-09-24 00:44:08 +03:00
cb29054e66 Show ancestor path for sub-projects and milestones.
To understand better to what projects a sub-project or
milestone belongs show the entire ancestor path.

Solves part of T69306.

Differential Revision: https://developer.blender.org/D5656
2019-09-24 00:44:08 +03:00
0416648b39 Merge branch 'master' into blender-tweaks 2019-09-18 10:42:01 +02:00
01fea69352 Limit some workflow elements to moderators
Allows to streamline triaging and keeping track of reports status,
together with removing confusing elements which non-moderators can
not use anyway.
2019-09-03 15:38:59 +02:00
c7a2133639 Differential: fill author field from user account
Do it unless this information was provided by `arc diff`.
2019-09-03 14:43:43 +02:00
a162f188c2 Differential: exclude fields from commit message to follow our guidelines
Do it as a tweaks to render due to:

- Those fields are considered permanent.
- We still want to be able to specify them when creating new revisions
  (at least for subscribers/reviewers).
2019-09-03 14:36:00 +02:00
2b54804c7b Merge branch 'master' into blender-tweaks 2019-09-03 14:24:06 +02:00
a8fef30871 Merge branch 'master' into blender-tweaks 2019-09-02 10:19:50 +02:00
9dc4d903c7 Ignore disabled users for Git/SVN synchronization 2019-08-01 16:51:02 +02:00
b8b18995c5 Merge branch 'master' into blender-tweaks 2019-06-20 10:02:20 +02:00
971b397133 Merge branch 'master' into blender-tweaks 2019-06-19 10:37:08 +02:00
aee7dc94bc More fixes for MFA 2019-06-18 16:18:29 +02:00
feb8db1212 Fix non-vector sorting used on a vector 2019-06-18 10:44:00 +02:00
f9f7f005f2 Fix task count query not working 2019-06-18 09:05:19 +02:00
2321a886ae Merge branch 'master' into blender-tweaks 2019-06-18 08:29:51 +02:00
72858c6746 Wrap task count selection part to a query string 2019-06-18 08:22:30 +02:00
79ca5a3026 Update top bar to match other websites 2019-06-18 08:17:32 +02:00
3d37998174 Update diff guidelines, login and welcome text 2019-06-17 16:11:02 +02:00
ce29e40b57 Merge branch 'master' into blender-tweaks 2019-04-25 14:19:49 +02:00
ef26d6afdc Merge branch 'master' into blender-tweaks 2019-04-04 11:21:47 +02:00
dc84bc9391 Allow committing to SVN branches 2019-03-15 16:45:19 +01:00
321eb18cd7 Fix SVN rules synchronization script 2019-02-05 12:40:35 +01:00
6936b8c4f3 Ignore hidden fields in the feed 2019-02-04 15:15:43 +01:00
26f43ee9b8 Merge branch 'master' into blender-tweaks 2019-02-04 14:17:46 +01:00
e19d318af9 Merge branch 'master' into blender-tweaks 2019-01-25 15:35:33 +01:00
dfc7f764de Merge branch 'master' into blender-tweaks 2018-12-26 11:44:27 +01:00
331b4cc42c Use better suitable policy for the previous commits 2018-12-11 10:07:43 +01:00
674d537f91 Similar tweaks for priority and status 2018-12-11 10:01:28 +01:00
a442a019d9 Allow assigning task to anyone who can view the task
Matches old behavior prior to a recent update.
2018-12-11 09:58:32 +01:00
774a554405 Post-merge fix for API usage 2018-12-09 17:21:40 +01:00
90f56e07ff Attempt to fix usage of wrong class 2018-12-09 13:28:47 +01:00
4b428ffb8a Merge branch 'master' into blender-tweaks 2018-12-09 13:25:53 +01:00
bcd0caecd1 Merge branch 'master' into blender-tweaks 2018-12-04 10:29:38 +01:00
78c32afa4e Merge branch 'master' into blender-tweaks 2018-10-22 12:31:30 +02:00
f3302fe1b9 Grant fiel editing and deletion to admins 2018-08-03 11:58:08 +02:00
7eba06c43d Merge branch 'master' into blender-tweaks 2018-08-02 14:26:08 +02:00
713f8e2c6a Merge branch 'master' into blender-tweaks 2018-04-24 12:56:12 +02:00
ea0100310b Fix issue with double-slash added to maniphest URLs 2018-04-16 12:36:11 +02:00
4332bfe537 Merge branch 'master' into blender-tweaks 2018-04-16 12:29:33 +02:00
25ce2bdd92 Merge branch 'master' into blender-tweaks 2018-02-09 11:03:26 +01:00
dc17ef4f7c Merge branch 'master' into blender-tweaks 2018-01-29 09:40:46 +01:00
8097508909 Merge branch 'master' into blender-tweaks 2018-01-29 09:39:57 +01:00
8addc33734 Add state of the art fax defence into the source tree 2017-12-07 11:24:51 +01:00
990136a337 Grant profile editing to administrators 2017-12-07 09:32:02 +01:00
d14b204aae Merge branch 'master' into blender-tweaks 2017-12-06 16:58:17 +01:00
4067829d2c Fix/workaround for code review not working for logged out users 2017-10-19 07:50:39 +02:00
8bc4c8c485 Merge branch 'master' into blender-tweaks 2017-10-18 13:29:21 +02:00
bf8aec2a14 Merge branch 'master' into blender-tweaks 2017-10-02 10:51:32 +02:00
ace2787868 Fix T52676: Phabricator 'To Do' URL format and search error 2017-09-18 14:13:39 +02:00
08dde1da13 Merge branch 'master' into blender-tweaks 2017-09-08 15:50:36 +02:00
4cde7a4b06 Correct previous commit 2017-04-27 16:27:47 +02:00
42ce8a82af Fix hidden fields were isible in conduit 2017-04-27 16:16:35 +02:00
6d6e446f6f Merge branch 'github-facebook-master' into blender-tweaks 2017-04-27 16:14:16 +02:00
26bf667fec Fix unwanted caption for real now
Hopefully.
2017-03-29 13:25:16 +02:00
18b9c9b0b1 Attempt to remove Summary from differencial template 2017-03-29 12:12:47 +02:00
f78ca8833b Update generator script to the latest changes in Phabricator 2017-03-27 10:12:55 +02:00
19337de8f0 Merge branch 'master' into blender-tweaks 2017-03-20 09:28:31 +01:00
c2a4fbc98c Followup commit, allow changing task project tags 2017-03-15 16:04:22 +01:00
87c0e13efd Attempt to solve regressions in our policy rules
Users weher able to triage other's reports.
2017-03-15 12:13:18 +01:00
3442097b31 Attempt to fix arcanist diff 2017-03-14 15:31:10 +01:00
c7764ed1d0 Quick hack to restore old main page title 2017-03-14 13:58:08 +01:00
6e39022af9 Merge branch 'master' into blender-tweaks 2017-03-09 12:13:16 +01:00
d88716feba Merge branch 'blender-tweaks' of git.blender.org:phabricator into blender-tweaks 2017-01-20 14:13:04 +01:00
678347d836 Merge branch 'master' into blender-tweaks 2017-01-20 14:12:45 +01:00
890ccea846 Use separate form for addons bug 2016-12-02 09:48:16 +01:00
405525357d Fix T50155: Maniphest could provide better information on task reject
Use bug report form when reporting bug from the project page in maniphest
2016-12-02 09:39:48 +01:00
3eee2a3311 Fix typo from previous commit 2016-07-18 14:48:56 +02:00
55d1f0081d Fix links in the diff submission guidelines 2016-07-18 14:46:06 +02:00
ea73d9bf86 Fix bug accessing user manage form 2016-07-07 16:20:29 +02:00
3eab15180c Fix bugs accessing user preferences 2016-07-07 16:13:07 +02:00
2210be45fe Make it possible to create new tasks with Blender's policies 2016-07-07 12:17:07 +02:00
5bdc3dfad8 Merge branch 'master' into blender-tweaks 2016-07-01 09:16:15 +02:00
c22440fe30 Merge branch 'master' into blender-tweaks 2016-05-20 11:26:25 +02:00
b41844290b Merge branch 'master' into blender-tweaks 2016-04-06 09:39:20 +02:00
32a3440156 Merge branch 'blender-tweaks' of git.blender.org:phabricator into blender-tweaks 2016-03-15 18:25:38 +05:00
630eb3c143 Post-merge blender tweaks 2016-03-15 18:25:26 +05:00
534b628ad2 Update mobile 'home' page links
If you use phabricator with a mobile phone with small screen space
you get only the sidebar at http://developer.blender.org/.

If you the click the 'Home - Command Center' Button available for
mobile devices you get the site http://developer.blender.org/home/

But there, the short-cut links will result in a 404 page (or login,
if you are not logged in) so better make the links there absolute.

Patch by Leon (Leon95), thanks!

Reviewers: sergey

Subscribers: sergey

Projects: #phabricator, #infrastructure:_websites

Differential Revision: https://developer.blender.org/D1843
2016-03-14 11:37:40 +05:00
8a463524b4 Use own Phab install for arc
This way we can apply patches submitted to our tracker.
2016-03-14 11:36:24 +05:00
b50eefbd5b Merge branch 'master' into blender-tweaks 2016-03-05 16:19:51 +05:00
3a2ebeca11 Merge branch 'github-facebook-master' into blender-tweaks 2015-12-24 22:50:19 +05:00
95f7da61a9 Remove unneeded custom configuration options 2015-12-18 19:55:22 +05:00
0d04beb777 Switch maniphest to sue new phab forms instead of some hardcoded fields etc 2015-12-18 15:46:18 +05:00
7ab26ff7af Bring back comment about drag-drop in the re-markup edit field. 2015-12-18 15:29:49 +05:00
6800fc6103 Preserve line breaks for the instruction fields 2015-12-18 15:15:59 +05:00
9077c1f24f Merge branch 'master' into blender-tweaks
Still some work to be done bring back some removed customizations,
but they can be done using new awesome phab features.
2015-12-18 15:04:15 +05:00
2327f2af6f Show maniphest task type in the list 2015-12-17 23:06:46 +05:00
41fff9c930 Fix alignment in the maniphest list
"Assigned To" and "By" fields were too short.
2015-11-13 18:11:21 +05:00
cfcef40c65 Merge branch 'master' into blender-tweaks 2015-11-13 17:46:32 +05:00
29d158a3bc Don't show list of members in the project's details page
This only adds clutter, plus we've got dedicated page for members.
2015-11-13 17:34:41 +05:00
411cf324dd Linking to project will go to project's details page, not to the board
Board is not so much helpful to see for average users.
2015-11-13 17:30:36 +05:00
52230a803f Merge branch 'github-facebook-master' into blender-tweaks 2015-11-13 17:20:03 +05:00
ddf6f275db Show login in the user monogram
This is actually appeared handy after discussion with campbell.
2015-06-03 15:13:29 +05:00
8c65ee54d0 Make user monograms surviving cases when there's no real name 2015-06-03 14:59:41 +05:00
d07d44fcc7 Remove debug-only code from the previous commit 2015-06-03 14:34:39 +05:00
772d59f0ab Make real name more primary way of communication 2015-06-03 14:32:32 +05:00
f7da3f88f9 Fix T44841: Error when clicking Maniphest in the phabricator sidebar 2015-05-26 13:12:07 +05:00
e4f6cf9993 Attempt to fix issues with task counter returning several rows 2015-05-26 12:58:32 +05:00
dd34c93656 Put gruping back to the task counter sql 2015-05-26 12:35:44 +05:00
320ffa2308 Fix buildbot application
Was using deprecated class.
2015-05-18 15:27:46 +05:00
b5e7136f68 Update celerity map 2015-05-18 01:31:51 +05:00
e2b7664451 Use different widths for dashboard and regular maniphest task 2015-05-18 01:31:22 +05:00
725ad07e6f Attempt to make both welcome screen and maniphest task list happy with word wrapping 2015-05-18 01:27:59 +05:00
75cbd69d85 One more tweak, sorry for the trouble 2015-05-18 01:02:59 +05:00
a9a71ef011 Tweak to previous commit
Previous commit made it so task title is cropper far too soon.
Still not really ideal solution, but should be good enough for now.
2015-05-18 01:00:29 +05:00
362ba36695 Fix authorship not fitting into maniphest task in the list 2015-05-18 00:39:58 +05:00
38d687ede2 Attempt to make default repositories sorting by name 2015-05-17 23:39:55 +05:00
78cd1cf494 Remove results of wrong merge 2015-05-17 22:49:57 +05:00
0b990d42cd Add Blender Next to always visible projects 2015-05-17 22:14:53 +05:00
74571de5b6 Deduplicate some code, saved/unsaved query title is handling in getDescriptionForQuery 2015-05-17 21:47:22 +05:00
01ce61f6e2 Update text of addon patch submission 2015-05-17 21:40:14 +05:00
09c9aa078d Tweaks to the home file to make it wrap nicely 2015-05-17 21:36:00 +05:00
73441d690e Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
	src/applications/config/option/PhabricatorCoreConfigOptions.php
	src/applications/differential/controller/DifferentialDiffCreateController.php
	src/applications/files/storage/PhabricatorFile.php
	src/applications/maniphest/query/ManiphestTaskQuery.php
	src/applications/maniphest/query/ManiphestTaskSearchEngine.php
	src/applications/repository/query/PhabricatorRepositorySearchEngine.php
	src/applications/search/controller/PhabricatorApplicationSearchController.php
2015-05-17 21:24:32 +05:00
afdc21ee04 Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
2015-03-06 20:28:53 +05:00
fc317b7f28 Move extension applications to where they are expected to be 2015-03-06 20:21:53 +05:00
b3f2b7faf4 Use a bit closer to previous installation icon for buildbot application 2015-03-06 20:19:45 +05:00
7ea70dcb53 Make home page much closer to the upstream
Mainly get rid of hardcoded feed panel, this can be done with
dashboards now.
2015-03-06 19:27:42 +05:00
d0b90fdef7 Replace all favicon files with blender logo 2015-03-06 19:13:16 +05:00
3c05d31d2e Merge branch 'master' into blender-tweaks
Conflicts:
	.gitignore
	resources/celerity/map.php
	src/__phutil_library_map__.php
	src/applications/home/controller/PhabricatorHomeMainController.php
	src/applications/maniphest/controller/ManiphestSubscribeController.php
	src/applications/maniphest/event/ManiphestActionMenuEventListener.php
	src/applications/maniphest/query/ManiphestTaskQuery.php
	webroot/rsrc/css/application/base/main-menu-view.css
	webroot/rsrc/css/sprite-menu.css
2015-02-27 13:23:38 +05:00
11f5eb21af Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
	src/applications/differential/controller/DifferentialDiffCreateController.php
2014-11-29 18:37:00 +05:00
c90665645e Gitosis sync fixes and improvements
- Fixed some remaining issues caused by the update process.
- Support custom rules for the repositories.
  Currnetly only supports allowing users.
2014-11-10 01:38:58 +05:00
fb3c71820b Fix rebuild gitadmin using old user typeconst class 2014-11-10 01:16:32 +05:00
e533774ac0 Some post-merge tweaks, regenerated celerity map 2014-11-08 18:38:00 +05:00
647f8de9bb Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
	webroot/favicon.ico
	webroot/rsrc/css/application/base/main-menu-view.css
	webroot/rsrc/css/sprite-menu.css
2014-11-08 18:37:40 +05:00
599bf57cd8 Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
	src/applications/maniphest/controller/ManiphestTaskListController.php
2014-11-01 17:23:50 +05:00
50c506ea2d Merge branch 'master' into blender-tweaks
Conflicts:
	conf/default.conf.php
	resources/celerity/map.php
2014-10-11 20:22:36 +06:00
4cc7990b1a Attempt to fix bad url to To Do tasks in maniphest 2014-10-03 02:35:19 +06:00
4ef5fdefb2 Experiment with showing both real and login names
This way it's still easy to reference people with @ and makes
it more natural communication using real name if needed.
2014-10-03 02:32:57 +06:00
a018837214 Revert "Show real name rather than the nick name in the interface all over the place"
This reverts commit e74f0e1558.

Showing just a name makes it impossible to reference people in a comments using @.
2014-10-01 11:27:36 +06:00
e74f0e1558 Show real name rather than the nick name in the interface all over the place 2014-09-30 18:06:15 +06:00
fd882b5167 Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
2014-09-30 14:31:44 +06:00
15bda62b89 Fix shouldAllowPublic method redeclaration 2014-09-23 13:34:54 +06:00
5e4ce96266 Fix some more issues after the merge 2014-09-23 00:57:46 +06:00
dbd2f0d06f Fix bad conflict resolve
Also fix wrong push capability check.
2014-09-23 00:23:56 +06:00
c87e657412 Fix wrong navigation from custom query to hidden project 2014-09-22 19:29:02 +06:00
6275e3f08f Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
	src/__phutil_library_map__.php
2014-09-21 19:41:03 +06:00
bc2e4a2452 Always Blender Regressions project in the maniphest projects list 2014-09-14 16:14:53 +06:00
ba7d9fb724 Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
	src/__phutil_library_map__.php
	src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
	src/applications/project/controller/PhabricatorProjectListController.php
2014-09-14 16:12:27 +06:00
c50ab96abb tweak to hidden custom field 2014-07-28 01:39:05 +06:00
fa68c6a0fa Preserve line breaks in projects descriptions
This is slightly better fix than previous commit, but
we might totally revisit it as a part of

  https://secure.phabricator.com/D10014
2014-07-22 21:17:35 +06:00
d1b60fec48 Revert "Preserve lone breaks in projects description"
This reverts commit 87e0a26d24.
2014-07-22 20:51:24 +06:00
99a839605c Fix wrong source for projects
Also made code lcoser to master.
2014-07-22 20:31:31 +06:00
08f5b3e920 Merge branch 'master' into blender-tweaks 2014-07-22 20:20:57 +06:00
87e0a26d24 Preserve lone breaks in projects description 2014-07-22 15:12:17 +06:00
91c07e9f99 Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
	src/applications/maniphest/controller/ManiphestTaskEditController.php
2014-07-22 13:33:45 +06:00
9b2b75e4b4 Workaround for regular users not being able to report new tasks
This is caused by the self-locking check which fails in our usecase.
2014-07-22 13:03:23 +06:00
77c913c70a Correct key name usage in the previous commit 2014-07-20 16:48:47 +06:00
1a1bea527c Attempt to fix issue with sending new differencial revision to review 2014-07-20 16:43:32 +06:00
46f8f6d90d Pin buildbot and documentation by default 2014-07-20 03:59:29 +06:00
dbad3b6489 Quick fix for the maniphest navigation 2014-07-20 03:40:17 +06:00
1821cedbc4 Show unconfirmed project link 2014-07-20 03:14:05 +06:00
1448eafd64 Use HTTPS link to the buildbot 2014-07-20 03:11:59 +06:00
8f4047b36a Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
	src/applications/home/controller/PhabricatorHomeMainController.php
	src/applications/maniphest/controller/ManiphestTaskEditController.php
	src/applications/maniphest/query/ManiphestTaskQuery.php
	src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
2014-07-17 01:33:33 +06:00
2683fde42a Remove hacks around custom task status
Seems it's no longer needed since statuses are actually
configurable via UI configurator.
2014-06-06 18:12:12 +06:00
02522d877d Tweak for storage migration script to update status for archived tasks 2014-06-06 18:06:13 +06:00
769ffa2982 Restore hack which allowed importing projects.b.o passwords 2014-06-06 17:29:39 +06:00
33eed14141 Page title got lost 2014-06-06 15:33:08 +06:00
a0bd47a012 Correction to hidden custom fields 2014-06-06 15:12:55 +06:00
8c14ef97bf Get rid of custom code in home page
Seems it's no longer needes.
2014-06-06 15:03:48 +06:00
5bdfbe8250 Attempt to fix maniphest tak editing form 2014-06-06 14:50:09 +06:00
f2319c9689 Don't show archived projects in maniphest sidebar 2014-06-06 14:34:38 +06:00
c3138c6497 Merge branch 'master' into blender-tweaks
Conflicts:
	resources/celerity/map.php
	src/__phutil_library_map__.php
	src/applications/auth/application/PhabricatorApplicationAuth.php
	src/applications/base/controller/PhabricatorController.php
	src/applications/differential/customfield/DifferentialLintField.php
	src/applications/differential/customfield/DifferentialUnitField.php
	src/applications/differential/field/specification/DifferentialManiphestTasksFieldSpecification.php
	src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
	src/applications/feed/application/PhabricatorApplicationFeed.php
	src/applications/home/controller/PhabricatorHomeController.php
	src/applications/home/controller/PhabricatorHomeMainController.php
	src/applications/maniphest/constants/ManiphestTaskStatus.php
	src/applications/maniphest/controller/ManiphestController.php
	src/applications/maniphest/controller/ManiphestTaskDetailController.php
	src/applications/maniphest/controller/ManiphestTaskEditController.php
	src/applications/maniphest/controller/ManiphestTaskListController.php
	src/applications/maniphest/event/ManiphestActionMenuEventListener.php
	src/applications/maniphest/query/ManiphestTaskQuery.php
	src/applications/maniphest/query/ManiphestTaskSearchEngine.php
	src/applications/maniphest/view/ManiphestTaskProjectsView.php
	src/applications/people/storage/PhabricatorUser.php
	src/applications/project/controller/PhabricatorProjectListController.php
	src/applications/project/controller/PhabricatorProjectProfileController.php
	src/applications/search/controller/PhabricatorApplicationSearchController.php
	src/applications/settings/controller/PhabricatorSettingsMainController.php
	src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
	src/view/phui/PHUIPropertyListView.php
	webroot/rsrc/css/sprite-menu.css
2014-06-05 17:07:20 +06:00
56e8308621 SVN auth script: enable commit access to /tags folder 2014-03-27 18:18:56 +06:00
cb6a8756b6 Migration: remove files and changes from gforge migration.
These were moved to blender-migration branch.
2014-02-03 13:35:20 +01:00
e7242143a0 Merge: more tweaks to project page for when there is no description or no members. 2014-02-03 13:19:26 +01:00
c12898b63a Merge: restore project description to more prominent location again. 2014-02-03 13:07:10 +01:00
9453f29deb Merge branch 'master' into blender-tweaks
Conflicts:
	scripts/celerity_mapper.php
	src/__celerity_resource_map__.php
	src/applications/differential/controller/DifferentialDiffCreateController.php
	src/applications/diffusion/controller/DiffusionBrowseFileController.php
	src/applications/diffusion/controller/DiffusionRepositoryEditPolicyController.php
	src/applications/files/storage/PhabricatorFile.php
	src/applications/maniphest/controller/ManiphestTaskListController.php
	src/applications/project/controller/PhabricatorProjectController.php
	src/applications/project/controller/PhabricatorProjectProfileController.php
	src/applications/repository/storage/PhabricatorRepository.php
	webroot/rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js
2014-02-03 15:42:43 +06:00
9093816113 Phabricator: restore reviewers and CCs field in commit messages, they are needed
for arc diff.
2014-01-24 17:58:08 +01:00
3471c1b87e Fix T37772: Tab indentation missing in Diffusion
This seems to be a specific of how browsers are dealing with
spaces/tabs. Multiple spaces works just fine, but multiple
tabs were treating as a single space which breaks indentation.

Now made it so tabs are replaced with 4 spaces. Not ideal but
still better than fully unreadable code.
2014-01-17 17:05:14 +06:00
30f0de9427 Fix T38210: phabricator file downloads not working for non-logged in users. 2014-01-15 15:53:50 +01:00
5278e94566 Further arc commit messages tweak to follow guidelines, leave out CCs,
Reviewers (not Reviewed By), Maniphest Tasks and Summary label.
2014-01-04 05:37:58 +01:00
08b1e9d764 Fix buildbot link not working after move to https, redirect instead now. 2014-01-04 05:13:51 +01:00
30427e0626 Leave reviewers and CCs out of commit message. 2014-01-04 05:13:28 +01:00
752a5b9bc3 Further tweak to bug report page descriptions. 2013-12-29 18:25:34 +01:00
a5390d5461 Improve bug report page descriptions. 2013-12-29 18:23:02 +01:00
a02fa19d5a Fixed typo in previous commit, no functional changes. 2013-12-25 17:44:36 +06:00
f88bdcd4ff Truncate file to 10 sec on display with diffusion
Displaying huge files like .po used loads of CPU power
and might have easily run out of time or memory.

Now file will be truncated after 10 sec of view generation.
2013-12-25 17:34:24 +06:00
b98f8a570c Added a way to generate login=>(email, real name) map
Also fixed a typo in custom field in user change password function.
2013-11-29 22:58:33 +06:00
644070340d Add list of all archived repositories 2013-11-28 15:39:25 +06:00
93fecfe7e9 Added list of archived repositories which RO access to them
Currently only bf-extensions, rest of the repos will arrive soon.
2013-11-28 15:19:32 +06:00
53bb3ac361 Make sure all the users have RO access to the repo 2013-11-28 15:09:37 +06:00
137df8c6d3 Make sure there's no trailing slash in svn's subpath
This gives issues with svn access file, leading to
access denied by the server.
2013-11-28 14:57:24 +06:00
9863285bf0 Fix missing project action list on mobile. 2013-11-27 20:47:30 +01:00
a392b4a14f Fix typo in svn authentification generator script 2013-11-28 00:21:59 +06:00
d03569e908 Correction for the previous commit 2013-11-27 22:20:55 +06:00
b1097e9c61 Fix for crash when pushable project have unknown users
Apparently we've got Unknown Object (Phabricator User) in
translation project now.

Not sure how it became invalid, but automated scripts
better be robust for this.
2013-11-27 22:14:07 +06:00
5bda6bf48b Attempt to make SVN happy with untrusted server certificate
Need this to fetch changes from our svn.b.o which have untrusted
issuer of the certificate.
2013-11-27 19:36:13 +06:00
a03cdacd9e Tweaks to svn access rights importer
- Make it work with repositories outside of svn.b.o.
  Useful for local debugging.

- Make it aware of svn subpath repository setting.
  So now it's possible to restrict commit rights
  to a subpath in svn repository.

- Make SVN repositories always available for
  read-only access;
2013-11-27 18:37:37 +06:00
c3fe33f5a0 Remove debug-only code from svn auth script 2013-11-27 02:25:47 +06:00
5d823ff1a7 Fix T37436: Add a method to show the bug count
Now query header will show text like:

  N total tasks for query "Foo"
2013-11-22 00:24:14 +06:00
63df9dd1f4 Undo test commit. 2013-11-21 19:10:36 +01:00
0c80c9fead Add "Open Revisions" builtin query for differential, and have links for both
code reviews and patches on welcome page.
2013-11-21 19:05:38 +01:00
b8d42a7cc6 Test commit for disabling merge commits. 2013-11-21 19:05:38 +01:00
4e7dc30fea Fix typo in method name 2013-11-21 23:17:13 +06:00
99c68b1091 Fix T37529: show up to 4 project tags for maniphest tasks instead of 2. 2013-11-19 16:14:03 +01:00
d48979091e Maniphest: order open tasks by date created and don't group by priority, to
make it more like the old bug tracker. Also makes count bugs fairly easy by
just looking at the number on the second or third page.
2013-11-17 10:21:38 +01:00
785caa5b6c Test commit for mail hooks, would prefer to do not it on live install but
that seems to be the only place it fails.
2013-11-16 04:22:04 +01:00
261d7331f4 Remove some unnecessary code from last commit. 2013-11-16 02:49:55 +01:00
28bd8409df Remove unnecessary duplicate buildbot crumb. 2013-11-16 02:44:17 +01:00
47008a157c Browse Patches pointed to a wrong project 2013-11-16 00:00:02 +06:00
fc1e8681ef Add application name in crumbs display. 2013-11-15 17:30:28 +01:00
17085b4379 Try to clarify that the select projects listed on the home page are not all
projects.
2013-11-15 17:30:28 +01:00
dd6eefcd9c Make "Active" projects the default instead of "Joined". 2013-11-15 17:30:28 +01:00
ae2b0e63d6 Further tweaks to gi ermissions script
- Seems @all didn't work, now use explicit list of repos
- Handle cases when phab user uses the same key as used for
  "system" repositories.
2013-11-15 20:19:20 +06:00
bcbae097de Another attempt to make phab sync working 2013-11-15 18:55:16 +06:00
b59dca4eeb Made it so public keys are stored in files with .pub extension
It is an attempt to solve issues with missing permissions on
pahabricator users.
2013-11-15 18:49:56 +06:00
8098798fa9 Fix T37435: clicking maniphest next button with no /query in the URL would lose
the default query and show the search form on the second page.

This was a bug in Phabricator itself, just not as likely to happen because we
have the default query as Open Tasks while they have Assigned, which is unlikely
to give more than 100 results and so there would be no next button.
2013-11-15 02:46:28 +01:00
d8d0e2c99f Fix T37447: Add a link to project list when creating tasks. 2013-11-15 00:32:18 +01:00
214233f82f Show author name in task list views.
Fixes T37451.
2013-11-15 00:04:00 +01:00
eebe82c990 Assume 4 space for tabs by default in differential. 2013-11-14 23:27:09 +01:00
5b73071d64 Removed debug-only left-over 2013-11-15 02:29:03 +06:00
cd04de7f72 Order diffusion repositories by name (so Blender is at the top). 2013-11-14 19:44:47 +01:00
cf4b1317e1 Fix T37441: Could not assign tasks to self (via the "Assigned To" box)
Issue was caused by our database having login "watch" which is
apparently an object's method in firefox.

Added workaround for this into the code.
2013-11-14 21:24:26 +06:00
10e353b090 Allow anyone to login to developer.blender.org again. 2013-11-14 12:15:23 +01:00
81bd756ec6 Also correct sheband for svn auth script 2013-11-14 13:34:55 +06:00
9527d8aafd tweak to shebang so cron is happy with it 2013-11-14 13:31:50 +06:00
e35bc58e0e Fixes for gitosis admin script
Basically, pull wasn't happening correct and it was
updating files from CWD< not from workdir.
2013-11-14 12:44:59 +06:00
bd0f9fd913 Add sculpting and websites projects to welcome page. 2013-11-13 19:53:22 +01:00
44957f7806 Get repsitory name from svn URI
Would only work for repos inside svn.b.o.
Some further checks might be needed.
2013-11-14 00:21:04 +06:00
7ff26eb95d Show message that developer.blender.org is not open yet for logins. 2013-11-13 19:10:10 +01:00
1f2471fa67 Use git repository name from uri. not from repo name 2013-11-14 00:09:40 +06:00
7a5588a408 Appeared a typo in previous commmit
Didn't notice on system where had htaccess custom field.
2013-11-13 14:37:47 +06:00
ac03ae737f Fix for recent htaccess hack broke user registration
Issue was caused by custom fields loading will set
user's profile. In case user is new, this profile
will have title and blurb set to NULL, which is
unsupported.

Solved by setting title and blurb to an empty string
if setPassword detects that this is a new user.

tested with accountadin script, need to be tested
with online registration as well.
2013-11-13 14:35:29 +06:00
f9db95f555 Add a missing translations project member. 2013-11-13 09:06:25 +01:00
2bc045a997 * Add another duplicate email
* Remove addon testing tmp code
* Make files public viewable
2013-11-13 09:03:09 +01:00
531b0fa529 Added script to generate svn auth files
This script only generates content of either svnroot-access
or svnroot-authfile (depending on command line arguments).

It's not directly usable yet and would need some magic to
make it able to pass the data to svn.b.o. But this is up to
machines setup, not to the script.
2013-11-12 23:47:17 +06:00
6b1ed4fb9b Seems we need to load custom field manully 2013-11-12 23:46:10 +06:00
645a6a9f54 Made rebuild_gitadmin only take git repositories into account 2013-11-12 22:43:01 +06:00
88cd291b0f Only allow admins to login and disable new account registration.
This is a temporary change to avoid accidental interference during import tomorrow.
2013-11-12 14:55:36 +01:00
76e4aaf429 Import unix passwords for svn access, and fix passwrod/password typo. 2013-11-12 14:29:03 +01:00
365f8ad981 Added instructions about htaccess_passowrd_hash hidden field 2013-11-12 18:10:06 +06:00
0af08f9469 Added hack to user class to set htaccess hash when setting password
Uses hash generation from gforge code. Wouldn't work correct for
imported users yet.

TODO: Imported users are to use hash from projects.b.o's database.
2013-11-12 18:02:37 +06:00
23ce658af7 Made it possible to mark custom fields as hidden
it is done using "hidden": true setting in custom
field specification.

Hidden fields will never be displayed and their
intention is to be used by autohmatization scripts.
2013-11-12 18:02:37 +06:00
0a54474f98 Better Submit Addon page. 2013-11-11 21:00:30 +01:00
93ca9ed3a8 Update docs for migration 2013-11-12 00:58:04 +06:00
d110fdccc2 Added options to disable lint and unit tests in diffusion 2013-11-12 00:53:39 +06:00
4197d983bf Fix 404 after clicking Find Owners in diffusion, since we have this application disabled. 2013-11-11 17:49:03 +01:00
d23883b799 Update design task and todo task instructions. 2013-11-09 15:46:03 +01:00
675d2ac374 Link to addon docs on submit patch page. 2013-11-09 15:25:03 +01:00
5c86735ab7 On projects pages, add Submit Patch, Add To Do, Add Design Task links. It's all
a bit hardcoded but makes the expected workflow easier to understand.
2013-11-09 15:12:40 +01:00
566274dcac Patch/diff html pages updates. 2013-11-08 22:01:38 +01:00
de2814545b Add Submit Patch, Create Design Task and Create To Do actions in Maniphest when
browsing the corresponding.
2013-11-08 21:01:07 +01:00
4ea26f22c4 Add html instructions for diff/patch, todo and design tasks. 2013-11-08 20:55:53 +01:00
458bc7f921 Update task import script to handle addons. 2013-11-07 18:30:39 +01:00
3839698e4a Remove unnecessary whitespace. 2013-11-07 16:54:39 +01:00
b403863e62 Correction to documentation, extra bachslashes 2013-11-07 20:41:56 +06:00
246b73f922 Fix type in previous commit, sorry 2013-11-07 20:39:56 +06:00
6da7f462a9 Fix for documentation 2013-11-07 20:36:43 +06:00
90874b8b00 Links which goes to different site better open pages in new tab 2013-11-07 20:33:52 +06:00
7e874a33fb Added instructions to patch submission form. 2013-11-07 20:33:51 +06:00
bbe357e5c0 Add [developer.blender.org] in mail subject instead of only [Maniphest], [Diffusion], etc.
For casual users it's important to be able to see where the message comes from.
2013-11-07 20:33:51 +06:00
428cdae3cf Further tweaks to gitosis conf rebuild script
- Pull need SSH key as well
- Repository names are ensured to be lower case
- Use verbose name for commti author
2013-11-07 20:33:51 +06:00
1c689a6b24 Correct shebang for gitx-ssh 2013-11-07 20:33:51 +06:00
55972ef2c1 Gitadmin configuration now could be commited and pushed from update script 2013-11-07 20:33:51 +06:00
966687d1ab Project pages now have a Report Bug link, and View Tasks now uses the project
filter rather than setting up a query.
2013-11-07 20:33:51 +06:00
6a86144adf Another update to migration steps. 2013-11-07 20:33:51 +06:00
57765e88c0 Update migration steps with mailhook.
This commit also tests the mailhook itself, if all goes well.
2013-11-07 20:33:51 +06:00
60cddad047 Whitespace cleanup 2013-11-07 20:33:51 +06:00
a77a33dc50 Rework gitosis config generator to use pushable settings from repository 2013-11-07 20:33:51 +06:00
405a07e638 Made 'pushable' of repository editable for non-hosted repos as well 2013-11-07 20:33:51 +06:00
75f9ce9d77 Missed this in previous 'revert changes' commit 2013-11-07 20:33:50 +06:00
259588e747 Added script which rebuilds gitadmin configuration
It generates new gitosis.conf and public key files.
Actual commit to repo would likely be done with a
wrapper script which will run in cron.
2013-11-07 20:33:50 +06:00
0e8b746fb0 Tweaks to page and mail titles
- Welcome page will now have title "Blender Foundation: Welcome",
  the same as in old projects.b.o.
- Mail will be sent from name of BF, not phabricator, seems to
  be quite logical to do.
2013-11-07 20:33:50 +06:00
753917c10c Revert changes in AphrontFormPolicyControl.php
After recent Brecht's commit they're not needed.

Euh, didn't notice policies could be edited in configuration :(
2013-11-07 20:33:50 +06:00
39f7364f25 Tweaks to maniphest
- Remove quick links to create tasks, now only
  "Report Bug" to a specified project is allowed.
- Added 'Addons' to list of 'always visible'
  in navigation bar projects.
2013-11-07 20:33:50 +06:00
8d7de5fcb6 Tasks: don't force particular policy in the code here, let it be defined by the
default policies configured for maniphest.
2013-11-07 20:33:50 +06:00
01398f762b Better looks and css for welcome.html. For reference, __celerity_resource_map__.php
was updated with this command.

./scripts/celerity_mapper.php --with-custom webroot
2013-11-07 20:33:50 +06:00
e12d2efe81 Add buildbot and developer wiki links in the menu. 2013-11-07 20:33:23 +06:00
5c7055d840 Reshuffle order of builtin queries 2013-11-07 20:33:23 +06:00
562c7aa126 Use blender.org's favicon 2013-11-07 20:33:22 +06:00
5994088e50 Tweaks to welcome and bug report pages, making them a bit prettier and hopefully
more clear.
2013-11-07 20:33:22 +06:00
9edbe22e19 Update welcome.html with correct project IDs. 2013-11-07 20:33:22 +06:00
7fabfff397 Update migration steps with info from test run of importing to developer.blender.org. 2013-11-07 20:33:22 +06:00
356a2fe0e8 Rework task submission form
- It is possible now to submit tasks to specified project.
- All the extra fields which are not interesting got us
  are now hidden.
- Default visibility is User (maybe we might want
  "Everyone" in fact)?
- Only Administrators are able to edit issues by
  default.
- Links to bug report are available from welcome page
  and from project's maniphest page.
- Added file with report bug guidelines. Currently
  just copy-pasted it from project.b.o, some style
  tweaks are welcome.
2013-11-07 20:33:22 +06:00
54193ebb15 Add link to all types of tasks within single project 2013-11-07 20:33:22 +06:00
2b4bb9cddd Highlight current task type in side menu
So now Bug,Patch,etc categories are always visible
and active one is being highlighted.
2013-11-07 20:33:22 +06:00
2758907517 Add some new duplicate email users for migration. 2013-11-07 20:33:22 +06:00
a7f641eee2 Allow public access to main page, with login button in the top right, and show
activity feed on the main page.
2013-11-07 20:33:22 +06:00
9799ceff80 More fixes for migration, in particular for setting policies on projects and tasks. 2013-11-07 20:33:22 +06:00
26e86c2171 Add missing files and minor migration tweaks. 2013-11-07 20:33:22 +06:00
f6c293fb4d Migration tweaks, and patch to temporarily disable sending mails and updating feeds
during import of data.
2013-11-07 20:33:22 +06:00
5abc5cf9f8 Lots of little fixes for import, status should be imported mostly OK now.
A dded a migration_steps.txt that outlines the steps to migrate data.

Still todo:
* Extensions/addons, skipped now, will do next week
* Hide some unnecessary transactions
* Figure out policies for tasks and projects
* Test run over all tasks to see that they work
2013-11-07 20:33:22 +06:00
41a820fdf0 Tweak appearance of projects page to show description more prominent, and
minor tweaks to closed status and welcome html margins.
2013-11-07 20:33:22 +06:00
2c8b09da21 Welcome page tweaks (links are mostly broken, but shows proposed structure). 2013-11-07 20:33:22 +06:00
a7ff526360 Migration: add system to warn users with merged accounts to use the other account,
and provide an option to change the username to their choice after login.
2013-11-07 20:33:21 +06:00
1a8148be97 Correct link to blender bug tracker 2013-11-07 20:33:21 +06:00
199dd85140 Show project and task type in query result header
This is intended to make it more clear which task
types are currently being displayed.

Needed to revoke "final" modifier from class
PhabricatorApplicationSearchController and make
it use separate method to get description for
this.
2013-11-07 20:33:21 +06:00
f6c44fdb03 Hide inactive projects and add button to show them all
For now uses stupid hardcoded list of "interesting"
projects. Might be changed to something smarter later.
2013-11-07 20:33:21 +06:00
1aade72cb1 Add extra filter level to filter tasks by type 2013-11-07 20:33:21 +06:00
121c9d308b Fix migration script error due to changed phab API. 2013-11-07 20:33:21 +06:00
d60a47ab5b Add migration/dump to gitignore so it's not commited by accident 2013-11-07 20:33:21 +06:00
8af9b8796f Fkx for syntax error, unexpected T_OBJECT_OPERATOR 2013-11-07 20:33:21 +06:00
7dd7673fb3 Added per-project categories to the side bar of maniphest
Currently adds all the projects there, and at this moment
current project is not being highlighted, it's only get
displayed on the top anchor bar.

TODO:
- We would probably wouldn't want all the projects there,
  need to add some filter there.
- Make it ore clear which project is currently active.
2013-11-07 20:33:21 +06:00
aeaf0902e1 Migration scripts in somewhat better state now, main missing part is that statuses
are not imported correctly, though there's a bunch of details to figure out.
2013-11-07 20:33:21 +06:00
498842475c Initial scripts for migration of data from gforge to phabricator.
Import of tasks is still a hacked together mess from before I figured out the
structure that we want.
2013-11-07 20:33:21 +06:00
755177508e Modify LiskDAO to support overriding ID and dates, used for import of data. 2013-11-07 20:33:21 +06:00
5b49fd93e2 Don't show edit button for tasks if editing is not allowed 2013-11-07 20:33:21 +06:00
968ea35b03 Use blender logo in header 2013-11-07 20:33:21 +06:00
97ed90b82a Fix for tiy typo in previous commit 2013-11-07 20:33:21 +06:00
ba34aa535a Move custom login form text to an html file
Also committing all the template files needed for
making phabricator closer to how our gforge looks.
2013-11-07 20:33:21 +06:00
e56041da3b Hide "Create Project" if it's not allowed for current user
Having a button which says "You are not allowed to press it"
is pretty much strange behavior. Better not to have such button
at first place.

For now it only affects Projects application. There're also
other places where we might want this change.
2013-11-07 20:33:20 +06:00
b19fbc39af Modification so we can import gforge passwords, by adding an extra md5 to the
password hash. By skipping this step on import, we keep passwords working.
2013-11-07 20:33:20 +06:00
1036d419fa Add "Archived" status for closing tasks. 2013-11-07 20:33:20 +06:00
0011789443 Added welcome.file setting
This setting operates quite the same as welcome.html
except for the difference that it points to a file
from which to get the HTML code.

It's more convenient to use file with HTML code if
one need to display rather long welcome screen.
2013-11-07 20:33:20 +06:00
ff7d71fb87 Make install_ubuntu.sh work for Debian as well 2013-11-07 20:33:20 +06:00
654 changed files with 12314 additions and 27458 deletions

View File

@@ -1,5 +1,5 @@
{
"phabricator.uri": "https://secure.phabricator.com/",
"phabricator.uri": "https://developer.blender.org/",
"load": ["src/"],
"history.immutable": false
}

View File

@@ -64,13 +64,13 @@
"text": {
"type": "text",
"exclude": [
"(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect))"
"(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json))"
]
},
"text-without-length": {
"type": "text",
"include": [
"(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect))"
"(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json))"
],
"severity": {
"3": "disabled"

3
.gitignore vendored
View File

@@ -40,3 +40,6 @@
# Places for users to add custom resources.
/resources/cows/custom/*
/resources/figlet/custom/*
# blender migration files
migration/dump/

722
externals/amazon-ses/ses.php vendored Normal file
View File

@@ -0,0 +1,722 @@
<?php
/**
*
* Copyright (c) 2011, Dan Myers.
* Parts copyright (c) 2008, Donovan Schonknecht.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* This is a modified BSD license (the third clause has been removed).
* The BSD license may be found here:
* http://www.opensource.org/licenses/bsd-license.php
*
* Amazon Simple Email Service is a trademark of Amazon.com, Inc. or its affiliates.
*
* SimpleEmailService is based on Donovan Schonknecht's Amazon S3 PHP class, found here:
* http://undesigned.org.za/2007/10/22/amazon-s3-php-class
*
*/
/**
* Amazon SimpleEmailService PHP class
*
* @link http://sourceforge.net/projects/php-aws-ses/
* version 0.8.1
*
*/
class SimpleEmailService
{
protected $__accessKey; // AWS Access key
protected $__secretKey; // AWS Secret key
protected $__host;
public function getAccessKey() { return $this->__accessKey; }
public function getSecretKey() { return $this->__secretKey; }
public function getHost() { return $this->__host; }
protected $__verifyHost = 1;
protected $__verifyPeer = 1;
// verifyHost and verifyPeer determine whether curl verifies ssl certificates.
// It may be necessary to disable these checks on certain systems.
// These only have an effect if SSL is enabled.
public function verifyHost() { return $this->__verifyHost; }
public function enableVerifyHost($enable = true) { $this->__verifyHost = $enable; }
public function verifyPeer() { return $this->__verifyPeer; }
public function enableVerifyPeer($enable = true) { $this->__verifyPeer = $enable; }
// If you use exceptions, errors will be communicated by throwing a
// SimpleEmailServiceException. By default, they will be trigger_error()'d.
protected $__useExceptions = 0;
public function useExceptions() { return $this->__useExceptions; }
public function enableUseExceptions($enable = true) { $this->__useExceptions = $enable; }
/**
* Constructor
*
* @param string $accessKey Access key
* @param string $secretKey Secret key
* @return void
*/
public function __construct($accessKey = null, $secretKey = null, $host = 'email.us-east-1.amazonaws.com') {
if (!function_exists('simplexml_load_string')) {
throw new Exception(
pht(
'The PHP SimpleXML extension is not available, but this '.
'extension is required to send mail via Amazon SES, because '.
'Amazon SES returns API responses in XML format. Install or '.
'enable the SimpleXML extension.'));
}
// Catch mistakes with reading the wrong column out of the SES
// documentation. See T10728.
if (preg_match('(-smtp)', $host)) {
throw new Exception(
pht(
'Amazon SES is not configured correctly: the configured SES '.
'endpoint ("%s") is an SMTP endpoint. Instead, use an API (HTTPS) '.
'endpoint.',
$host));
}
if ($accessKey !== null && $secretKey !== null) {
$this->setAuth($accessKey, $secretKey);
}
$this->__host = $host;
}
/**
* Set AWS access key and secret key
*
* @param string $accessKey Access key
* @param string $secretKey Secret key
* @return void
*/
public function setAuth($accessKey, $secretKey) {
$this->__accessKey = $accessKey;
$this->__secretKey = $secretKey;
}
/**
* Lists the email addresses that have been verified and can be used as the 'From' address
*
* @return An array containing two items: a list of verified email addresses, and the request id.
*/
public function listVerifiedEmailAddresses() {
$rest = new SimpleEmailServiceRequest($this, 'GET');
$rest->setParameter('Action', 'ListVerifiedEmailAddresses');
$rest = $rest->getResponse();
$response = array();
if(!isset($rest->body)) {
return $response;
}
$addresses = array();
foreach($rest->body->ListVerifiedEmailAddressesResult->VerifiedEmailAddresses->member as $address) {
$addresses[] = (string)$address;
}
$response['Addresses'] = $addresses;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Requests verification of the provided email address, so it can be used
* as the 'From' address when sending emails through SimpleEmailService.
*
* After submitting this request, you should receive a verification email
* from Amazon at the specified address containing instructions to follow.
*
* @param string email The email address to get verified
* @return The request id for this request.
*/
public function verifyEmailAddress($email) {
$rest = new SimpleEmailServiceRequest($this, 'POST');
$rest->setParameter('Action', 'VerifyEmailAddress');
$rest->setParameter('EmailAddress', $email);
$rest = $rest->getResponse();
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Removes the specified email address from the list of verified addresses.
*
* @param string email The email address to remove
* @return The request id for this request.
*/
public function deleteVerifiedEmailAddress($email) {
$rest = new SimpleEmailServiceRequest($this, 'DELETE');
$rest->setParameter('Action', 'DeleteVerifiedEmailAddress');
$rest->setParameter('EmailAddress', $email);
$rest = $rest->getResponse();
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Retrieves information on the current activity limits for this account.
* See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendQuota.html
*
* @return An array containing information on this account's activity limits.
*/
public function getSendQuota() {
$rest = new SimpleEmailServiceRequest($this, 'GET');
$rest->setParameter('Action', 'GetSendQuota');
$rest = $rest->getResponse();
$response = array();
if(!isset($rest->body)) {
return $response;
}
$response['Max24HourSend'] = (string)$rest->body->GetSendQuotaResult->Max24HourSend;
$response['MaxSendRate'] = (string)$rest->body->GetSendQuotaResult->MaxSendRate;
$response['SentLast24Hours'] = (string)$rest->body->GetSendQuotaResult->SentLast24Hours;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Retrieves statistics for the last two weeks of activity on this account.
* See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendStatistics.html
*
* @return An array of activity statistics. Each array item covers a 15-minute period.
*/
public function getSendStatistics() {
$rest = new SimpleEmailServiceRequest($this, 'GET');
$rest->setParameter('Action', 'GetSendStatistics');
$rest = $rest->getResponse();
$response = array();
if(!isset($rest->body)) {
return $response;
}
$datapoints = array();
foreach($rest->body->GetSendStatisticsResult->SendDataPoints->member as $datapoint) {
$p = array();
$p['Bounces'] = (string)$datapoint->Bounces;
$p['Complaints'] = (string)$datapoint->Complaints;
$p['DeliveryAttempts'] = (string)$datapoint->DeliveryAttempts;
$p['Rejects'] = (string)$datapoint->Rejects;
$p['Timestamp'] = (string)$datapoint->Timestamp;
$datapoints[] = $p;
}
$response['SendDataPoints'] = $datapoints;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
public function sendRawEmail($raw) {
$rest = new SimpleEmailServiceRequest($this, 'POST');
$rest->setParameter('Action', 'SendRawEmail');
$rest->setParameter('RawMessage.Data', base64_encode($raw));
$rest = $rest->getResponse();
$response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Given a SimpleEmailServiceMessage object, submits the message to the service for sending.
*
* @return An array containing the unique identifier for this message and a separate request id.
* Returns false if the provided message is missing any required fields.
*/
public function sendEmail($sesMessage) {
if(!$sesMessage->validate()) {
return false;
}
$rest = new SimpleEmailServiceRequest($this, 'POST');
$rest->setParameter('Action', 'SendEmail');
$i = 1;
foreach($sesMessage->to as $to) {
$rest->setParameter('Destination.ToAddresses.member.'.$i, $to);
$i++;
}
if(is_array($sesMessage->cc)) {
$i = 1;
foreach($sesMessage->cc as $cc) {
$rest->setParameter('Destination.CcAddresses.member.'.$i, $cc);
$i++;
}
}
if(is_array($sesMessage->bcc)) {
$i = 1;
foreach($sesMessage->bcc as $bcc) {
$rest->setParameter('Destination.BccAddresses.member.'.$i, $bcc);
$i++;
}
}
if(is_array($sesMessage->replyto)) {
$i = 1;
foreach($sesMessage->replyto as $replyto) {
$rest->setParameter('ReplyToAddresses.member.'.$i, $replyto);
$i++;
}
}
$rest->setParameter('Source', $sesMessage->from);
if($sesMessage->returnpath != null) {
$rest->setParameter('ReturnPath', $sesMessage->returnpath);
}
if($sesMessage->subject != null && strlen($sesMessage->subject) > 0) {
$rest->setParameter('Message.Subject.Data', $sesMessage->subject);
if($sesMessage->subjectCharset != null && strlen($sesMessage->subjectCharset) > 0) {
$rest->setParameter('Message.Subject.Charset', $sesMessage->subjectCharset);
}
}
if($sesMessage->messagetext != null && strlen($sesMessage->messagetext) > 0) {
$rest->setParameter('Message.Body.Text.Data', $sesMessage->messagetext);
if($sesMessage->messageTextCharset != null && strlen($sesMessage->messageTextCharset) > 0) {
$rest->setParameter('Message.Body.Text.Charset', $sesMessage->messageTextCharset);
}
}
if($sesMessage->messagehtml != null && strlen($sesMessage->messagehtml) > 0) {
$rest->setParameter('Message.Body.Html.Data', $sesMessage->messagehtml);
if($sesMessage->messageHtmlCharset != null && strlen($sesMessage->messageHtmlCharset) > 0) {
$rest->setParameter('Message.Body.Html.Charset', $sesMessage->messageHtmlCharset);
}
}
$rest = $rest->getResponse();
$response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Trigger an error message
*
* @internal Used by member functions to output errors
* @param array $error Array containing error information
* @return string
*/
public function __triggerError($functionname, $error)
{
if($error == false) {
$message = sprintf("SimpleEmailService::%s(): Encountered an error, but no description given", $functionname);
}
else if(isset($error['curl']) && $error['curl'])
{
$message = sprintf("SimpleEmailService::%s(): %s %s", $functionname, $error['code'], $error['message']);
}
else if(isset($error['Error']))
{
$e = $error['Error'];
$message = sprintf("SimpleEmailService::%s(): %s - %s: %s\nRequest Id: %s\n", $functionname, $e['Type'], $e['Code'], $e['Message'], $error['RequestId']);
}
if ($this->useExceptions()) {
throw new SimpleEmailServiceException($message);
} else {
trigger_error($message, E_USER_WARNING);
}
}
/**
* Callback handler for 503 retries.
*
* @internal Used by SimpleDBRequest to call the user-specified callback, if set
* @param $attempt The number of failed attempts so far
* @return The retry delay in microseconds, or 0 to stop retrying.
*/
public function __executeServiceTemporarilyUnavailableRetryDelay($attempt)
{
if(is_callable($this->__serviceUnavailableRetryDelayCallback)) {
$callback = $this->__serviceUnavailableRetryDelayCallback;
return $callback($attempt);
}
return 0;
}
}
final class SimpleEmailServiceRequest
{
private $ses, $verb, $parameters = array();
public $response;
/**
* Constructor
*
* @param string $ses The SimpleEmailService object making this request
* @param string $action action
* @param string $verb HTTP verb
* @return mixed
*/
function __construct($ses, $verb) {
$this->ses = $ses;
$this->verb = $verb;
$this->response = new STDClass;
$this->response->error = false;
}
/**
* Set request parameter
*
* @param string $key Key
* @param string $value Value
* @param boolean $replace Whether to replace the key if it already exists (default true)
* @return void
*/
public function setParameter($key, $value, $replace = true) {
if(!$replace && isset($this->parameters[$key]))
{
$temp = (array)($this->parameters[$key]);
$temp[] = $value;
$this->parameters[$key] = $temp;
}
else
{
$this->parameters[$key] = $value;
}
}
/**
* Get the response
*
* @return object | false
*/
public function getResponse() {
$params = array();
foreach ($this->parameters as $var => $value)
{
if(is_array($value))
{
foreach($value as $v)
{
$params[] = $var.'='.$this->__customUrlEncode($v);
}
}
else
{
$params[] = $var.'='.$this->__customUrlEncode($value);
}
}
sort($params, SORT_STRING);
// must be in format 'Sun, 06 Nov 1994 08:49:37 GMT'
$date = gmdate('D, d M Y H:i:s e');
$query = implode('&', $params);
$headers = array();
$headers[] = 'Date: '.$date;
$headers[] = 'Host: '.$this->ses->getHost();
$auth = 'AWS3-HTTPS AWSAccessKeyId='.$this->ses->getAccessKey();
$auth .= ',Algorithm=HmacSHA256,Signature='.$this->__getSignature($date);
$headers[] = 'X-Amzn-Authorization: '.$auth;
$url = 'https://'.$this->ses->getHost().'/';
// Basic setup
$curl = curl_init();
curl_setopt($curl, CURLOPT_USERAGENT, 'SimpleEmailService/php');
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, ($this->ses->verifyHost() ? 2 : 0));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, ($this->ses->verifyPeer() ? 1 : 0));
// Request types
switch ($this->verb) {
case 'GET':
$url .= '?'.$query;
break;
case 'POST':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
curl_setopt($curl, CURLOPT_POSTFIELDS, $query);
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
break;
case 'DELETE':
$url .= '?'.$query;
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
break;
default: break;
}
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
// Execute, grab errors
if (!curl_exec($curl)) {
throw new SimpleEmailServiceException(
pht(
'Encountered an error while making an HTTP request to Amazon SES '.
'(cURL Error #%d): %s',
curl_errno($curl),
curl_error($curl)));
}
$this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($this->response->code != 200) {
throw new SimpleEmailServiceException(
pht(
'Unexpected HTTP status while making request to Amazon SES: '.
'expected 200, got %s.',
$this->response->code));
}
@curl_close($curl);
// Parse body into XML
if ($this->response->error === false && isset($this->response->body)) {
$this->response->body = simplexml_load_string($this->response->body);
// Grab SES errors
if (!in_array($this->response->code, array(200, 201, 202, 204))
&& isset($this->response->body->Error)) {
$error = $this->response->body->Error;
$output = array();
$output['curl'] = false;
$output['Error'] = array();
$output['Error']['Type'] = (string)$error->Type;
$output['Error']['Code'] = (string)$error->Code;
$output['Error']['Message'] = (string)$error->Message;
$output['RequestId'] = (string)$this->response->body->RequestId;
$this->response->error = $output;
unset($this->response->body);
}
}
return $this->response;
}
/**
* CURL write callback
*
* @param resource &$curl CURL resource
* @param string &$data Data
* @return integer
*/
private function __responseWriteCallback(&$curl, &$data) {
if(!isset($this->response->body)) $this->response->body = '';
$this->response->body .= $data;
return strlen($data);
}
/**
* Contributed by afx114
* URL encode the parameters as per http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?Query_QueryAuth.html
* PHP's rawurlencode() follows RFC 1738, not RFC 3986 as required by Amazon. The only difference is the tilde (~), so convert it back after rawurlencode
* See: http://www.morganney.com/blog/API/AWS-Product-Advertising-API-Requires-a-Signed-Request.php
*
* @param string $var String to encode
* @return string
*/
private function __customUrlEncode($var) {
return str_replace('%7E', '~', rawurlencode($var));
}
/**
* Generate the auth string using Hmac-SHA256
*
* @internal Used by SimpleDBRequest::getResponse()
* @param string $string String to sign
* @return string
*/
private function __getSignature($string) {
return base64_encode(hash_hmac('sha256', $string, $this->ses->getSecretKey(), true));
}
}
final class SimpleEmailServiceMessage {
// these are public for convenience only
// these are not to be used outside of the SimpleEmailService class!
public $to, $cc, $bcc, $replyto;
public $from, $returnpath;
public $subject, $messagetext, $messagehtml;
public $subjectCharset, $messageTextCharset, $messageHtmlCharset;
function __construct() {
$to = array();
$cc = array();
$bcc = array();
$replyto = array();
$from = null;
$returnpath = null;
$subject = null;
$messagetext = null;
$messagehtml = null;
$subjectCharset = null;
$messageTextCharset = null;
$messageHtmlCharset = null;
}
/**
* addTo, addCC, addBCC, and addReplyTo have the following behavior:
* If a single address is passed, it is appended to the current list of addresses.
* If an array of addresses is passed, that array is merged into the current list.
*/
function addTo($to) {
if(!is_array($to)) {
$this->to[] = $to;
}
else {
$this->to = array_merge($this->to, $to);
}
}
function addCC($cc) {
if(!is_array($cc)) {
$this->cc[] = $cc;
}
else {
$this->cc = array_merge($this->cc, $cc);
}
}
function addBCC($bcc) {
if(!is_array($bcc)) {
$this->bcc[] = $bcc;
}
else {
$this->bcc = array_merge($this->bcc, $bcc);
}
}
function addReplyTo($replyto) {
if(!is_array($replyto)) {
$this->replyto[] = $replyto;
}
else {
$this->replyto = array_merge($this->replyto, $replyto);
}
}
function setFrom($from) {
$this->from = $from;
}
function setReturnPath($returnpath) {
$this->returnpath = $returnpath;
}
function setSubject($subject) {
$this->subject = $subject;
}
function setSubjectCharset($charset) {
$this->subjectCharset = $charset;
}
function setMessageFromString($text, $html = null) {
$this->messagetext = $text;
$this->messagehtml = $html;
}
function setMessageFromFile($textfile, $htmlfile = null) {
if(file_exists($textfile) && is_file($textfile) && is_readable($textfile)) {
$this->messagetext = file_get_contents($textfile);
}
if(file_exists($htmlfile) && is_file($htmlfile) && is_readable($htmlfile)) {
$this->messagehtml = file_get_contents($htmlfile);
}
}
function setMessageFromURL($texturl, $htmlurl = null) {
$this->messagetext = file_get_contents($texturl);
if($htmlurl !== null) {
$this->messagehtml = file_get_contents($htmlurl);
}
}
function setMessageCharset($textCharset, $htmlCharset = null) {
$this->messageTextCharset = $textCharset;
$this->messageHtmlCharset = $htmlCharset;
}
/**
* Validates whether the message object has sufficient information to submit a request to SES.
* This does not guarantee the message will arrive, nor that the request will succeed;
* instead, it makes sure that no required fields are missing.
*
* This is used internally before attempting a SendEmail or SendRawEmail request,
* but it can be used outside of this file if verification is desired.
* May be useful if e.g. the data is being populated from a form; developers can generally
* use this function to verify completeness instead of writing custom logic.
*
* @return boolean
*/
public function validate() {
if(count($this->to) == 0)
return false;
if($this->from == null || strlen($this->from) == 0)
return false;
if($this->messagetext == null)
return false;
return true;
}
}
/**
* Thrown by SimpleEmailService when errors occur if you call
* enableUseExceptions(true).
*/
final class SimpleEmailServiceException extends Exception {
}

View File

@@ -1,769 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
<!--
Copyright © 1991-2013 Unicode, Inc.
CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
For terms of use, see http://www.unicode.org/copyright.html
-->
<supplementalData>
<version number="$Revision$"/>
<windowsZones>
<mapTimezones otherVersion="7e00402" typeVersion="2016i">
<!-- (UTC-12:00) International Date Line West -->
<mapZone other="Dateline Standard Time" territory="001" type="Etc/GMT+12"/>
<mapZone other="Dateline Standard Time" territory="ZZ" type="Etc/GMT+12"/>
<!-- (UTC-11:00) Coordinated Universal Time-11 -->
<mapZone other="UTC-11" territory="001" type="Etc/GMT+11"/>
<mapZone other="UTC-11" territory="AS" type="Pacific/Pago_Pago"/>
<mapZone other="UTC-11" territory="NU" type="Pacific/Niue"/>
<mapZone other="UTC-11" territory="UM" type="Pacific/Midway"/>
<mapZone other="UTC-11" territory="ZZ" type="Etc/GMT+11"/>
<!-- (UTC-10:00) Aleutian Islands -->
<mapZone other="Aleutian Standard Time" territory="001" type="America/Adak"/>
<mapZone other="Aleutian Standard Time" territory="US" type="America/Adak"/>
<!-- (UTC-10:00) Hawaii -->
<mapZone other="Hawaiian Standard Time" territory="001" type="Pacific/Honolulu"/>
<mapZone other="Hawaiian Standard Time" territory="CK" type="Pacific/Rarotonga"/>
<mapZone other="Hawaiian Standard Time" territory="PF" type="Pacific/Tahiti"/>
<mapZone other="Hawaiian Standard Time" territory="UM" type="Pacific/Johnston"/>
<mapZone other="Hawaiian Standard Time" territory="US" type="Pacific/Honolulu"/>
<mapZone other="Hawaiian Standard Time" territory="ZZ" type="Etc/GMT+10"/>
<!-- (UTC-09:30) Marquesas Islands -->
<mapZone other="Marquesas Standard Time" territory="001" type="Pacific/Marquesas"/>
<mapZone other="Marquesas Standard Time" territory="PF" type="Pacific/Marquesas"/>
<!-- (UTC-09:00) Alaska -->
<mapZone other="Alaskan Standard Time" territory="001" type="America/Anchorage"/>
<mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Metlakatla America/Nome America/Sitka America/Yakutat"/>
<!-- (UTC-09:00) Coordinated Universal Time-09 -->
<mapZone other="UTC-09" territory="001" type="Etc/GMT+9"/>
<mapZone other="UTC-09" territory="PF" type="Pacific/Gambier"/>
<mapZone other="UTC-09" territory="ZZ" type="Etc/GMT+9"/>
<!-- (UTC-08:00) Baja California -->
<mapZone other="Pacific Standard Time (Mexico)" territory="001" type="America/Tijuana"/>
<mapZone other="Pacific Standard Time (Mexico)" territory="MX" type="America/Tijuana America/Santa_Isabel"/>
<!-- (UTC-08:00) Coordinated Universal Time-08 -->
<mapZone other="UTC-08" territory="001" type="Etc/GMT+8"/>
<mapZone other="UTC-08" territory="PN" type="Pacific/Pitcairn"/>
<mapZone other="UTC-08" territory="ZZ" type="Etc/GMT+8"/>
<!-- (UTC-08:00) Pacific Time (US & Canada) -->
<mapZone other="Pacific Standard Time" territory="001" type="America/Los_Angeles"/>
<mapZone other="Pacific Standard Time" territory="CA" type="America/Vancouver America/Dawson America/Whitehorse"/>
<mapZone other="Pacific Standard Time" territory="US" type="America/Los_Angeles"/>
<mapZone other="Pacific Standard Time" territory="ZZ" type="PST8PDT"/>
<!-- (UTC-07:00) Arizona -->
<mapZone other="US Mountain Standard Time" territory="001" type="America/Phoenix"/>
<mapZone other="US Mountain Standard Time" territory="CA" type="America/Dawson_Creek America/Creston America/Fort_Nelson"/>
<mapZone other="US Mountain Standard Time" territory="MX" type="America/Hermosillo"/>
<mapZone other="US Mountain Standard Time" territory="US" type="America/Phoenix"/>
<mapZone other="US Mountain Standard Time" territory="ZZ" type="Etc/GMT+7"/>
<!-- (UTC-07:00) Chihuahua, La Paz, Mazatlan -->
<mapZone other="Mountain Standard Time (Mexico)" territory="001" type="America/Chihuahua"/>
<mapZone other="Mountain Standard Time (Mexico)" territory="MX" type="America/Chihuahua America/Mazatlan"/>
<!-- (UTC-07:00) Mountain Time (US & Canada) -->
<mapZone other="Mountain Standard Time" territory="001" type="America/Denver"/>
<mapZone other="Mountain Standard Time" territory="CA" type="America/Edmonton America/Cambridge_Bay America/Inuvik America/Yellowknife"/>
<mapZone other="Mountain Standard Time" territory="MX" type="America/Ojinaga"/>
<mapZone other="Mountain Standard Time" territory="US" type="America/Denver America/Boise"/>
<mapZone other="Mountain Standard Time" territory="ZZ" type="MST7MDT"/>
<!-- (UTC-06:00) Central America -->
<mapZone other="Central America Standard Time" territory="001" type="America/Guatemala"/>
<mapZone other="Central America Standard Time" territory="BZ" type="America/Belize"/>
<mapZone other="Central America Standard Time" territory="CR" type="America/Costa_Rica"/>
<mapZone other="Central America Standard Time" territory="EC" type="Pacific/Galapagos"/>
<mapZone other="Central America Standard Time" territory="GT" type="America/Guatemala"/>
<mapZone other="Central America Standard Time" territory="HN" type="America/Tegucigalpa"/>
<mapZone other="Central America Standard Time" territory="NI" type="America/Managua"/>
<mapZone other="Central America Standard Time" territory="SV" type="America/El_Salvador"/>
<mapZone other="Central America Standard Time" territory="ZZ" type="Etc/GMT+6"/>
<!-- (UTC-06:00) Central Time (US & Canada) -->
<mapZone other="Central Standard Time" territory="001" type="America/Chicago"/>
<mapZone other="Central Standard Time" territory="CA" type="America/Winnipeg America/Rainy_River America/Rankin_Inlet America/Resolute"/>
<mapZone other="Central Standard Time" territory="MX" type="America/Matamoros"/>
<mapZone other="Central Standard Time" territory="US" type="America/Chicago America/Indiana/Knox America/Indiana/Tell_City America/Menominee America/North_Dakota/Beulah America/North_Dakota/Center America/North_Dakota/New_Salem"/>
<mapZone other="Central Standard Time" territory="ZZ" type="CST6CDT"/>
<!-- (UTC-06:00) Easter Island -->
<mapZone other="Easter Island Standard Time" territory="001" type="Pacific/Easter"/>
<mapZone other="Easter Island Standard Time" territory="CL" type="Pacific/Easter"/>
<!-- (UTC-06:00) Guadalajara, Mexico City, Monterrey -->
<mapZone other="Central Standard Time (Mexico)" territory="001" type="America/Mexico_City"/>
<mapZone other="Central Standard Time (Mexico)" territory="MX" type="America/Mexico_City America/Bahia_Banderas America/Merida America/Monterrey"/>
<!-- (UTC-06:00) Saskatchewan -->
<mapZone other="Canada Central Standard Time" territory="001" type="America/Regina"/>
<mapZone other="Canada Central Standard Time" territory="CA" type="America/Regina America/Swift_Current"/>
<!-- (UTC-05:00) Bogota, Lima, Quito, Rio Branco -->
<mapZone other="SA Pacific Standard Time" territory="001" type="America/Bogota"/>
<mapZone other="SA Pacific Standard Time" territory="BR" type="America/Rio_Branco America/Eirunepe"/>
<mapZone other="SA Pacific Standard Time" territory="CA" type="America/Coral_Harbour"/>
<mapZone other="SA Pacific Standard Time" territory="CO" type="America/Bogota"/>
<mapZone other="SA Pacific Standard Time" territory="EC" type="America/Guayaquil"/>
<mapZone other="SA Pacific Standard Time" territory="JM" type="America/Jamaica"/>
<mapZone other="SA Pacific Standard Time" territory="KY" type="America/Cayman"/>
<mapZone other="SA Pacific Standard Time" territory="PA" type="America/Panama"/>
<mapZone other="SA Pacific Standard Time" territory="PE" type="America/Lima"/>
<mapZone other="SA Pacific Standard Time" territory="ZZ" type="Etc/GMT+5"/>
<!-- (UTC-05:00) Chetumal -->
<mapZone other="Eastern Standard Time (Mexico)" territory="001" type="America/Cancun"/>
<mapZone other="Eastern Standard Time (Mexico)" territory="MX" type="America/Cancun"/>
<!-- (UTC-05:00) Eastern Time (US & Canada) -->
<mapZone other="Eastern Standard Time" territory="001" type="America/New_York"/>
<mapZone other="Eastern Standard Time" territory="BS" type="America/Nassau"/>
<mapZone other="Eastern Standard Time" territory="CA" type="America/Toronto America/Iqaluit America/Montreal America/Nipigon America/Pangnirtung America/Thunder_Bay"/>
<mapZone other="Eastern Standard Time" territory="US" type="America/New_York America/Detroit America/Indiana/Petersburg America/Indiana/Vincennes America/Indiana/Winamac America/Kentucky/Monticello America/Louisville"/>
<mapZone other="Eastern Standard Time" territory="ZZ" type="EST5EDT"/>
<!-- (UTC-05:00) Haiti -->
<mapZone other="Haiti Standard Time" territory="001" type="America/Port-au-Prince"/>
<mapZone other="Haiti Standard Time" territory="HT" type="America/Port-au-Prince"/>
<!-- (UTC-05:00) Havana -->
<mapZone other="Cuba Standard Time" territory="001" type="America/Havana"/>
<mapZone other="Cuba Standard Time" territory="CU" type="America/Havana"/>
<!-- (UTC-05:00) Indiana (East) -->
<mapZone other="US Eastern Standard Time" territory="001" type="America/Indianapolis"/>
<mapZone other="US Eastern Standard Time" territory="US" type="America/Indianapolis America/Indiana/Marengo America/Indiana/Vevay"/>
<!-- (UTC-04:00) Asuncion -->
<mapZone other="Paraguay Standard Time" territory="001" type="America/Asuncion"/>
<mapZone other="Paraguay Standard Time" territory="PY" type="America/Asuncion"/>
<!-- (UTC-04:00) Atlantic Time (Canada) -->
<mapZone other="Atlantic Standard Time" territory="001" type="America/Halifax"/>
<mapZone other="Atlantic Standard Time" territory="BM" type="Atlantic/Bermuda"/>
<mapZone other="Atlantic Standard Time" territory="CA" type="America/Halifax America/Glace_Bay America/Goose_Bay America/Moncton"/>
<mapZone other="Atlantic Standard Time" territory="GL" type="America/Thule"/>
<!-- (UTC-04:00) Caracas -->
<mapZone other="Venezuela Standard Time" territory="001" type="America/Caracas"/>
<mapZone other="Venezuela Standard Time" territory="VE" type="America/Caracas"/>
<!-- (UTC-04:00) Cuiaba -->
<mapZone other="Central Brazilian Standard Time" territory="001" type="America/Cuiaba"/>
<mapZone other="Central Brazilian Standard Time" territory="BR" type="America/Cuiaba America/Campo_Grande"/>
<!-- (UTC-04:00) Georgetown, La Paz, Manaus, San Juan -->
<mapZone other="SA Western Standard Time" territory="001" type="America/La_Paz"/>
<mapZone other="SA Western Standard Time" territory="AG" type="America/Antigua"/>
<mapZone other="SA Western Standard Time" territory="AI" type="America/Anguilla"/>
<mapZone other="SA Western Standard Time" territory="AW" type="America/Aruba"/>
<mapZone other="SA Western Standard Time" territory="BB" type="America/Barbados"/>
<mapZone other="SA Western Standard Time" territory="BL" type="America/St_Barthelemy"/>
<mapZone other="SA Western Standard Time" territory="BO" type="America/La_Paz"/>
<mapZone other="SA Western Standard Time" territory="BQ" type="America/Kralendijk"/>
<mapZone other="SA Western Standard Time" territory="BR" type="America/Manaus America/Boa_Vista America/Porto_Velho"/>
<mapZone other="SA Western Standard Time" territory="CA" type="America/Blanc-Sablon"/>
<mapZone other="SA Western Standard Time" territory="CW" type="America/Curacao"/>
<mapZone other="SA Western Standard Time" territory="DM" type="America/Dominica"/>
<mapZone other="SA Western Standard Time" territory="DO" type="America/Santo_Domingo"/>
<mapZone other="SA Western Standard Time" territory="GD" type="America/Grenada"/>
<mapZone other="SA Western Standard Time" territory="GP" type="America/Guadeloupe"/>
<mapZone other="SA Western Standard Time" territory="GY" type="America/Guyana"/>
<mapZone other="SA Western Standard Time" territory="KN" type="America/St_Kitts"/>
<mapZone other="SA Western Standard Time" territory="LC" type="America/St_Lucia"/>
<mapZone other="SA Western Standard Time" territory="MF" type="America/Marigot"/>
<mapZone other="SA Western Standard Time" territory="MQ" type="America/Martinique"/>
<mapZone other="SA Western Standard Time" territory="MS" type="America/Montserrat"/>
<mapZone other="SA Western Standard Time" territory="PR" type="America/Puerto_Rico"/>
<mapZone other="SA Western Standard Time" territory="SX" type="America/Lower_Princes"/>
<mapZone other="SA Western Standard Time" territory="TT" type="America/Port_of_Spain"/>
<mapZone other="SA Western Standard Time" territory="VC" type="America/St_Vincent"/>
<mapZone other="SA Western Standard Time" territory="VG" type="America/Tortola"/>
<mapZone other="SA Western Standard Time" territory="VI" type="America/St_Thomas"/>
<mapZone other="SA Western Standard Time" territory="ZZ" type="Etc/GMT+4"/>
<!-- (UTC-04:00) Santiago -->
<mapZone other="Pacific SA Standard Time" territory="001" type="America/Santiago"/>
<mapZone other="Pacific SA Standard Time" territory="AQ" type="Antarctica/Palmer"/>
<mapZone other="Pacific SA Standard Time" territory="CL" type="America/Santiago"/>
<!-- (UTC-04:00) Turks and Caicos -->
<mapZone other="Turks And Caicos Standard Time" territory="001" type="America/Grand_Turk"/>
<mapZone other="Turks And Caicos Standard Time" territory="TC" type="America/Grand_Turk"/>
<!-- (UTC-03:30) Newfoundland -->
<mapZone other="Newfoundland Standard Time" territory="001" type="America/St_Johns"/>
<mapZone other="Newfoundland Standard Time" territory="CA" type="America/St_Johns"/>
<!-- (UTC-03:00) Araguaina -->
<mapZone other="Tocantins Standard Time" territory="001" type="America/Araguaina"/>
<mapZone other="Tocantins Standard Time" territory="BR" type="America/Araguaina"/>
<!-- (UTC-03:00) Brasilia -->
<mapZone other="E. South America Standard Time" territory="001" type="America/Sao_Paulo"/>
<mapZone other="E. South America Standard Time" territory="BR" type="America/Sao_Paulo"/>
<!-- (UTC-03:00) Cayenne, Fortaleza -->
<mapZone other="SA Eastern Standard Time" territory="001" type="America/Cayenne"/>
<mapZone other="SA Eastern Standard Time" territory="AQ" type="Antarctica/Rothera"/>
<mapZone other="SA Eastern Standard Time" territory="BR" type="America/Fortaleza America/Belem America/Maceio America/Recife America/Santarem"/>
<mapZone other="SA Eastern Standard Time" territory="FK" type="Atlantic/Stanley"/>
<mapZone other="SA Eastern Standard Time" territory="GF" type="America/Cayenne"/>
<mapZone other="SA Eastern Standard Time" territory="SR" type="America/Paramaribo"/>
<mapZone other="SA Eastern Standard Time" territory="ZZ" type="Etc/GMT+3"/>
<!-- (UTC-03:00) City of Buenos Aires -->
<mapZone other="Argentina Standard Time" territory="001" type="America/Buenos_Aires"/>
<mapZone other="Argentina Standard Time" territory="AR" type="America/Buenos_Aires America/Argentina/La_Rioja America/Argentina/Rio_Gallegos America/Argentina/Salta America/Argentina/San_Juan America/Argentina/San_Luis America/Argentina/Tucuman America/Argentina/Ushuaia America/Catamarca America/Cordoba America/Jujuy America/Mendoza"/>
<!-- (UTC-03:00) Greenland -->
<mapZone other="Greenland Standard Time" territory="001" type="America/Godthab"/>
<mapZone other="Greenland Standard Time" territory="GL" type="America/Godthab"/>
<!-- (UTC-03:00) Montevideo -->
<mapZone other="Montevideo Standard Time" territory="001" type="America/Montevideo"/>
<mapZone other="Montevideo Standard Time" territory="UY" type="America/Montevideo"/>
<!-- (UTC-03:00) Saint Pierre and Miquelon -->
<mapZone other="Saint Pierre Standard Time" territory="001" type="America/Miquelon"/>
<mapZone other="Saint Pierre Standard Time" territory="PM" type="America/Miquelon"/>
<!-- (UTC-03:00) Salvador -->
<mapZone other="Bahia Standard Time" territory="001" type="America/Bahia"/>
<mapZone other="Bahia Standard Time" territory="BR" type="America/Bahia"/>
<!-- (UTC-02:00) Coordinated Universal Time-02 -->
<mapZone other="UTC-02" territory="001" type="Etc/GMT+2"/>
<mapZone other="UTC-02" territory="BR" type="America/Noronha"/>
<mapZone other="UTC-02" territory="GS" type="Atlantic/South_Georgia"/>
<mapZone other="UTC-02" territory="ZZ" type="Etc/GMT+2"/>
<!-- (UTC-01:00) Azores -->
<mapZone other="Azores Standard Time" territory="001" type="Atlantic/Azores"/>
<mapZone other="Azores Standard Time" territory="GL" type="America/Scoresbysund"/>
<mapZone other="Azores Standard Time" territory="PT" type="Atlantic/Azores"/>
<!-- (UTC-01:00) Cabo Verde Is. -->
<mapZone other="Cape Verde Standard Time" territory="001" type="Atlantic/Cape_Verde"/>
<mapZone other="Cape Verde Standard Time" territory="CV" type="Atlantic/Cape_Verde"/>
<mapZone other="Cape Verde Standard Time" territory="ZZ" type="Etc/GMT+1"/>
<!-- (UTC) Coordinated Universal Time -->
<mapZone other="UTC" territory="001" type="Etc/GMT"/>
<mapZone other="UTC" territory="GL" type="America/Danmarkshavn"/>
<mapZone other="UTC" territory="ZZ" type="Etc/GMT"/>
<!-- (UTC+00:00) Casablanca -->
<mapZone other="Morocco Standard Time" territory="001" type="Africa/Casablanca"/>
<mapZone other="Morocco Standard Time" territory="EH" type="Africa/El_Aaiun"/>
<mapZone other="Morocco Standard Time" territory="MA" type="Africa/Casablanca"/>
<!-- (UTC+00:00) Dublin, Edinburgh, Lisbon, London -->
<mapZone other="GMT Standard Time" territory="001" type="Europe/London"/>
<mapZone other="GMT Standard Time" territory="ES" type="Atlantic/Canary"/>
<mapZone other="GMT Standard Time" territory="FO" type="Atlantic/Faeroe"/>
<mapZone other="GMT Standard Time" territory="GB" type="Europe/London"/>
<mapZone other="GMT Standard Time" territory="GG" type="Europe/Guernsey"/>
<mapZone other="GMT Standard Time" territory="IE" type="Europe/Dublin"/>
<mapZone other="GMT Standard Time" territory="IM" type="Europe/Isle_of_Man"/>
<mapZone other="GMT Standard Time" territory="JE" type="Europe/Jersey"/>
<mapZone other="GMT Standard Time" territory="PT" type="Europe/Lisbon Atlantic/Madeira"/>
<!-- (UTC+00:00) Monrovia, Reykjavik -->
<mapZone other="Greenwich Standard Time" territory="001" type="Atlantic/Reykjavik"/>
<mapZone other="Greenwich Standard Time" territory="BF" type="Africa/Ouagadougou"/>
<mapZone other="Greenwich Standard Time" territory="CI" type="Africa/Abidjan"/>
<mapZone other="Greenwich Standard Time" territory="GH" type="Africa/Accra"/>
<mapZone other="Greenwich Standard Time" territory="GM" type="Africa/Banjul"/>
<mapZone other="Greenwich Standard Time" territory="GN" type="Africa/Conakry"/>
<mapZone other="Greenwich Standard Time" territory="GW" type="Africa/Bissau"/>
<mapZone other="Greenwich Standard Time" territory="IS" type="Atlantic/Reykjavik"/>
<mapZone other="Greenwich Standard Time" territory="LR" type="Africa/Monrovia"/>
<mapZone other="Greenwich Standard Time" territory="ML" type="Africa/Bamako"/>
<mapZone other="Greenwich Standard Time" territory="MR" type="Africa/Nouakchott"/>
<mapZone other="Greenwich Standard Time" territory="SH" type="Atlantic/St_Helena"/>
<mapZone other="Greenwich Standard Time" territory="SL" type="Africa/Freetown"/>
<mapZone other="Greenwich Standard Time" territory="SN" type="Africa/Dakar"/>
<mapZone other="Greenwich Standard Time" territory="ST" type="Africa/Sao_Tome"/>
<mapZone other="Greenwich Standard Time" territory="TG" type="Africa/Lome"/>
<!-- (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna -->
<mapZone other="W. Europe Standard Time" territory="001" type="Europe/Berlin"/>
<mapZone other="W. Europe Standard Time" territory="AD" type="Europe/Andorra"/>
<mapZone other="W. Europe Standard Time" territory="AT" type="Europe/Vienna"/>
<mapZone other="W. Europe Standard Time" territory="CH" type="Europe/Zurich"/>
<mapZone other="W. Europe Standard Time" territory="DE" type="Europe/Berlin Europe/Busingen"/>
<mapZone other="W. Europe Standard Time" territory="GI" type="Europe/Gibraltar"/>
<mapZone other="W. Europe Standard Time" territory="IT" type="Europe/Rome"/>
<mapZone other="W. Europe Standard Time" territory="LI" type="Europe/Vaduz"/>
<mapZone other="W. Europe Standard Time" territory="LU" type="Europe/Luxembourg"/>
<mapZone other="W. Europe Standard Time" territory="MC" type="Europe/Monaco"/>
<mapZone other="W. Europe Standard Time" territory="MT" type="Europe/Malta"/>
<mapZone other="W. Europe Standard Time" territory="NL" type="Europe/Amsterdam"/>
<mapZone other="W. Europe Standard Time" territory="NO" type="Europe/Oslo"/>
<mapZone other="W. Europe Standard Time" territory="SE" type="Europe/Stockholm"/>
<mapZone other="W. Europe Standard Time" territory="SJ" type="Arctic/Longyearbyen"/>
<mapZone other="W. Europe Standard Time" territory="SM" type="Europe/San_Marino"/>
<mapZone other="W. Europe Standard Time" territory="VA" type="Europe/Vatican"/>
<!-- (UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague -->
<mapZone other="Central Europe Standard Time" territory="001" type="Europe/Budapest"/>
<mapZone other="Central Europe Standard Time" territory="AL" type="Europe/Tirane"/>
<mapZone other="Central Europe Standard Time" territory="CZ" type="Europe/Prague"/>
<mapZone other="Central Europe Standard Time" territory="HU" type="Europe/Budapest"/>
<mapZone other="Central Europe Standard Time" territory="ME" type="Europe/Podgorica"/>
<mapZone other="Central Europe Standard Time" territory="RS" type="Europe/Belgrade"/>
<mapZone other="Central Europe Standard Time" territory="SI" type="Europe/Ljubljana"/>
<mapZone other="Central Europe Standard Time" territory="SK" type="Europe/Bratislava"/>
<!-- (UTC+01:00) Brussels, Copenhagen, Madrid, Paris -->
<mapZone other="Romance Standard Time" territory="001" type="Europe/Paris"/>
<mapZone other="Romance Standard Time" territory="BE" type="Europe/Brussels"/>
<mapZone other="Romance Standard Time" territory="DK" type="Europe/Copenhagen"/>
<mapZone other="Romance Standard Time" territory="ES" type="Europe/Madrid Africa/Ceuta"/>
<mapZone other="Romance Standard Time" territory="FR" type="Europe/Paris"/>
<!-- (UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb -->
<mapZone other="Central European Standard Time" territory="001" type="Europe/Warsaw"/>
<mapZone other="Central European Standard Time" territory="BA" type="Europe/Sarajevo"/>
<mapZone other="Central European Standard Time" territory="HR" type="Europe/Zagreb"/>
<mapZone other="Central European Standard Time" territory="MK" type="Europe/Skopje"/>
<mapZone other="Central European Standard Time" territory="PL" type="Europe/Warsaw"/>
<!-- (UTC+01:00) West Central Africa -->
<mapZone other="W. Central Africa Standard Time" territory="001" type="Africa/Lagos"/>
<mapZone other="W. Central Africa Standard Time" territory="AO" type="Africa/Luanda"/>
<mapZone other="W. Central Africa Standard Time" territory="BJ" type="Africa/Porto-Novo"/>
<mapZone other="W. Central Africa Standard Time" territory="CD" type="Africa/Kinshasa"/>
<mapZone other="W. Central Africa Standard Time" territory="CF" type="Africa/Bangui"/>
<mapZone other="W. Central Africa Standard Time" territory="CG" type="Africa/Brazzaville"/>
<mapZone other="W. Central Africa Standard Time" territory="CM" type="Africa/Douala"/>
<mapZone other="W. Central Africa Standard Time" territory="DZ" type="Africa/Algiers"/>
<mapZone other="W. Central Africa Standard Time" territory="GA" type="Africa/Libreville"/>
<mapZone other="W. Central Africa Standard Time" territory="GQ" type="Africa/Malabo"/>
<mapZone other="W. Central Africa Standard Time" territory="NE" type="Africa/Niamey"/>
<mapZone other="W. Central Africa Standard Time" territory="NG" type="Africa/Lagos"/>
<mapZone other="W. Central Africa Standard Time" territory="TD" type="Africa/Ndjamena"/>
<mapZone other="W. Central Africa Standard Time" territory="TN" type="Africa/Tunis"/>
<mapZone other="W. Central Africa Standard Time" territory="ZZ" type="Etc/GMT-1"/>
<!-- (UTC+01:00) Windhoek -->
<mapZone other="Namibia Standard Time" territory="001" type="Africa/Windhoek"/>
<mapZone other="Namibia Standard Time" territory="NA" type="Africa/Windhoek"/>
<!-- (UTC+02:00) Amman -->
<mapZone other="Jordan Standard Time" territory="001" type="Asia/Amman"/>
<mapZone other="Jordan Standard Time" territory="JO" type="Asia/Amman"/>
<!-- (UTC+02:00) Athens, Bucharest -->
<mapZone other="GTB Standard Time" territory="001" type="Europe/Bucharest"/>
<mapZone other="GTB Standard Time" territory="CY" type="Asia/Nicosia"/>
<mapZone other="GTB Standard Time" territory="GR" type="Europe/Athens"/>
<mapZone other="GTB Standard Time" territory="RO" type="Europe/Bucharest"/>
<!-- (UTC+02:00) Beirut -->
<mapZone other="Middle East Standard Time" territory="001" type="Asia/Beirut"/>
<mapZone other="Middle East Standard Time" territory="LB" type="Asia/Beirut"/>
<!-- (UTC+02:00) Cairo -->
<mapZone other="Egypt Standard Time" territory="001" type="Africa/Cairo"/>
<mapZone other="Egypt Standard Time" territory="EG" type="Africa/Cairo"/>
<!-- (UTC+02:00) Chisinau -->
<mapZone other="E. Europe Standard Time" territory="001" type="Europe/Chisinau"/>
<mapZone other="E. Europe Standard Time" territory="MD" type="Europe/Chisinau"/>
<!-- (UTC+02:00) Damascus -->
<mapZone other="Syria Standard Time" territory="001" type="Asia/Damascus"/>
<mapZone other="Syria Standard Time" territory="SY" type="Asia/Damascus"/>
<!-- (UTC+02:00) Gaza, Hebron -->
<mapZone other="West Bank Standard Time" territory="001" type="Asia/Hebron"/>
<mapZone other="West Bank Standard Time" territory="PS" type="Asia/Hebron Asia/Gaza"/>
<!-- (UTC+02:00) Harare, Pretoria -->
<mapZone other="South Africa Standard Time" territory="001" type="Africa/Johannesburg"/>
<mapZone other="South Africa Standard Time" territory="BI" type="Africa/Bujumbura"/>
<mapZone other="South Africa Standard Time" territory="BW" type="Africa/Gaborone"/>
<mapZone other="South Africa Standard Time" territory="CD" type="Africa/Lubumbashi"/>
<mapZone other="South Africa Standard Time" territory="LS" type="Africa/Maseru"/>
<mapZone other="South Africa Standard Time" territory="MW" type="Africa/Blantyre"/>
<mapZone other="South Africa Standard Time" territory="MZ" type="Africa/Maputo"/>
<mapZone other="South Africa Standard Time" territory="RW" type="Africa/Kigali"/>
<mapZone other="South Africa Standard Time" territory="SZ" type="Africa/Mbabane"/>
<mapZone other="South Africa Standard Time" territory="ZA" type="Africa/Johannesburg"/>
<mapZone other="South Africa Standard Time" territory="ZM" type="Africa/Lusaka"/>
<mapZone other="South Africa Standard Time" territory="ZW" type="Africa/Harare"/>
<mapZone other="South Africa Standard Time" territory="ZZ" type="Etc/GMT-2"/>
<!-- (UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius -->
<mapZone other="FLE Standard Time" territory="001" type="Europe/Kiev"/>
<mapZone other="FLE Standard Time" territory="AX" type="Europe/Mariehamn"/>
<mapZone other="FLE Standard Time" territory="BG" type="Europe/Sofia"/>
<mapZone other="FLE Standard Time" territory="EE" type="Europe/Tallinn"/>
<mapZone other="FLE Standard Time" territory="FI" type="Europe/Helsinki"/>
<mapZone other="FLE Standard Time" territory="LT" type="Europe/Vilnius"/>
<mapZone other="FLE Standard Time" territory="LV" type="Europe/Riga"/>
<mapZone other="FLE Standard Time" territory="UA" type="Europe/Kiev Europe/Uzhgorod Europe/Zaporozhye"/>
<!-- (UTC+02:00) Istanbul -->
<mapZone other="Turkey Standard Time" territory="001" type="Europe/Istanbul"/>
<mapZone other="Turkey Standard Time" territory="TR" type="Europe/Istanbul"/>
<!-- (UTC+02:00) Jerusalem -->
<mapZone other="Israel Standard Time" territory="001" type="Asia/Jerusalem"/>
<mapZone other="Israel Standard Time" territory="IL" type="Asia/Jerusalem"/>
<!-- (UTC+02:00) Kaliningrad -->
<mapZone other="Kaliningrad Standard Time" territory="001" type="Europe/Kaliningrad"/>
<mapZone other="Kaliningrad Standard Time" territory="RU" type="Europe/Kaliningrad"/>
<!-- (UTC+02:00) Tripoli -->
<mapZone other="Libya Standard Time" territory="001" type="Africa/Tripoli"/>
<mapZone other="Libya Standard Time" territory="LY" type="Africa/Tripoli"/>
<!-- (UTC+03:00) Baghdad -->
<mapZone other="Arabic Standard Time" territory="001" type="Asia/Baghdad"/>
<mapZone other="Arabic Standard Time" territory="IQ" type="Asia/Baghdad"/>
<!-- (UTC+03:00) Kuwait, Riyadh -->
<mapZone other="Arab Standard Time" territory="001" type="Asia/Riyadh"/>
<mapZone other="Arab Standard Time" territory="BH" type="Asia/Bahrain"/>
<mapZone other="Arab Standard Time" territory="KW" type="Asia/Kuwait"/>
<mapZone other="Arab Standard Time" territory="QA" type="Asia/Qatar"/>
<mapZone other="Arab Standard Time" territory="SA" type="Asia/Riyadh"/>
<mapZone other="Arab Standard Time" territory="YE" type="Asia/Aden"/>
<!-- (UTC+03:00) Minsk -->
<mapZone other="Belarus Standard Time" territory="001" type="Europe/Minsk"/>
<mapZone other="Belarus Standard Time" territory="BY" type="Europe/Minsk"/>
<!-- (UTC+03:00) Moscow, St. Petersburg, Volgograd -->
<mapZone other="Russian Standard Time" territory="001" type="Europe/Moscow"/>
<mapZone other="Russian Standard Time" territory="RU" type="Europe/Moscow Europe/Kirov Europe/Volgograd"/>
<mapZone other="Russian Standard Time" territory="UA" type="Europe/Simferopol"/>
<!-- (UTC+03:00) Nairobi -->
<mapZone other="E. Africa Standard Time" territory="001" type="Africa/Nairobi"/>
<mapZone other="E. Africa Standard Time" territory="AQ" type="Antarctica/Syowa"/>
<mapZone other="E. Africa Standard Time" territory="DJ" type="Africa/Djibouti"/>
<mapZone other="E. Africa Standard Time" territory="ER" type="Africa/Asmera"/>
<mapZone other="E. Africa Standard Time" territory="ET" type="Africa/Addis_Ababa"/>
<mapZone other="E. Africa Standard Time" territory="KE" type="Africa/Nairobi"/>
<mapZone other="E. Africa Standard Time" territory="KM" type="Indian/Comoro"/>
<mapZone other="E. Africa Standard Time" territory="MG" type="Indian/Antananarivo"/>
<mapZone other="E. Africa Standard Time" territory="SD" type="Africa/Khartoum"/>
<mapZone other="E. Africa Standard Time" territory="SO" type="Africa/Mogadishu"/>
<mapZone other="E. Africa Standard Time" territory="SS" type="Africa/Juba"/>
<mapZone other="E. Africa Standard Time" territory="TZ" type="Africa/Dar_es_Salaam"/>
<mapZone other="E. Africa Standard Time" territory="UG" type="Africa/Kampala"/>
<mapZone other="E. Africa Standard Time" territory="YT" type="Indian/Mayotte"/>
<mapZone other="E. Africa Standard Time" territory="ZZ" type="Etc/GMT-3"/>
<!-- (UTC+03:30) Tehran -->
<mapZone other="Iran Standard Time" territory="001" type="Asia/Tehran"/>
<mapZone other="Iran Standard Time" territory="IR" type="Asia/Tehran"/>
<!-- (UTC+04:00) Abu Dhabi, Muscat -->
<mapZone other="Arabian Standard Time" territory="001" type="Asia/Dubai"/>
<mapZone other="Arabian Standard Time" territory="AE" type="Asia/Dubai"/>
<mapZone other="Arabian Standard Time" territory="OM" type="Asia/Muscat"/>
<mapZone other="Arabian Standard Time" territory="ZZ" type="Etc/GMT-4"/>
<!-- (UTC+04:00) Astrakhan, Ulyanovsk -->
<mapZone other="Astrakhan Standard Time" territory="001" type="Europe/Astrakhan"/>
<mapZone other="Astrakhan Standard Time" territory="RU" type="Europe/Astrakhan Europe/Ulyanovsk"/>
<!-- (UTC+04:00) Baku -->
<mapZone other="Azerbaijan Standard Time" territory="001" type="Asia/Baku"/>
<mapZone other="Azerbaijan Standard Time" territory="AZ" type="Asia/Baku"/>
<!-- (UTC+04:00) Izhevsk, Samara -->
<mapZone other="Russia Time Zone 3" territory="001" type="Europe/Samara"/>
<mapZone other="Russia Time Zone 3" territory="RU" type="Europe/Samara"/>
<!-- (UTC+04:00) Port Louis -->
<mapZone other="Mauritius Standard Time" territory="001" type="Indian/Mauritius"/>
<mapZone other="Mauritius Standard Time" territory="MU" type="Indian/Mauritius"/>
<mapZone other="Mauritius Standard Time" territory="RE" type="Indian/Reunion"/>
<mapZone other="Mauritius Standard Time" territory="SC" type="Indian/Mahe"/>
<!-- (UTC+04:00) Tbilisi -->
<mapZone other="Georgian Standard Time" territory="001" type="Asia/Tbilisi"/>
<mapZone other="Georgian Standard Time" territory="GE" type="Asia/Tbilisi"/>
<!-- (UTC+04:00) Yerevan -->
<mapZone other="Caucasus Standard Time" territory="001" type="Asia/Yerevan"/>
<mapZone other="Caucasus Standard Time" territory="AM" type="Asia/Yerevan"/>
<!-- (UTC+04:30) Kabul -->
<mapZone other="Afghanistan Standard Time" territory="001" type="Asia/Kabul"/>
<mapZone other="Afghanistan Standard Time" territory="AF" type="Asia/Kabul"/>
<!-- (UTC+05:00) Ashgabat, Tashkent -->
<mapZone other="West Asia Standard Time" territory="001" type="Asia/Tashkent"/>
<mapZone other="West Asia Standard Time" territory="AQ" type="Antarctica/Mawson"/>
<mapZone other="West Asia Standard Time" territory="KZ" type="Asia/Oral Asia/Aqtau Asia/Aqtobe"/>
<mapZone other="West Asia Standard Time" territory="MV" type="Indian/Maldives"/>
<mapZone other="West Asia Standard Time" territory="TF" type="Indian/Kerguelen"/>
<mapZone other="West Asia Standard Time" territory="TJ" type="Asia/Dushanbe"/>
<mapZone other="West Asia Standard Time" territory="TM" type="Asia/Ashgabat"/>
<mapZone other="West Asia Standard Time" territory="UZ" type="Asia/Tashkent Asia/Samarkand"/>
<mapZone other="West Asia Standard Time" territory="ZZ" type="Etc/GMT-5"/>
<!-- (UTC+05:00) Ekaterinburg -->
<mapZone other="Ekaterinburg Standard Time" territory="001" type="Asia/Yekaterinburg"/>
<mapZone other="Ekaterinburg Standard Time" territory="RU" type="Asia/Yekaterinburg"/>
<!-- (UTC+05:00) Islamabad, Karachi -->
<mapZone other="Pakistan Standard Time" territory="001" type="Asia/Karachi"/>
<mapZone other="Pakistan Standard Time" territory="PK" type="Asia/Karachi"/>
<!-- (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi -->
<mapZone other="India Standard Time" territory="001" type="Asia/Calcutta"/>
<mapZone other="India Standard Time" territory="IN" type="Asia/Calcutta"/>
<!-- (UTC+05:30) Sri Jayawardenepura -->
<mapZone other="Sri Lanka Standard Time" territory="001" type="Asia/Colombo"/>
<mapZone other="Sri Lanka Standard Time" territory="LK" type="Asia/Colombo"/>
<!-- (UTC+05:45) Kathmandu -->
<mapZone other="Nepal Standard Time" territory="001" type="Asia/Katmandu"/>
<mapZone other="Nepal Standard Time" territory="NP" type="Asia/Katmandu"/>
<!-- (UTC+06:00) Astana -->
<mapZone other="Central Asia Standard Time" territory="001" type="Asia/Almaty"/>
<mapZone other="Central Asia Standard Time" territory="AQ" type="Antarctica/Vostok"/>
<mapZone other="Central Asia Standard Time" territory="CN" type="Asia/Urumqi"/>
<mapZone other="Central Asia Standard Time" territory="IO" type="Indian/Chagos"/>
<mapZone other="Central Asia Standard Time" territory="KG" type="Asia/Bishkek"/>
<mapZone other="Central Asia Standard Time" territory="KZ" type="Asia/Almaty Asia/Qyzylorda"/>
<mapZone other="Central Asia Standard Time" territory="ZZ" type="Etc/GMT-6"/>
<!-- (UTC+06:00) Dhaka -->
<mapZone other="Bangladesh Standard Time" territory="001" type="Asia/Dhaka"/>
<mapZone other="Bangladesh Standard Time" territory="BD" type="Asia/Dhaka"/>
<mapZone other="Bangladesh Standard Time" territory="BT" type="Asia/Thimphu"/>
<!-- (UTC+06:00) Omsk -->
<mapZone other="Omsk Standard Time" territory="001" type="Asia/Omsk"/>
<mapZone other="Omsk Standard Time" territory="RU" type="Asia/Omsk"/>
<!-- (UTC+06:30) Yangon (Rangoon) -->
<mapZone other="Myanmar Standard Time" territory="001" type="Asia/Rangoon"/>
<mapZone other="Myanmar Standard Time" territory="CC" type="Indian/Cocos"/>
<mapZone other="Myanmar Standard Time" territory="MM" type="Asia/Rangoon"/>
<!-- (UTC+07:00) Bangkok, Hanoi, Jakarta -->
<mapZone other="SE Asia Standard Time" territory="001" type="Asia/Bangkok"/>
<mapZone other="SE Asia Standard Time" territory="AQ" type="Antarctica/Davis"/>
<mapZone other="SE Asia Standard Time" territory="CX" type="Indian/Christmas"/>
<mapZone other="SE Asia Standard Time" territory="ID" type="Asia/Jakarta Asia/Pontianak"/>
<mapZone other="SE Asia Standard Time" territory="KH" type="Asia/Phnom_Penh"/>
<mapZone other="SE Asia Standard Time" territory="LA" type="Asia/Vientiane"/>
<mapZone other="SE Asia Standard Time" territory="TH" type="Asia/Bangkok"/>
<mapZone other="SE Asia Standard Time" territory="VN" type="Asia/Saigon"/>
<mapZone other="SE Asia Standard Time" territory="ZZ" type="Etc/GMT-7"/>
<!-- (UTC+07:00) Barnaul, Gorno-Altaysk -->
<mapZone other="Altai Standard Time" territory="001" type="Asia/Barnaul"/>
<mapZone other="Altai Standard Time" territory="RU" type="Asia/Barnaul"/>
<!-- (UTC+07:00) Hovd -->
<mapZone other="W. Mongolia Standard Time" territory="001" type="Asia/Hovd"/>
<mapZone other="W. Mongolia Standard Time" territory="MN" type="Asia/Hovd"/>
<!-- (UTC+07:00) Krasnoyarsk -->
<mapZone other="North Asia Standard Time" territory="001" type="Asia/Krasnoyarsk"/>
<mapZone other="North Asia Standard Time" territory="RU" type="Asia/Krasnoyarsk Asia/Novokuznetsk"/>
<!-- (UTC+07:00) Novosibirsk -->
<mapZone other="N. Central Asia Standard Time" territory="001" type="Asia/Novosibirsk"/>
<mapZone other="N. Central Asia Standard Time" territory="RU" type="Asia/Novosibirsk"/>
<!-- (UTC+07:00) Tomsk -->
<mapZone other="Tomsk Standard Time" territory="001" type="Asia/Tomsk"/>
<mapZone other="Tomsk Standard Time" territory="RU" type="Asia/Tomsk"/>
<!-- (UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi -->
<mapZone other="China Standard Time" territory="001" type="Asia/Shanghai"/>
<mapZone other="China Standard Time" territory="CN" type="Asia/Shanghai"/>
<mapZone other="China Standard Time" territory="HK" type="Asia/Hong_Kong"/>
<mapZone other="China Standard Time" territory="MO" type="Asia/Macau"/>
<!-- (UTC+08:00) Irkutsk -->
<mapZone other="North Asia East Standard Time" territory="001" type="Asia/Irkutsk"/>
<mapZone other="North Asia East Standard Time" territory="RU" type="Asia/Irkutsk"/>
<!-- (UTC+08:00) Kuala Lumpur, Singapore -->
<mapZone other="Singapore Standard Time" territory="001" type="Asia/Singapore"/>
<mapZone other="Singapore Standard Time" territory="BN" type="Asia/Brunei"/>
<mapZone other="Singapore Standard Time" territory="ID" type="Asia/Makassar"/>
<mapZone other="Singapore Standard Time" territory="MY" type="Asia/Kuala_Lumpur Asia/Kuching"/>
<mapZone other="Singapore Standard Time" territory="PH" type="Asia/Manila"/>
<mapZone other="Singapore Standard Time" territory="SG" type="Asia/Singapore"/>
<mapZone other="Singapore Standard Time" territory="ZZ" type="Etc/GMT-8"/>
<!-- (UTC+08:00) Perth -->
<mapZone other="W. Australia Standard Time" territory="001" type="Australia/Perth"/>
<mapZone other="W. Australia Standard Time" territory="AU" type="Australia/Perth"/>
<!-- (UTC+08:00) Taipei -->
<mapZone other="Taipei Standard Time" territory="001" type="Asia/Taipei"/>
<mapZone other="Taipei Standard Time" territory="TW" type="Asia/Taipei"/>
<!-- (UTC+08:00) Ulaanbaatar -->
<mapZone other="Ulaanbaatar Standard Time" territory="001" type="Asia/Ulaanbaatar"/>
<mapZone other="Ulaanbaatar Standard Time" territory="MN" type="Asia/Ulaanbaatar Asia/Choibalsan"/>
<!-- (UTC+08:30) Pyongyang -->
<mapZone other="North Korea Standard Time" territory="001" type="Asia/Pyongyang"/>
<mapZone other="North Korea Standard Time" territory="KP" type="Asia/Pyongyang"/>
<!-- (UTC+08:45) Eucla -->
<mapZone other="Aus Central W. Standard Time" territory="001" type="Australia/Eucla"/>
<mapZone other="Aus Central W. Standard Time" territory="AU" type="Australia/Eucla"/>
<!-- (UTC+09:00) Chita -->
<mapZone other="Transbaikal Standard Time" territory="001" type="Asia/Chita"/>
<mapZone other="Transbaikal Standard Time" territory="RU" type="Asia/Chita"/>
<!-- (UTC+09:00) Osaka, Sapporo, Tokyo -->
<mapZone other="Tokyo Standard Time" territory="001" type="Asia/Tokyo"/>
<mapZone other="Tokyo Standard Time" territory="ID" type="Asia/Jayapura"/>
<mapZone other="Tokyo Standard Time" territory="JP" type="Asia/Tokyo"/>
<mapZone other="Tokyo Standard Time" territory="PW" type="Pacific/Palau"/>
<mapZone other="Tokyo Standard Time" territory="TL" type="Asia/Dili"/>
<mapZone other="Tokyo Standard Time" territory="ZZ" type="Etc/GMT-9"/>
<!-- (UTC+09:00) Seoul -->
<mapZone other="Korea Standard Time" territory="001" type="Asia/Seoul"/>
<mapZone other="Korea Standard Time" territory="KR" type="Asia/Seoul"/>
<!-- (UTC+09:00) Yakutsk -->
<mapZone other="Yakutsk Standard Time" territory="001" type="Asia/Yakutsk"/>
<mapZone other="Yakutsk Standard Time" territory="RU" type="Asia/Yakutsk Asia/Khandyga"/>
<!-- (UTC+09:30) Adelaide -->
<mapZone other="Cen. Australia Standard Time" territory="001" type="Australia/Adelaide"/>
<mapZone other="Cen. Australia Standard Time" territory="AU" type="Australia/Adelaide Australia/Broken_Hill"/>
<!-- (UTC+09:30) Darwin -->
<mapZone other="AUS Central Standard Time" territory="001" type="Australia/Darwin"/>
<mapZone other="AUS Central Standard Time" territory="AU" type="Australia/Darwin"/>
<!-- (UTC+10:00) Brisbane -->
<mapZone other="E. Australia Standard Time" territory="001" type="Australia/Brisbane"/>
<mapZone other="E. Australia Standard Time" territory="AU" type="Australia/Brisbane Australia/Lindeman"/>
<!-- (UTC+10:00) Canberra, Melbourne, Sydney -->
<mapZone other="AUS Eastern Standard Time" territory="001" type="Australia/Sydney"/>
<mapZone other="AUS Eastern Standard Time" territory="AU" type="Australia/Sydney Australia/Melbourne"/>
<!-- (UTC+10:00) Guam, Port Moresby -->
<mapZone other="West Pacific Standard Time" territory="001" type="Pacific/Port_Moresby"/>
<mapZone other="West Pacific Standard Time" territory="AQ" type="Antarctica/DumontDUrville"/>
<mapZone other="West Pacific Standard Time" territory="FM" type="Pacific/Truk"/>
<mapZone other="West Pacific Standard Time" territory="GU" type="Pacific/Guam"/>
<mapZone other="West Pacific Standard Time" territory="MP" type="Pacific/Saipan"/>
<mapZone other="West Pacific Standard Time" territory="PG" type="Pacific/Port_Moresby"/>
<mapZone other="West Pacific Standard Time" territory="ZZ" type="Etc/GMT-10"/>
<!-- (UTC+10:00) Hobart -->
<mapZone other="Tasmania Standard Time" territory="001" type="Australia/Hobart"/>
<mapZone other="Tasmania Standard Time" territory="AU" type="Australia/Hobart Australia/Currie"/>
<!-- (UTC+10:00) Vladivostok -->
<mapZone other="Vladivostok Standard Time" territory="001" type="Asia/Vladivostok"/>
<mapZone other="Vladivostok Standard Time" territory="RU" type="Asia/Vladivostok Asia/Ust-Nera"/>
<!-- (UTC+10:30) Lord Howe Island -->
<mapZone other="Lord Howe Standard Time" territory="001" type="Australia/Lord_Howe"/>
<mapZone other="Lord Howe Standard Time" territory="AU" type="Australia/Lord_Howe"/>
<!-- (UTC+11:00) Bougainville Island -->
<mapZone other="Bougainville Standard Time" territory="001" type="Pacific/Bougainville"/>
<mapZone other="Bougainville Standard Time" territory="PG" type="Pacific/Bougainville"/>
<!-- (UTC+11:00) Chokurdakh -->
<mapZone other="Russia Time Zone 10" territory="001" type="Asia/Srednekolymsk"/>
<mapZone other="Russia Time Zone 10" territory="RU" type="Asia/Srednekolymsk"/>
<!-- (UTC+11:00) Magadan -->
<mapZone other="Magadan Standard Time" territory="001" type="Asia/Magadan"/>
<mapZone other="Magadan Standard Time" territory="RU" type="Asia/Magadan"/>
<!-- (UTC+11:00) Norfolk Island -->
<mapZone other="Norfolk Standard Time" territory="001" type="Pacific/Norfolk"/>
<mapZone other="Norfolk Standard Time" territory="NF" type="Pacific/Norfolk"/>
<!-- (UTC+11:00) Sakhalin -->
<mapZone other="Sakhalin Standard Time" territory="001" type="Asia/Sakhalin"/>
<mapZone other="Sakhalin Standard Time" territory="RU" type="Asia/Sakhalin"/>
<!-- (UTC+11:00) Solomon Is., New Caledonia -->
<mapZone other="Central Pacific Standard Time" territory="001" type="Pacific/Guadalcanal"/>
<mapZone other="Central Pacific Standard Time" territory="AQ" type="Antarctica/Casey"/>
<mapZone other="Central Pacific Standard Time" territory="AU" type="Antarctica/Macquarie"/>
<mapZone other="Central Pacific Standard Time" territory="FM" type="Pacific/Ponape Pacific/Kosrae"/>
<mapZone other="Central Pacific Standard Time" territory="NC" type="Pacific/Noumea"/>
<mapZone other="Central Pacific Standard Time" territory="SB" type="Pacific/Guadalcanal"/>
<mapZone other="Central Pacific Standard Time" territory="VU" type="Pacific/Efate"/>
<mapZone other="Central Pacific Standard Time" territory="ZZ" type="Etc/GMT-11"/>
<!-- (UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky -->
<mapZone other="Russia Time Zone 11" territory="001" type="Asia/Kamchatka"/>
<mapZone other="Russia Time Zone 11" territory="RU" type="Asia/Kamchatka Asia/Anadyr"/>
<!-- (UTC+12:00) Auckland, Wellington -->
<mapZone other="New Zealand Standard Time" territory="001" type="Pacific/Auckland"/>
<mapZone other="New Zealand Standard Time" territory="AQ" type="Antarctica/McMurdo"/>
<mapZone other="New Zealand Standard Time" territory="NZ" type="Pacific/Auckland"/>
<!-- (UTC+12:00) Coordinated Universal Time+12 -->
<mapZone other="UTC+12" territory="001" type="Etc/GMT-12"/>
<mapZone other="UTC+12" territory="KI" type="Pacific/Tarawa"/>
<mapZone other="UTC+12" territory="MH" type="Pacific/Majuro Pacific/Kwajalein"/>
<mapZone other="UTC+12" territory="NR" type="Pacific/Nauru"/>
<mapZone other="UTC+12" territory="TV" type="Pacific/Funafuti"/>
<mapZone other="UTC+12" territory="UM" type="Pacific/Wake"/>
<mapZone other="UTC+12" territory="WF" type="Pacific/Wallis"/>
<mapZone other="UTC+12" territory="ZZ" type="Etc/GMT-12"/>
<!-- (UTC+12:00) Fiji -->
<mapZone other="Fiji Standard Time" territory="001" type="Pacific/Fiji"/>
<mapZone other="Fiji Standard Time" territory="FJ" type="Pacific/Fiji"/>
<!-- (UTC+12:45) Chatham Islands -->
<mapZone other="Chatham Islands Standard Time" territory="001" type="Pacific/Chatham"/>
<mapZone other="Chatham Islands Standard Time" territory="NZ" type="Pacific/Chatham"/>
<!-- (UTC+13:00) Nuku'alofa -->
<mapZone other="Tonga Standard Time" territory="001" type="Pacific/Tongatapu"/>
<mapZone other="Tonga Standard Time" territory="KI" type="Pacific/Enderbury"/>
<mapZone other="Tonga Standard Time" territory="TK" type="Pacific/Fakaofo"/>
<mapZone other="Tonga Standard Time" territory="TO" type="Pacific/Tongatapu"/>
<mapZone other="Tonga Standard Time" territory="ZZ" type="Etc/GMT-13"/>
<!-- (UTC+13:00) Samoa -->
<mapZone other="Samoa Standard Time" territory="001" type="Pacific/Apia"/>
<mapZone other="Samoa Standard Time" territory="WS" type="Pacific/Apia"/>
<!-- (UTC+14:00) Kiritimati Island -->
<mapZone other="Line Islands Standard Time" territory="001" type="Pacific/Kiritimati"/>
<mapZone other="Line Islands Standard Time" territory="KI" type="Pacific/Kiritimati"/>
<mapZone other="Line Islands Standard Time" territory="ZZ" type="Etc/GMT-14"/>
</mapTimezones>
</windowsZones>
</supplementalData>

View File

@@ -1110,6 +1110,8 @@ class PHPMailer {
if($this->MessageID != '') {
$result .= $this->HeaderLine('Message-ID',$this->MessageID);
} else {
$result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
}
$result .= $this->HeaderLine('X-Priority', $this->Priority);
$result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (phpmailer.sourceforge.net)');

610
migration/dedup.php Normal file
View File

@@ -0,0 +1,610 @@
<?php
$migrate_dedup_users = array();
$migrate_dedup_users["midiclub"] = "midclub";
$migrate_dedup_users["trip"] = "car";
$migrate_dedup_users["thunderbolt"] = "thunderbolt16";
$migrate_dedup_users["avirnig"] = "beav";
$migrate_dedup_users["jeaninmontreal"] = "jeanm";
$migrate_dedup_users["nneo"] = "nneonneo";
$migrate_dedup_users["ebonyj"] = "jim";
$migrate_dedup_users["tnr"] = "ichem";
$migrate_dedup_users["archus"] = "rstehwien";
$migrate_dedup_users["kalysto"] = "kalar";
$migrate_dedup_users["eloe"] = "mouvanteloe";
$migrate_dedup_users["erdling"] = "eitj";
$migrate_dedup_users["cwinebrinner"] = "chuck_1027";
$migrate_dedup_users["foo"] = "mfranz";
$migrate_dedup_users["f00fbug"] = "viviowns";
$migrate_dedup_users["olavbalm"] = "olav";
$migrate_dedup_users["kapong"] = "el_kapong";
$migrate_dedup_users["thorax"] = "rofthorax";
$migrate_dedup_users["kramer3d"] = "asdfghytrewq";
$migrate_dedup_users["reevee"] = "vee";
$migrate_dedup_users["matzeb"] = "matzebraun";
$migrate_dedup_users["jmsoler"] = "jms";
$migrate_dedup_users["aqa"] = "angelito";
$migrate_dedup_users["blendorphin"] = "blenergetic";
$migrate_dedup_users["paolo"] = "jody";
$migrate_dedup_users["blendedidea"] = "chubango";
$migrate_dedup_users["ruiramos"] = "p3ctu5";
$migrate_dedup_users["head_chees"] = "vassilizaitsaf";
$migrate_dedup_users["kiernan"] = "rofthorax";
$migrate_dedup_users["olaf_arnold"] = "olafarnold";
$migrate_dedup_users["joseyanesaltos"] = "joseyanes";
$migrate_dedup_users["nicholasfrancis"] = "nicholas";
$migrate_dedup_users["briancary"] = "brinux";
$migrate_dedup_users["piotrek"] = "ccpiotr";
$migrate_dedup_users["slashdev"] = "colonel_panic";
$migrate_dedup_users["rj2004"] = "rjblender";
$migrate_dedup_users["jeffreymcgrew"] = "toast";
$migrate_dedup_users["hendersj"] = "jhenderson";
$migrate_dedup_users["pierpy"] = "pier";
$migrate_dedup_users["buschhardt"] = "ishtar";
$migrate_dedup_users["timlesher"] = "tim";
$migrate_dedup_users["tizmo"] = "timlucas";
$migrate_dedup_users["pprandin"] = "pashaweb";
$migrate_dedup_users["servivo"] = "taz";
$migrate_dedup_users["stabby"] = "xingo";
$migrate_dedup_users["jimmydietulpe"] = "jimmadietulpe";
$migrate_dedup_users["palmnet"] = "palmer";
$migrate_dedup_users["graphique-chti"] = "gcprod";
$migrate_dedup_users["dreamkatana1"] = "dreamkatana";
$migrate_dedup_users["wolverine"] = "shane";
$migrate_dedup_users["schwarz"] = "wene";
$migrate_dedup_users["justtesting"] = "levon";
$migrate_dedup_users["erwin_xnan"] = "erwin";
$migrate_dedup_users["zack"] = "krystof";
$migrate_dedup_users["fabinator31"] = "fab31";
$migrate_dedup_users["themilkman"] = "cliftonium";
$migrate_dedup_users["etiennel"] = "choupi";
$migrate_dedup_users["m_i_c_h_e_l"] = "michaels";
$migrate_dedup_users["techrolla"] = "muffinpeddler";
$migrate_dedup_users["fireflymantis"] = "twilight";
$migrate_dedup_users["pinucset0"] = "pinucset";
$migrate_dedup_users["franzrogar"] = "blender-i18n-po";
$migrate_dedup_users["pinucset1"] = "pinucset";
$migrate_dedup_users["pinucset3"] = "pinucset";
$migrate_dedup_users["threeddream"] = "x-axis";
$migrate_dedup_users["mudbot"] = "laurence";
$migrate_dedup_users["ligeyx"] = "ligey";
$migrate_dedup_users["dreas"] = "dre";
$migrate_dedup_users["joorvapl"] = "joorva";
$migrate_dedup_users["voyage"] = "xenithi";
$migrate_dedup_users["azter"] = "azterix";
$migrate_dedup_users["lapinbleu"] = "simeon";
$migrate_dedup_users["emmanuel_t"] = "emmanuel";
$migrate_dedup_users["cricket"] = "gku";
$migrate_dedup_users["justanumber"] = "mad4linux";
$migrate_dedup_users["johnnyen"] = "johnny";
$migrate_dedup_users["leiota"] = "marcopolo";
$migrate_dedup_users["blackstorm"] = "ikbendirk";
$migrate_dedup_users["richie"] = "ligey";
$migrate_dedup_users["mrcarnivore2"] = "mrcarnivore";
$migrate_dedup_users["muddym1nd"] = "muddymind";
$migrate_dedup_users["eumesmo"] = "vasco_da_gama";
$migrate_dedup_users["ph-kby"] = "olav";
$migrate_dedup_users["piotrek_"] = "ccpiotr";
$migrate_dedup_users["joaquin"] = "prospero";
$migrate_dedup_users["konrad_haenel"] = "konrad8ha";
$migrate_dedup_users["benstabler"] = "lightning";
$migrate_dedup_users["gustav"] = "draknoid";
$migrate_dedup_users["slikdigit"] = "bassamk";
$migrate_dedup_users["karlerlandsen"] = "lethalsidep";
$migrate_dedup_users["djwillmsoh"] = "djw1";
$migrate_dedup_users["aschmitz"] = "root_42";
$migrate_dedup_users["malfunc"] = "mcq";
$migrate_dedup_users["m_jack"] = "jack";
$migrate_dedup_users["davey"] = "madcow";
$migrate_dedup_users["iraytrace"] = "butler";
$migrate_dedup_users["duser"] = "mslechols";
$migrate_dedup_users["akito"] = "ton";
$migrate_dedup_users["jochenschmitt"] = "s4504kr";
$migrate_dedup_users["digikiller"] = "ace1";
$migrate_dedup_users["shzhc"] = "zzzz";
$migrate_dedup_users["cyrilbrulebois"] = "kibi";
$migrate_dedup_users["themushroom"] = "draknoid";
$migrate_dedup_users["zinkmaster"] = "mariolink";
$migrate_dedup_users["ashsid"] = "ash";
$migrate_dedup_users["novato"] = "jimenez";
$migrate_dedup_users["jadie_p"] = "jadie";
$migrate_dedup_users["neshshah007"] = "neshrai";
$migrate_dedup_users["ryanshow"] = "tankcoder";
$migrate_dedup_users["squarelinesq"] = "squareline";
$migrate_dedup_users["virginijus"] = "ernestas1994";
$migrate_dedup_users["renderdemon"] = "pnio";
$migrate_dedup_users["kwab"] = "kobi";
$migrate_dedup_users["peterjonckheere"] = "jonckheerep";
$migrate_dedup_users["vidar"] = "vidarino";
$migrate_dedup_users["sapjohannes"] = "johannesje";
$migrate_dedup_users["youri"] = "ayers";
$migrate_dedup_users["theamiman"] = "amiman";
$migrate_dedup_users["chrisg_ksi"] = "red_planet";
$migrate_dedup_users["blenderorgfan"] = "franciscosilva";
$migrate_dedup_users["ericd"] = "ericdrayer";
$migrate_dedup_users["asdfghjkl2"] = "asdfghjkl";
$migrate_dedup_users["quorn"] = "alienit";
$migrate_dedup_users["daan221"] = "phoenix221";
$migrate_dedup_users["user"] = "mslechols";
$migrate_dedup_users["pyb"] = "lomperillo";
$migrate_dedup_users["g56"] = "gwald";
$migrate_dedup_users["ausland"] = "vots";
$migrate_dedup_users["quillinan"] = "orinoco";
$migrate_dedup_users["t3tsuj1n"] = "bootsheoz";
$migrate_dedup_users["molflesh"] = "melflash";
$migrate_dedup_users["rickta59"] = "aerobicsboy";
$migrate_dedup_users["nolopoly"] = "lopoly";
$migrate_dedup_users["jeel"] = "jl57";
$migrate_dedup_users["brianmccumber2"] = "brianmccumber";
$migrate_dedup_users["mouette"] = "moutte";
$migrate_dedup_users["wout"] = "wonderingwout";
$migrate_dedup_users["fatfinger"] = "mattyc";
$migrate_dedup_users["nathangull"] = "nateg";
$migrate_dedup_users["tampadave"] = "dkmweeks";
$migrate_dedup_users["sausages"] = "lethargic";
$migrate_dedup_users["chris3d"] = "chrisbd";
$migrate_dedup_users["kaito"] = "ton";
$migrate_dedup_users["akirasan"] = "akira_b";
$migrate_dedup_users["fiddle"] = "rayf";
$migrate_dedup_users["bald"] = "manu";
$migrate_dedup_users["mistermelquin"] = "melquin";
$migrate_dedup_users["kofish"] = "kingdomoffish";
$migrate_dedup_users["onanj"] = "orangee";
$migrate_dedup_users["jasoncarrier"] = "jason";
$migrate_dedup_users["joelgodin"] = "jenniferblender";
$migrate_dedup_users["oranj"] = "orangee";
$migrate_dedup_users["schdeb"] = "schmidtcristian";
$migrate_dedup_users["bastian"] = "angerb";
$migrate_dedup_users["patko"] = "patco";
$migrate_dedup_users["arakyd666"] = "arakyd";
$migrate_dedup_users["karimf"] = "rim-k";
$migrate_dedup_users["nletwory"] = "jestertestest";
$migrate_dedup_users["mansoorhyder"] = "mansoor";
$migrate_dedup_users["rocketmagnet"] = "steventr";
$migrate_dedup_users["thekernal"] = "kernal";
$migrate_dedup_users["satom"] = "satriatomat";
$migrate_dedup_users["a624"] = "a623";
$migrate_dedup_users["jldavilacasares"] = "muki";
$migrate_dedup_users["segersj"] = "segers";
$migrate_dedup_users["hoisan49"] = "hoisan";
$migrate_dedup_users["j_bessette"] = "linuxpimp20";
$migrate_dedup_users["andreas999"] = "andreas";
$migrate_dedup_users["dusan"] = "dsc512";
$migrate_dedup_users["dx-mon"] = "mantr100";
$migrate_dedup_users["ilislab"] = "ids";
$migrate_dedup_users["rben"] = "raybenjamin";
$migrate_dedup_users["sphere"] = "dysonsphere";
$migrate_dedup_users["highlife22"] = "highlife";
$migrate_dedup_users["yonarw"] = "yona";
$migrate_dedup_users["kryptic89"] = "kryptic";
$migrate_dedup_users["axelp"] = "axel";
$migrate_dedup_users["perebalsach"] = "fog22";
$migrate_dedup_users["alogerson"] = "gerson";
$migrate_dedup_users["cotejrp"] = "cotejrp1";
$migrate_dedup_users["tak"] = "carlosjamesr";
$migrate_dedup_users["ziddy"] = "anishchandran";
$migrate_dedup_users["asdgz"] = "blenderiseur";
$migrate_dedup_users["cdated"] = "cdated257";
$migrate_dedup_users["jayrkalugin"] = "jayr";
$migrate_dedup_users["zeemzoet"] = "johannesje";
$migrate_dedup_users["duo"] = "ambyra";
$migrate_dedup_users["philb"] = "chewbacca";
$migrate_dedup_users["tzi"] = "izt";
$migrate_dedup_users["squirrelthetire"] = "squirrel-tire";
$migrate_dedup_users["anwyn"] = "sugarshark";
$migrate_dedup_users["a2z"] = "a2zaa";
$migrate_dedup_users["fxrex"] = "femi";
$migrate_dedup_users["alethewiz"] = "mfaso68";
$migrate_dedup_users["noseferrit"] = "billybong";
$migrate_dedup_users["fcali"] = "fabz";
$migrate_dedup_users["bbirchler"] = "bblender";
$migrate_dedup_users["oslosewers"] = "oslo";
$migrate_dedup_users["wtrsltnk"] = "wtr";
$migrate_dedup_users["cspohst"] = "spohst";
$migrate_dedup_users["warhawk08"] = "warhawk1990";
$migrate_dedup_users["magick_crow"] = "magickcrow";
$migrate_dedup_users["guillaumem"] = "guillaume";
$migrate_dedup_users["jwitthuhn"] = "rahu";
$migrate_dedup_users["fmehigan"] = "frank_me";
$migrate_dedup_users["ilissys"] = "ids";
$migrate_dedup_users["supermoaaa"] = "moaaa";
$migrate_dedup_users["nwmatt"] = "mhenley";
$migrate_dedup_users["bezel"] = "xshell";
$migrate_dedup_users["rebuss"] = "studioa";
$migrate_dedup_users["geonom"] = "geoadel";
$migrate_dedup_users["seldan"] = "farakon";
$migrate_dedup_users["kuru76"] = "kcorbin";
$migrate_dedup_users["kapil"] = "kapilbedarkar";
$migrate_dedup_users["henryiii"] = "henryschreiner";
$migrate_dedup_users["pateamcarl"] = "carlhuth";
$migrate_dedup_users["kamen"] = "bigbob1993";
$migrate_dedup_users["olddemon"] = "old_demon";
$migrate_dedup_users["draklaw2"] = "draklaw";
$migrate_dedup_users["ksdlee"] = "kdlee";
$migrate_dedup_users["xource"] = "admix";
$migrate_dedup_users["invertednormal"] = "smokebox46and2";
$migrate_dedup_users["feelgoodcomics"] = "onlygoodwin";
$migrate_dedup_users["glorund"] = "undolaure";
$migrate_dedup_users["soulofsound"] = "johnnym";
$migrate_dedup_users["ghigi123"] = "ghigi";
$migrate_dedup_users["stefanvoigthpi"] = "derstefan";
$migrate_dedup_users["dblenderv"] = "default";
$migrate_dedup_users["ophiocus"] = "oneliner";
$migrate_dedup_users["colleywrks"] = "colley";
$migrate_dedup_users["fogy"] = "fog22";
$migrate_dedup_users["vidarn"] = "bida70";
$migrate_dedup_users["kevin"] = "goblender2541";
$migrate_dedup_users["tisachris"] = "warflight";
$migrate_dedup_users["personalex2"] = "personalex";
$migrate_dedup_users["polypa"] = "lile";
$migrate_dedup_users["i4dnf"] = "emilian";
$migrate_dedup_users["nitalleb"] = "singleman";
$migrate_dedup_users["tcgodoy"] = "godoy";
$migrate_dedup_users["joorva_pl"] = "joorva";
$migrate_dedup_users["sex"] = "xest";
$migrate_dedup_users["chipmunk"] = "ignatz";
$migrate_dedup_users["schalmagne"] = "chalmagne";
$migrate_dedup_users["michalziulek"] = "eneida";
$migrate_dedup_users["jeeps"] = "jeepster";
$migrate_dedup_users["yain"] = "chaos";
$migrate_dedup_users["amadio"] = "marble";
$migrate_dedup_users["javiere"] = "javierchavez";
$migrate_dedup_users["drmzperx"] = "mzperx";
$migrate_dedup_users["sebastianreaser"] = "sebastian0";
$migrate_dedup_users["idolon"] = "spadija";
$migrate_dedup_users["alanhzhcn"] = "alanhzh";
$migrate_dedup_users["jeanc"] = "jean";
$migrate_dedup_users["transblue2"] = "transblue";
$migrate_dedup_users["cesarwilfredo"] = "cesar";
$migrate_dedup_users["nspyr"] = "spiderfire";
$migrate_dedup_users["supermegamoaaa"] = "moaaa";
$migrate_dedup_users["firetiger"] = "opensolution";
$migrate_dedup_users["skarg"] = "cados";
$migrate_dedup_users["realheadcrusher"] = "danigr";
$migrate_dedup_users["kpg"] = "nihylius";
$migrate_dedup_users["tft"] = "tft67";
$migrate_dedup_users["storabbarn"] = "morre";
$migrate_dedup_users["bacurau"] = "roger_roo";
$migrate_dedup_users["alisari"] = "parasoley";
$migrate_dedup_users["skiski"] = "superrom";
$migrate_dedup_users["halfninja"] = "nickh";
$migrate_dedup_users["drell_develop"] = "drellex";
$migrate_dedup_users["highlif3"] = "highlife";
$migrate_dedup_users["broggsim1"] = "broggsim";
$migrate_dedup_users["blendermanuci"] = "yunior88";
$migrate_dedup_users["jpt9"] = "jtuttle";
$migrate_dedup_users["farrer"] = "farpro";
$migrate_dedup_users["badcheez"] = "randall";
$migrate_dedup_users["leewj_"] = "lohns";
$migrate_dedup_users["martin107"] = "martinfrances";
$migrate_dedup_users["dedpan"] = "tberghuis";
$migrate_dedup_users["whiterabbit"] = "dreamscapearts";
$migrate_dedup_users["shuvro"] = "shuvro05";
$migrate_dedup_users["anichandru"] = "anishchandran";
$migrate_dedup_users["ncaralph"] = "ralphdoctorow";
$migrate_dedup_users["nlongchamps"] = "nlong";
$migrate_dedup_users["mawi37"] = "mawi";
$migrate_dedup_users["rizla"] = "jay";
$migrate_dedup_users["jeh"] = "mirkril";
$migrate_dedup_users["b-fighter"] = "fanatic";
$migrate_dedup_users["wasamonkey"] = "wasa";
$migrate_dedup_users["nicola"] = "martin45";
$migrate_dedup_users["method-es"] = "method";
$migrate_dedup_users["pablow"] = "warhole";
$migrate_dedup_users["alanishzh"] = "alanhzh";
$migrate_dedup_users["thunder947"] = "thunder";
$migrate_dedup_users["yijimi"] = "roler";
$migrate_dedup_users["jaydenb"] = "logidude";
$migrate_dedup_users["leandropolus"] = "leandrosz";
$migrate_dedup_users["ibi002"] = "ibi001";
$migrate_dedup_users["hakanortasoz"] = "tayfax";
$migrate_dedup_users["enur"] = "rune";
$migrate_dedup_users["dsuesse"] = "qiip";
$migrate_dedup_users["bipedlaboratory"] = "redmetal";
$migrate_dedup_users["codeyhanson"] = "codey";
$migrate_dedup_users["alphonso_b"] = "alfonso_b";
$migrate_dedup_users["surreal6"] = "carlospadial";
$migrate_dedup_users["kralizek"] = "kral";
$migrate_dedup_users["kfrechet"] = "keithfr";
$migrate_dedup_users["jwedlake"] = "joshwedlake";
$migrate_dedup_users["westie630"] = "bully";
$migrate_dedup_users["fictional"] = "icefire";
$migrate_dedup_users["zelozelos"] = "zelozelos1";
$migrate_dedup_users["dragonlord"] = "acuena";
$migrate_dedup_users["mrcheese"] = "jpeg";
$migrate_dedup_users["willemverwey"] = "dandandan";
$migrate_dedup_users["jhed"] = "anton_foy";
$migrate_dedup_users["treacy1077"] = "briant";
$migrate_dedup_users["xest"] = "xembie";
$migrate_dedup_users["cyphl25"] = "jmsfreezer";
$migrate_dedup_users["wynk"] = "wynn";
$migrate_dedup_users["trock2957"] = "trock";
$migrate_dedup_users["mr_bomb"] = "carter24";
$migrate_dedup_users["nikolaus"] = "tortellini";
$migrate_dedup_users["pegasus_001"] = "pegasus001";
$migrate_dedup_users["fayte"] = "fayte220";
$migrate_dedup_users["jagang_8"] = "jagang";
$migrate_dedup_users["thetwom"] = "moe";
$migrate_dedup_users["kevlareditor"] = "klthomas";
$migrate_dedup_users["damien_deom"] = "dams";
$migrate_dedup_users["reynantem"] = "reynante";
$migrate_dedup_users["hiralm01"] = "hiralm";
$migrate_dedup_users["radix7"] = "wyldethang";
$migrate_dedup_users["puppetm"] = "puppetmaster";
$migrate_dedup_users["vimax"] = "vimaxus";
$migrate_dedup_users["mozzy69"] = "lyndon";
$migrate_dedup_users["vitranaccad"] = "thestorm74";
$migrate_dedup_users["kj12345"] = "kevinjames";
$migrate_dedup_users["foinix"] = "mrnoodle";
$migrate_dedup_users["shizu"] = "sntulix";
$migrate_dedup_users["kukulcangod23"] = "kukulcangod";
$migrate_dedup_users["piliq"] = "qpblendpolis";
$migrate_dedup_users["benvad"] = "vbenny";
$migrate_dedup_users["fgribben"] = "sharkey";
$migrate_dedup_users["makospince"] = "mako_spince";
$migrate_dedup_users["ablenderuser"] = "pancakeface";
$migrate_dedup_users["axelphi"] = "axel";
$migrate_dedup_users["josiasbh"] = "josias";
$migrate_dedup_users["mattsix"] = "majawe";
$migrate_dedup_users["duarte_ramos"] = "dphantom";
$migrate_dedup_users["killogge"] = "muraj";
$migrate_dedup_users["luka"] = "omgwtfbbq";
$migrate_dedup_users["chromemonkey"] = "brian651msp";
$migrate_dedup_users["e_d_i"] = "ide";
$migrate_dedup_users["mrhaynesy"] = "haynesy";
$migrate_dedup_users["imagineering"] = "imagineer";
$migrate_dedup_users["virgiliovasconc"] = "virgilio";
$migrate_dedup_users["blenderlb57"] = "louigi";
$migrate_dedup_users["nick65"] = "dgnicola";
$migrate_dedup_users["mmatthews"] = "mamatthews";
$migrate_dedup_users["gerwood"] = "arilian";
$migrate_dedup_users["xsidmax"] = "xsi";
$migrate_dedup_users["mix-yag"] = "aynahsim";
$migrate_dedup_users["wpthomas"] = "tallguy";
$migrate_dedup_users["bp007"] = "bender007";
$migrate_dedup_users["emach"] = "eon4blender";
$migrate_dedup_users["alfclement"] = "alfc";
$migrate_dedup_users["blenderwell"] = "goplexian";
$migrate_dedup_users["asebastianr"] = "sebastian0";
$migrate_dedup_users["joepal1976"] = "joepal";
$migrate_dedup_users["chertov"] = "lestat";
$migrate_dedup_users["djela63"] = "jerominovich";
$migrate_dedup_users["lppinto"] = "lpciper";
$migrate_dedup_users["prana"] = "nomath";
$migrate_dedup_users["sarubadoru"] = "salvador";
$migrate_dedup_users["rarebit"] = "rawstar7";
$migrate_dedup_users["hikikamori"] = "hikkikamori";
$migrate_dedup_users["mrduke"] = "drop";
$migrate_dedup_users["deetee"] = "dertee";
$migrate_dedup_users["woooooah"] = "noxwell";
$migrate_dedup_users["macoss"] = "ossmac";
$migrate_dedup_users["bunnyboy212"] = "blender_buddie";
$migrate_dedup_users["maxgrip"] = "czarek";
$migrate_dedup_users["tentonman"] = "titandtat";
$migrate_dedup_users["jabozzo"] = "bozzo";
$migrate_dedup_users["reinways"] = "reinw";
$migrate_dedup_users["petergk"] = "nihylius";
$migrate_dedup_users["zphr3000"] = "zphr";
$migrate_dedup_users["ruivo"] = "andreh";
$migrate_dedup_users["kosta"] = "kgd";
$migrate_dedup_users["delter"] = "dertee";
$migrate_dedup_users["jmiller"] = "lethargic";
$migrate_dedup_users["dealga"] = "zeffii";
$migrate_dedup_users["bogey"] = "daveh";
$migrate_dedup_users["silencebe"] = "silence";
$migrate_dedup_users["temozarela"] = "gorn";
$migrate_dedup_users["tischite"] = "greenbutton";
$migrate_dedup_users["buisson"] = "dinodino";
$migrate_dedup_users["cfox"] = "colinfox";
$migrate_dedup_users["hunkadoodle"] = "hunkadoodledoo";
$migrate_dedup_users["jlwitthuhn"] = "rahu";
$migrate_dedup_users["hvfrancesco"] = "hva";
$migrate_dedup_users["hazim-jamal"] = "hazim1";
$migrate_dedup_users["aurosutru"] = "tlm";
$migrate_dedup_users["pierrea"] = "pier2";
$migrate_dedup_users["zoon"] = "zoonpolygonikon";
$migrate_dedup_users["gruntbatch"] = "carmichael";
$migrate_dedup_users["petru"] = "virusanti";
$migrate_dedup_users["mikeh74"] = "mikeh";
$migrate_dedup_users["sugoi"] = "juntunen";
$migrate_dedup_users["bartje"] = "bartart3d";
$migrate_dedup_users["yaoyansi"] = "yaoyansi2";
$migrate_dedup_users["rafek"] = "rafek_finearts";
$migrate_dedup_users["caspern"] = "caspernilsson";
$migrate_dedup_users["lee"] = "s_random";
$migrate_dedup_users["sparky"] = "mmikkelsen";
$migrate_dedup_users["wigglyframes"] = "rene";
$migrate_dedup_users["kroni"] = "kronos";
$migrate_dedup_users["xgl_asyliax"] = "xglasyliax";
$migrate_dedup_users["blendeador"] = "luisperosio";
$migrate_dedup_users["kaos1986"] = "kaos86";
$migrate_dedup_users["adamdoyle"] = "advs89";
$migrate_dedup_users["kahenraz"] = "mistrel";
$migrate_dedup_users["ccliffe"] = "cjcliffe";
$migrate_dedup_users["nullfied"] = "xercesblue";
$migrate_dedup_users["dustyghost"] = "dustbin1_uk";
$migrate_dedup_users["claaskuhnen"] = "cekuhnendev";
$migrate_dedup_users["vercingetorix"] = "diamond_rust";
$migrate_dedup_users["plugboy"] = "centerlaw";
$migrate_dedup_users["rben13"] = "raybenjamin";
$migrate_dedup_users["deangiberson"] = "voidptr";
$migrate_dedup_users["makeitwork"] = "bboybram";
$migrate_dedup_users["iljosli"] = "jos";
$migrate_dedup_users["slowan"] = "slovan";
$migrate_dedup_users["mooonwalkercz"] = "mooonwalker";
$migrate_dedup_users["tapplek"] = "tapple";
$migrate_dedup_users["blendmond"] = "cornix";
$migrate_dedup_users["giorgiomartini"] = "tweakingknobs";
$migrate_dedup_users["davidray"] = "deadman";
$migrate_dedup_users["justthisguy"] = "nyctef";
$migrate_dedup_users["logobar"] = "freyer";
$migrate_dedup_users["zooo"] = "leon_cheung";
$migrate_dedup_users["josiasalexandre"] = "josias";
$migrate_dedup_users["tsukiko_chan"] = "tsukikochan";
$migrate_dedup_users["akira_san"] = "akira_b";
$migrate_dedup_users["walberti"] = "walbertievarist";
$migrate_dedup_users["astro"] = "tnboma";
$migrate_dedup_users["mccx"] = "mcq";
$migrate_dedup_users["daa84"] = "daa";
$migrate_dedup_users["sivert3"] = "cent";
$migrate_dedup_users["twentyone"] = "glade";
$migrate_dedup_users["endi2"] = "endi";
$migrate_dedup_users["jamesr"] = "james_r";
$migrate_dedup_users["reece"] = "reecerobinson";
$migrate_dedup_users["dustractor"] = "bpygrams";
$migrate_dedup_users["pencilhead"] = "pencil-head";
$migrate_dedup_users["toml"] = "tomol";
$migrate_dedup_users["colinm"] = "tablaman";
$migrate_dedup_users["blendphys2"] = "blendphys";
$migrate_dedup_users["xgoff"] = "zzyxz";
$migrate_dedup_users["coleingraham"] = "coledingraham";
$migrate_dedup_users["danielvmacedo"] = "skul3r";
$migrate_dedup_users["burster"] = "przemaz";
$migrate_dedup_users["tung"] = "tungster";
$migrate_dedup_users["chessie"] = "blackcatt";
$migrate_dedup_users["foxdog"] = "rubbernuke";
$migrate_dedup_users["mordachai"] = "gus";
$migrate_dedup_users["chilamlai"] = "cllai";
$migrate_dedup_users["sliders34"] = "sliders";
$migrate_dedup_users["benji852"] = "benjamin852";
$migrate_dedup_users["bebinalpha"] = "bebin";
$migrate_dedup_users["mr78"] = "alexandr";
$migrate_dedup_users["avirillion"] = "tarion";
$migrate_dedup_users["matthiasro"] = "matthias_r";
$migrate_dedup_users["debearseax"] = "seogeniuss";
$migrate_dedup_users["karja"] = "trockenfrosch";
$migrate_dedup_users["rojuinex"] = "ifrit";
$migrate_dedup_users["bernardo"] = "dados";
$migrate_dedup_users["ddeclara"] = "decden";
$migrate_dedup_users["zm_sansan"] = "sansan";
$migrate_dedup_users["useless"] = "cortot";
$migrate_dedup_users["tymnclono"] = "sooccatly";
$migrate_dedup_users["rodrigo_b"] = "rodrigob";
$migrate_dedup_users["shnurui"] = "spinster";
$migrate_dedup_users["michalisz"] = "michaliszissiou";
$migrate_dedup_users["fbbdev"] = "babboide";
$migrate_dedup_users["tjackson"] = "tjonline";
$migrate_dedup_users["ramaswamy"] = "ramaswamysriram";
$migrate_dedup_users["allrod5"] = "rodblender";
$migrate_dedup_users["qcp"] = "qpblendpolis";
$migrate_dedup_users["ftsf"] = "thesleepless";
$migrate_dedup_users["umagoo2012"] = "umagoo";
$migrate_dedup_users["raven"] = "rune";
$migrate_dedup_users["hsaito"] = "integer";
$migrate_dedup_users["paulthegreat"] = "digitalpyro";
$migrate_dedup_users["capheen"] = "dval";
$migrate_dedup_users["rskinner"] = "rws";
$migrate_dedup_users["gregstein"] = "gregorein";
$migrate_dedup_users["matty686"] = "matty";
$migrate_dedup_users["selby_rowley"] = "selby";
$migrate_dedup_users["shembolstudio"] = "natadams8";
$migrate_dedup_users["grenzfrequence"] = "goatrance";
$migrate_dedup_users["stephan"] = "schdeffan";
$migrate_dedup_users["axis33"] = "dsc512";
$migrate_dedup_users["redandfish"] = "red-fish";
$migrate_dedup_users["artsapcemedia"] = "arzpace";
$migrate_dedup_users["artspacemedia"] = "arzpace";
$migrate_dedup_users["mccmcc"] = "mcq";
$migrate_dedup_users["seocitterx"] = "mediabuy";
$migrate_dedup_users["lightning_limn"] = "lightning4";
$migrate_dedup_users["omarlakhdar"] = "archimage";
$migrate_dedup_users["regeleionescu"] = "regele";
$migrate_dedup_users["mitchell_decker"] = "michealikruhara";
$migrate_dedup_users["joselebon"] = "jl57";
$migrate_dedup_users["simonbroggi"] = "broggsim";
$migrate_dedup_users["inwadnepe"] = "ceapbatatry";
$migrate_dedup_users["ehobjman"] = "resbsp";
$migrate_dedup_users["davelassanske"] = "dolby411";
$migrate_dedup_users["jsu"] = "jansub";
$migrate_dedup_users["agricola"] = "agricola1";
$migrate_dedup_users["bartoszek"] = "bartus";
$migrate_dedup_users["captainoblivion"] = "cptoblivion";
$migrate_dedup_users["alexmcourt"] = "personalex";
$migrate_dedup_users["jmsprss"] = "xonar";
$migrate_dedup_users["awarnock"] = "salsa";
$migrate_dedup_users["mcc2"] = "mcq";
$migrate_dedup_users["psyborg042"] = "psyborg";
$migrate_dedup_users["ushiproject"] = "ushi";
$migrate_dedup_users["mrjimmy"] = "mrjimmyos";
$migrate_dedup_users["thefinalcut"] = "tlousky";
$migrate_dedup_users["startheshadow"] = "star";
$migrate_dedup_users["axredneck"] = "redneck";
$migrate_dedup_users["phimestudio"] = "phime";
$migrate_dedup_users["dwatts1"] = "dlax";
$migrate_dedup_users["rertjoi"] = "rertjwi";
$migrate_dedup_users["erdjkgh"] = "rertjwi";
$migrate_dedup_users["libertainsrg"] = "fcougar";
$migrate_dedup_users["godling72"] = "dmelchio";
$migrate_dedup_users["myclay"] = "thenewone";
$migrate_dedup_users["ecaspersen"] = "ecasper";
$migrate_dedup_users["driewiel"] = "driesiedriewiel";
$migrate_dedup_users["bhupen"] = "bhupendra";
$migrate_dedup_users["caosdoar"] = "mailoyo";
$migrate_dedup_users["polyspin"] = "butler";
$migrate_dedup_users["qalb_al_aqrab"] = "efimpetelin";
$migrate_dedup_users["fdfdfdfffd"] = "fcougar";
$migrate_dedup_users["brianlockett"] = "macrow";
$migrate_dedup_users["claude"] = "coco";
$migrate_dedup_users["mattostgard"] = "drflail";
$migrate_dedup_users["cekuhnen_new"] = "cekuhnendev";
$migrate_dedup_users["kirill_lukyanov"] = "kirill";
$migrate_dedup_users["jan-eric"] = "janeric96";
$migrate_dedup_users["daniel_h"] = "dhoughto";
$migrate_dedup_users["raphaelbarros"] = "thebigheadone";
$migrate_dedup_users["salas"] = "tychota";
$migrate_dedup_users["danieljsamson"] = "techfix";
$migrate_dedup_users["vinagrito"] = "aechemendia";
$migrate_dedup_users["lin_165"] = "b1657022405";
$migrate_dedup_users["cwebber"] = "paroneayea";
$migrate_dedup_users["harolddadomo"] = "harold";
$migrate_dedup_users["rabidsquirrel"] = "genericusername";
$migrate_dedup_users["larry3"] = "lehibou";
$migrate_dedup_users["predoe"] = "petronius3d";
$migrate_dedup_users["skoo"] = "stefano";
$migrate_dedup_users["cabergolinety"] = "azathioprinewww";
$migrate_dedup_users["prestijkorsan07"] = "prestij07";
$migrate_dedup_users["scottpetrovic"] = "slpetrov";
$migrate_dedup_users["zooly76"] = "zooly";
$migrate_dedup_users["theoryanimation"] = "davidandrade";
$migrate_dedup_users["daninsky"] = "danishit";
$migrate_dedup_users["eyesee2013"] = "eyesee";
$migrate_dedup_users["megacal"] = "cmcgaugh";
$migrate_dedup_users["const"] = "kostas100";
$migrate_dedup_users["ngaudenzi"] = "puppetmaster";
$migrate_dedup_users["mroguski"] = "kaelthas";
$migrate_dedup_users["brdf"] = "origin";
$migrate_dedup_users["davis3d"] = "davis";
$migrate_dedup_users["rldigital"] = "locatelli";
$migrate_dedup_users["tomforsythe"] = "gallifrey77203";
$migrate_dedup_users["gbrnk"] = "benoe";
$migrate_dedup_users["arekkasprzyk"] = "kasperski";
$migrate_dedup_users["imbusy1"] = "imbusy";
$migrate_dedup_users["mfoxdoggg"] = "mfoxdogg";
$migrate_dedup_users["knusk"] = "kanutus";
$migrate_dedup_users["tomekk"] = "anders211";
$migrate_dedup_users["kitsueb"] = "kitsu_eb";
$migrate_dedup_users["slugzzz"] = "tsquar3d";
$migrate_dedup_users["moore500"] = "mmoore500";
$migrate_dedup_users["verumbra"] = "sebastian0";
$migrate_dedup_users["blenderbug"] = "nikola";
$migrate_dedup_users["adailtoncomp"] = "adailton";
$migrate_dedup_users["mchs3d"] = "abtrumpet";
// disabled users who have tasks
$migrate_dedup_users["sjoerd"] = "sjoerddevries";
$migrate_dedup_users["matali"] = "mat_ali";
$migrate_dedup_users["voicelesscushio"] = "None";
$migrate_dedup_users["bigben0328"] = "None";
$migrate_dedup_users["santamouse"] = "None";
$migrate_dedup_users["andreanckaert"] = "None";
$migrate_dedup_users["yesmydear"] = "None";
$migrate_dedup_users["spacetug"] = "None";
$migrate_dedup_users["omegafold"] = "None";
// testing
$migrate_dedup_users["blendix_rename_test_a"] = "blendix";
$migrate_dedup_users["blendix_rename_test_b"] = "blendix";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -54,6 +54,8 @@ return array(
'javelin-behavior-aphlict-dropdown',
'javelin-behavior-history-install',
'javelin-behavior-phabricator-gesture',
'javelin-behavior-phabricator-active-nav',
'javelin-behavior-phabricator-nav',
'javelin-behavior-phabricator-remarkup-assist',
'phabricator-textareautils',
'phabricator-file-upload',
@@ -86,12 +88,6 @@ return array(
'javelin-behavior-aphlict-status',
'javelin-behavior-user-menu',
'phabricator-favicon',
'javelin-behavior-phui-tab-group',
'javelin-behavior-phui-submenu',
'phuix-button-view',
'javelin-behavior-comment-actions',
'phuix-form-control-view',
'phuix-autocomplete',
),
'core.pkg.css' => array(
'phabricator-core-css',
@@ -162,14 +158,9 @@ return array(
'phabricator-feed-css',
'phabricator-dashboard-css',
'aphront-multi-column-view-css',
'phui-curtain-object-ref-view-css',
'phui-comment-form-css',
'phui-head-thing-view-css',
'conpherence-durable-column-view',
'phui-button-bar-css',
),
'conpherence.pkg.css' => array(
'conpherence-durable-column-view',
'conpherence-menu-css',
'conpherence-color-css',
'conpherence-message-pane-css',
@@ -196,8 +187,7 @@ return array(
'phabricator-content-source-view-css',
'inline-comment-summary-css',
'phui-inline-comment-view-css',
'diff-tree-view-css',
'phui-formation-view-css',
'phabricator-filetree-view-css',
),
'differential.pkg.js' => array(
'phabricator-drag-and-drop-file-upload',
@@ -214,14 +204,6 @@ return array(
'phabricator-diff-inline',
'phabricator-diff-changeset',
'phabricator-diff-changeset-list',
'phabricator-diff-tree-view',
'phabricator-diff-path-view',
'phuix-formation-view',
'phuix-formation-column-view',
'phuix-formation-flank-view',
'javelin-external-editor-link-engine',
),
'diffusion.pkg.css' => array(
'diffusion-icons-css',
@@ -238,9 +220,4 @@ return array(
'javelin-behavior-maniphest-batch-selector',
'javelin-behavior-maniphest-list-editor',
),
'dark-console.pkg.js' => array(
'javelin-behavior-dark-console',
'phabricator-darklog',
'phabricator-darkmessage',
),
);

View File

@@ -7,6 +7,7 @@ $status_map = array(
3 => 'invalid',
4 => 'duplicate',
5 => 'spite',
123450 => 'archived',
);
$conn_w = id(new ManiphestTask())->establishConnection('w');

View File

@@ -16,9 +16,6 @@ foreach (new LiskMigrationIterator($table) as $doc) {
continue;
}
$new_view_policy = $default_view_policy;
$new_edit_policy = $default_edit_policy;
// If this was previously a magical project wiki page (under projects/, but
// not projects/ itself) we need to apply the project policies. Otherwise,
// apply the default policies.
@@ -38,24 +35,26 @@ foreach (new LiskMigrationIterator($table) as $doc) {
->executeOne();
if ($project) {
$view_policy = nonempty($project->getViewPolicy(), $default_view_policy);
$edit_policy = nonempty($project->getEditPolicy(), $default_edit_policy);
$new_view_policy = $view_policy;
$new_edit_policy = $edit_policy;
$project_name = $project->getName();
echo pht(
"Migrating document %d to project policy %s...\n",
$id,
$project_name);
$doc->setViewPolicy($view_policy);
$doc->setEditPolicy($edit_policy);
$doc->save();
continue;
}
}
echo pht('Migrating document %d to new policy...', $id)."\n";
queryfx(
$conn_w,
'UPDATE %R SET viewPolicy = %s, editPolicy = %s
WHERE id = %d',
$table,
$new_view_policy,
$new_edit_policy,
$id);
echo pht('Migrating document %d to default install policy...', $id)."\n";
$doc->setViewPolicy($default_view_policy);
$doc->setEditPolicy($default_edit_policy);
$doc->save();
}
echo pht('Done.')."\n";

View File

@@ -40,8 +40,7 @@ foreach ($lists as $list) {
if (!$username_okay) {
echo pht(
'Failed to migrate mailing list "%s": unable to generate a unique '.
'username for it.',
$name)."\n";
'username for it.')."\n";
continue;
}

View File

@@ -4,8 +4,18 @@
// underlying file data were not written correctly. This restores edges for
// any missing pastes.
// See T13510. The "pastebin" database was later renamed to "paste", which
// broke this migration. The migration was removed in 2020 since it seems
// plausible that zero installs are impacted (only installs that ran code
// from November 2015 and have not upgraded in five years could possibly be
// impacted).
$table = new PhabricatorPaste();
$edge_type = PhabricatorObjectHasFileEdgeType::EDGECONST;
foreach (new LiskMigrationIterator($table) as $paste) {
$paste_phid = $paste->getPHID();
$file_phid = $paste->getFilePHID();
if (!$file_phid) {
continue;
}
id(new PhabricatorEdgeEditor())
->addEdge($paste_phid, $edge_type, $file_phid)
->save();
}

View File

@@ -1,10 +0,0 @@
CREATE TABLE {$NAMESPACE}_user.user_externalaccountidentifier (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
externalAccountPHID VARBINARY(64) NOT NULL,
providerConfigPHID VARBINARY(64) NOT NULL,
identifierHash BINARY(12) NOT NULL,
identifierRaw LONGTEXT NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};

View File

@@ -1,40 +0,0 @@
<?php
$account_table = new PhabricatorExternalAccount();
$identifier_table = new PhabricatorExternalAccountIdentifier();
$conn = $account_table->establishConnection('w');
$table_name = $account_table->getTableName();
$iterator = new LiskRawMigrationIterator($conn, $table_name);
foreach ($iterator as $account_row) {
// We don't need to migrate "accountID" values for "password" accounts,
// since these were dummy values in the first place and are no longer
// read or written after D21014. (There would be no harm in writing these
// rows, but it's easy to skip them.)
if ($account_row['accountType'] === 'password') {
continue;
}
$account_id = $account_row['accountID'];
if (!strlen($account_id)) {
continue;
}
queryfx(
$conn,
'INSERT IGNORE INTO %R (
phid, externalAccountPHID, providerConfigPHID,
identifierHash, identifierRaw,
dateCreated, dateModified)
VALUES (%s, %s, %s, %s, %s, %d, %d)',
$identifier_table,
$identifier_table->generatePHID(),
$account_row['phid'],
$account_row['providerConfigPHID'],
PhabricatorHash::digestForIndex($account_id),
$account_id,
$account_row['dateCreated'],
$account_row['dateModified']);
}

View File

@@ -1,21 +0,0 @@
<?php
// See T13493. This table previously had a UNIQUE KEY on "<accountType,
// accountDomain, accountID>", which is obsolete. The application now violates
// this key, so make sure it gets dropped.
// There's no "IF EXISTS" modifier for "ALTER TABLE" so run this as a PHP patch
// instead of an SQL patch.
$table = new PhabricatorExternalAccount();
$conn = $table->establishConnection('w');
try {
queryfx(
$conn,
'ALTER TABLE %R DROP KEY %T',
$table,
'account_details');
} catch (AphrontQueryException $ex) {
// Ignore.
}

View File

@@ -1,9 +0,0 @@
CREATE TABLE {$NAMESPACE}_paste.paste_paste_fdocument (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectPHID VARBINARY(64) NOT NULL,
isClosed BOOL NOT NULL,
authorPHID VARBINARY(64),
ownerPHID VARBINARY(64),
epochCreated INT UNSIGNED NOT NULL,
epochModified INT UNSIGNED NOT NULL
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -1,8 +0,0 @@
CREATE TABLE {$NAMESPACE}_paste.paste_paste_ffield (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
documentID INT UNSIGNED NOT NULL,
fieldKey VARCHAR(4) NOT NULL COLLATE {$COLLATE_TEXT},
rawCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT},
termCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT},
normalCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT}
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -1,5 +0,0 @@
CREATE TABLE {$NAMESPACE}_paste.paste_paste_fngrams (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
documentID INT UNSIGNED NOT NULL,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT}
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -1,7 +0,0 @@
CREATE TABLE {$NAMESPACE}_paste.paste_paste_fngrams_common (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
needsCollection BOOL NOT NULL,
UNIQUE KEY `key_ngram` (ngram),
KEY `key_collect` (needsCollection)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -1,9 +0,0 @@
CREATE TABLE {$NAMESPACE}_differential.differential_viewstate (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
viewerPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewState LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_viewer` (viewerPHID, objectPHID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -1,2 +0,0 @@
ALTER TABLE {$NAMESPACE}_differential.differential_transaction_comment
ADD attributes LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};

View File

@@ -1,2 +0,0 @@
ALTER TABLE {$NAMESPACE}_audit.audit_transaction_comment
ADD attributes LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};

View File

@@ -1,2 +0,0 @@
UPDATE {$NAMESPACE}_differential.differential_transaction_comment
SET attributes = '{}' WHERE attributes = '';

View File

@@ -1,2 +0,0 @@
UPDATE {$NAMESPACE}_audit.audit_transaction_comment
SET attributes = '{}' WHERE attributes = '';

View File

@@ -1 +0,0 @@
DROP TABLE {$NAMESPACE}_differential.differential_changeset_parse_cache;

View File

@@ -1,7 +0,0 @@
CREATE TABLE {$NAMESPACE}_differential.differential_changeset_parse_cache (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
cacheIndex BINARY(12) NOT NULL,
cache LONGBLOB NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
UNIQUE KEY `key_cacheIndex` (cacheIndex)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -1 +0,0 @@
DROP TABLE IF EXISTS {$NAMESPACE}_differential.differential_commit;

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/init/init-script.php';
$xml = $root.'/externals/cldr/cldr_windows_timezones.xml';
$xml = Filesystem::readFile($xml);
$xml = new SimpleXMLElement($xml);
$result_map = array();
$ignore = array(
'UTC',
'UTC-11',
'UTC-02',
'UTC-08',
'UTC-09',
'UTC+12',
);
$ignore = array_fuse($ignore);
$zones = $xml->windowsZones->mapTimezones->mapZone;
foreach ($zones as $zone) {
$windows_name = (string)$zone['other'];
$target_name = (string)$zone['type'];
// Ignore the offset-based timezones from the CLDR map, since we handle
// these later.
if (isset($ignore[$windows_name])) {
continue;
}
// We've already seen this timezone so we don't need to add it to the map
// again.
if (isset($result_map[$windows_name])) {
continue;
}
$result_map[$windows_name] = $target_name;
}
asort($result_map);
echo id(new PhutilJSON())
->encodeFormatted($result_map);

View File

@@ -1,126 +0,0 @@
{
"Egypt Standard Time": "Africa/Cairo",
"Morocco Standard Time": "Africa/Casablanca",
"South Africa Standard Time": "Africa/Johannesburg",
"W. Central Africa Standard Time": "Africa/Lagos",
"E. Africa Standard Time": "Africa/Nairobi",
"Libya Standard Time": "Africa/Tripoli",
"Namibia Standard Time": "Africa/Windhoek",
"Aleutian Standard Time": "America/Adak",
"Alaskan Standard Time": "America/Anchorage",
"Tocantins Standard Time": "America/Araguaina",
"Paraguay Standard Time": "America/Asuncion",
"Bahia Standard Time": "America/Bahia",
"SA Pacific Standard Time": "America/Bogota",
"Argentina Standard Time": "America/Buenos_Aires",
"Eastern Standard Time (Mexico)": "America/Cancun",
"Venezuela Standard Time": "America/Caracas",
"SA Eastern Standard Time": "America/Cayenne",
"Central Standard Time": "America/Chicago",
"Mountain Standard Time (Mexico)": "America/Chihuahua",
"Central Brazilian Standard Time": "America/Cuiaba",
"Mountain Standard Time": "America/Denver",
"Greenland Standard Time": "America/Godthab",
"Turks And Caicos Standard Time": "America/Grand_Turk",
"Central America Standard Time": "America/Guatemala",
"Atlantic Standard Time": "America/Halifax",
"Cuba Standard Time": "America/Havana",
"US Eastern Standard Time": "America/Indianapolis",
"SA Western Standard Time": "America/La_Paz",
"Pacific Standard Time": "America/Los_Angeles",
"Central Standard Time (Mexico)": "America/Mexico_City",
"Saint Pierre Standard Time": "America/Miquelon",
"Montevideo Standard Time": "America/Montevideo",
"Eastern Standard Time": "America/New_York",
"US Mountain Standard Time": "America/Phoenix",
"Haiti Standard Time": "America/Port-au-Prince",
"Canada Central Standard Time": "America/Regina",
"Pacific SA Standard Time": "America/Santiago",
"E. South America Standard Time": "America/Sao_Paulo",
"Newfoundland Standard Time": "America/St_Johns",
"Pacific Standard Time (Mexico)": "America/Tijuana",
"Central Asia Standard Time": "Asia/Almaty",
"Jordan Standard Time": "Asia/Amman",
"Arabic Standard Time": "Asia/Baghdad",
"Azerbaijan Standard Time": "Asia/Baku",
"SE Asia Standard Time": "Asia/Bangkok",
"Altai Standard Time": "Asia/Barnaul",
"Middle East Standard Time": "Asia/Beirut",
"India Standard Time": "Asia/Calcutta",
"Transbaikal Standard Time": "Asia/Chita",
"Sri Lanka Standard Time": "Asia/Colombo",
"Syria Standard Time": "Asia/Damascus",
"Bangladesh Standard Time": "Asia/Dhaka",
"Arabian Standard Time": "Asia/Dubai",
"West Bank Standard Time": "Asia/Hebron",
"W. Mongolia Standard Time": "Asia/Hovd",
"North Asia East Standard Time": "Asia/Irkutsk",
"Israel Standard Time": "Asia/Jerusalem",
"Afghanistan Standard Time": "Asia/Kabul",
"Russia Time Zone 11": "Asia/Kamchatka",
"Pakistan Standard Time": "Asia/Karachi",
"Nepal Standard Time": "Asia/Katmandu",
"North Asia Standard Time": "Asia/Krasnoyarsk",
"Magadan Standard Time": "Asia/Magadan",
"N. Central Asia Standard Time": "Asia/Novosibirsk",
"Omsk Standard Time": "Asia/Omsk",
"North Korea Standard Time": "Asia/Pyongyang",
"Myanmar Standard Time": "Asia/Rangoon",
"Arab Standard Time": "Asia/Riyadh",
"Sakhalin Standard Time": "Asia/Sakhalin",
"Korea Standard Time": "Asia/Seoul",
"China Standard Time": "Asia/Shanghai",
"Singapore Standard Time": "Asia/Singapore",
"Russia Time Zone 10": "Asia/Srednekolymsk",
"Taipei Standard Time": "Asia/Taipei",
"West Asia Standard Time": "Asia/Tashkent",
"Georgian Standard Time": "Asia/Tbilisi",
"Iran Standard Time": "Asia/Tehran",
"Tokyo Standard Time": "Asia/Tokyo",
"Tomsk Standard Time": "Asia/Tomsk",
"Ulaanbaatar Standard Time": "Asia/Ulaanbaatar",
"Vladivostok Standard Time": "Asia/Vladivostok",
"Yakutsk Standard Time": "Asia/Yakutsk",
"Ekaterinburg Standard Time": "Asia/Yekaterinburg",
"Caucasus Standard Time": "Asia/Yerevan",
"Azores Standard Time": "Atlantic/Azores",
"Cape Verde Standard Time": "Atlantic/Cape_Verde",
"Greenwich Standard Time": "Atlantic/Reykjavik",
"Cen. Australia Standard Time": "Australia/Adelaide",
"E. Australia Standard Time": "Australia/Brisbane",
"AUS Central Standard Time": "Australia/Darwin",
"Aus Central W. Standard Time": "Australia/Eucla",
"Tasmania Standard Time": "Australia/Hobart",
"Lord Howe Standard Time": "Australia/Lord_Howe",
"W. Australia Standard Time": "Australia/Perth",
"AUS Eastern Standard Time": "Australia/Sydney",
"Dateline Standard Time": "Etc/GMT+12",
"Astrakhan Standard Time": "Europe/Astrakhan",
"W. Europe Standard Time": "Europe/Berlin",
"GTB Standard Time": "Europe/Bucharest",
"Central Europe Standard Time": "Europe/Budapest",
"E. Europe Standard Time": "Europe/Chisinau",
"Turkey Standard Time": "Europe/Istanbul",
"Kaliningrad Standard Time": "Europe/Kaliningrad",
"FLE Standard Time": "Europe/Kiev",
"GMT Standard Time": "Europe/London",
"Belarus Standard Time": "Europe/Minsk",
"Russian Standard Time": "Europe/Moscow",
"Romance Standard Time": "Europe/Paris",
"Russia Time Zone 3": "Europe/Samara",
"Central European Standard Time": "Europe/Warsaw",
"Mauritius Standard Time": "Indian/Mauritius",
"Samoa Standard Time": "Pacific/Apia",
"New Zealand Standard Time": "Pacific/Auckland",
"Bougainville Standard Time": "Pacific/Bougainville",
"Chatham Islands Standard Time": "Pacific/Chatham",
"Easter Island Standard Time": "Pacific/Easter",
"Fiji Standard Time": "Pacific/Fiji",
"Central Pacific Standard Time": "Pacific/Guadalcanal",
"Hawaiian Standard Time": "Pacific/Honolulu",
"Line Islands Standard Time": "Pacific/Kiritimati",
"Marquesas Standard Time": "Pacific/Marquesas",
"Norfolk Standard Time": "Pacific/Norfolk",
"West Pacific Standard Time": "Pacific/Port_Moresby",
"Tonga Standard Time": "Pacific/Tongatapu"
}

View File

@@ -0,0 +1,172 @@
#!/usr/local/bin/php
<?php
#
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# The Original Code is Copyright (C) 2019, Blender Foundation
# All rights reserved.
#
# Contributor(s): Sergey Sharybin.
#
# ***** END GPL LICENSE BLOCK *****
#
# This scripts implements external AuthBasicProvider which can be used
# to authentificate users using Phabricator's database.
#
# Example configuration:
#
# .htaccess file:
#
# AuthType Basic
# AuthName "Please Enter Password"
# AuthBasicProvider external
# AuthExternal phabricator
# Require valid-user
#
# It is also required to have the following provider registered in the
# configuration. There are two ways to do it:
#
# 1. Separate conf file in /etc/apache2/conf-enabled
# Create a file (say, authz_external_phabricator.conf) with the following
# content:
#
# DefineExternalAuth phabricator pipe /path/to/auth_provider.php
#
# This method allows to use provider in any VHOST.
#
# Downside: from local tests .htaccess file picks the provider nicely,
# but attempts to specifu the provider in virtual host configuration
# resulted in issues (seems to be different initialization order between
# global config in those files and virtual hosts).
#
# 2. Define provider in the virtual host definition:
#
# <IfModule mod_authnz_external.c>
# AddExternalAuth phabricator /path/to/auth_provider.php
# SetExternalAuthMethod phabricator pipe
# </IfModule>
#
# This method allowed to use this auth provider for SVN DAV.
$IS_DEBUG = false;
$PHABRICATOR_ROOT = dirname(dirname(dirname(__FILE__)));
require_once $PHABRICATOR_ROOT.'/scripts/__init_script__.php';
function initLogging() {
global $IS_DEBUG;
global $argv;
for ($i = 1; $i < count($argv); ++$i) {
if ($argv[$i] == '--debug') {
$IS_DEBUG = true;
}
}
}
function debugLog(string $text) {
global $IS_DEBUG;
if (!$IS_DEBUG) {
return;
}
print("${text}\n");
}
function removeSingleTrailingNewline(string $string) {
if ($string === '') {
return $string;
}
$length = strlen($string);
if ($string[$length - 1] == "\n") {
return substr($string, 0, -1);
}
return $string;
}
class AuthRequest {
public $user_name;
public $password;
static function fromStdin() {
$auth_request = new AuthRequest();
$stdin = fopen('php://stdin', 'r');
$auth_request->user_name = removeSingleTrailingNewline(fgets($stdin));
$auth_request->password = removeSingleTrailingNewline(fgets($stdin));
return $auth_request;
}
};
function getUserForAuthRequest(AuthRequest $auth_request) {
if ($auth_request->user_name === '') {
return null;
}
$username_or_email = $auth_request->user_name;
$user = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$username_or_email);
if (!$user) {
$user = PhabricatorUser::loadOneWithEmailAddress(
$username_or_email);
}
return $user;
}
function createContentSourceForAuth() {
return PhabricatorContentSource::newForSource(
PhabricatorUnitTestContentSource::SOURCECONST);
}
function isValidAuth(AuthRequest $auth_request) {
debugLog("Begin authentification check for user '$auth_request->user_name'");
$user = getUserForAuthRequest($auth_request);
if (!$user) {
debugLog("No PhabricatorUser() object found for requested user.");
return false;
}
$content_source = createContentSourceForAuth();
$envelope = new PhutilOpaqueEnvelope($auth_request->password);
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($user)
->setContentSource($content_source)
->setPasswordType(PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT)
->setObject($user);
if (!$engine->isValidPassword($envelope)) {
debugLog('Engine reported invalid password.');
return false;
}
debugLog('Authentirfication passed.');
return true;
}
function main() {
initLogging();
$auth_request = AuthRequest::fromStdin();
if (!isValidAuth($auth_request)) {
exit(1);
}
exit(0);
}
main();
?>

View File

@@ -1,131 +0,0 @@
#!/usr/bin/env php
<?php
if (function_exists('pcntl_async_signals')) {
pcntl_async_signals(true);
} else {
declare(ticks = 1);
}
require_once dirname(__FILE__).'/../../__init_script__.php';
if (!posix_isatty(STDOUT)) {
$sid = posix_setsid();
if ($sid <= 0) {
throw new Exception(pht('Failed to create new process session!'));
}
}
$args = new PhutilArgumentParser($argv);
$args->setTagline(pht('daemon executor'));
$args->setSynopsis(<<<EOHELP
**exec_daemon.php** [__options__] __daemon__ ...
Run an instance of __daemon__.
EOHELP
);
$args->parse(
array(
array(
'name' => 'trace',
'help' => pht('Enable debug tracing.'),
),
array(
'name' => 'trace-memory',
'help' => pht('Enable debug memory tracing.'),
),
array(
'name' => 'verbose',
'help' => pht('Enable verbose activity logging.'),
),
array(
'name' => 'label',
'short' => 'l',
'param' => 'label',
'help' => pht(
'Optional process label. Makes "%s" nicer, no behavioral effects.',
'ps'),
),
array(
'name' => 'daemon',
'wildcard' => true,
),
));
$trace_memory = $args->getArg('trace-memory');
$trace_mode = $args->getArg('trace') || $trace_memory;
$verbose = $args->getArg('verbose');
if (function_exists('posix_isatty') && posix_isatty(STDIN)) {
fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n");
}
$config = @file_get_contents('php://stdin');
$config = id(new PhutilJSONParser())->parse($config);
PhutilTypeSpec::checkMap(
$config,
array(
'log' => 'optional string|null',
'argv' => 'optional list<wild>',
'load' => 'optional list<string>',
'down' => 'optional int',
));
$log = idx($config, 'log');
if ($log) {
ini_set('error_log', $log);
PhutilErrorHandler::setErrorListener(array('PhutilDaemon', 'errorListener'));
}
$load = idx($config, 'load', array());
foreach ($load as $library) {
$library = Filesystem::resolvePath($library);
phutil_load_library($library);
}
PhutilErrorHandler::initialize();
$daemon = $args->getArg('daemon');
if (!$daemon) {
throw new PhutilArgumentUsageException(
pht('Specify which class of daemon to start.'));
} else if (count($daemon) > 1) {
throw new PhutilArgumentUsageException(
pht('Specify exactly one daemon to start.'));
} else {
$daemon = head($daemon);
if (!class_exists($daemon)) {
throw new PhutilArgumentUsageException(
pht(
'No class "%s" exists in any known library.',
$daemon));
} else if (!is_subclass_of($daemon, 'PhutilDaemon')) {
throw new PhutilArgumentUsageException(
pht(
'Class "%s" is not a subclass of "%s".',
$daemon,
'PhutilDaemon'));
}
}
$argv = idx($config, 'argv', array());
$daemon = newv($daemon, array($argv));
if ($trace_mode) {
$daemon->setTraceMode();
}
if ($trace_memory) {
$daemon->setTraceMemory();
}
if ($verbose) {
$daemon->setVerbose(true);
}
$down_duration = idx($config, 'down');
if ($down_duration) {
$daemon->setScaledownDuration($down_duration);
}
$daemon->execute();

6
scripts/gitadmin/gitx-ssh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/local/bin/bash
set -e
set -u
ssh -i $SSH_KEYFILE $@

View File

@@ -0,0 +1,316 @@
#!/usr/local/bin/php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
function escape_name($name) {
return preg_replace('/[^A-Za-z0-9\-]/', '_', $name);
}
function startswith($string, $prefix) {
return substr($string, 0, strlen($prefix)) == $prefix;
}
function endswith($string, $suffix) {
$suffix_length = strlen($suffix);
return substr($string, strlen($string) - $suffix_length,
$suffix_length) == $suffix;
}
function write_ini_file($array, $file) {
$res = array();
foreach ($array as $key => $val) {
if (is_array($val)) {
$res[] = "[$key]";
foreach ($val as $skey => $sval) {
$res[] = "$skey = $sval";
}
$res[] = '';
} else {
$res[] = "$key = $val";
}
}
file_put_contents($file, implode("\n", $res));
}
// Get user's heys and put them to the configuration
function handleSingleUserPHID(
$keydir, $viewer, $userPHID, $system_keys, &$used_keys) {
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($userPHID))
->executeOne();
if (!$user) {
return array();
}
if ($user->getIsDisabled()) {
return array();
}
$keys = id(new PhabricatorAuthSSHKey())->loadAllWhere(
'objectPHID = %s',
$user->getPHID());
$members = array();
foreach ($keys as $key) {
$full_key_content =
$key->getKeyType().' '.
$key->getKeyBody().' '.
$key->getKeyComment()."\n";
if (array_key_exists($full_key_content, $system_keys)) {
$members[] = $system_keys[$full_key_content];
} else {
$escaped_key_name = escape_name($key->getName());
$member = 'PHAB_'.$user->getUserName().
'_'.$escaped_key_name.
'_'.$key->getID();
$members[] = $member;
if (!array_key_exists($member, $used_keys)) {
$used_keys[$member] = true;
file_put_contents("$keydir/$member.pub", $full_key_content);
}
}
}
return $members;
}
function handleCustomPolicy(
$keydir, $viewer, $policy, $system_keys, &$used_keys) {
$members = array();
$rules = $policy->getRules();
foreach ($rules as $rule) {
// Everyone is denied by default anyway
if ($rule['action'] == 'allow') {
if ($rule['rule'] == 'PhabricatorPolicyRuleUsers') {
foreach ($rule['value'] as $userPHID) {
$members = array_merge($members,
handleSingleUserPHID($keydir, $viewer, $userPHID,
$system_keys, $used_keys));
}
} else {
/* pass */
}
}
}
return $members;
}
// Parse repository and put it's members to the config file
function handleSingleRepository(
$keydir, $viewer, $repository, $all_repositories, $system_keys,
&$new_configuration, &$used_keys) {
$policies = PhabricatorPolicyQuery::loadPolicies(
$viewer,
$repository);
$pushable = $policies[DiffusionPushCapability::CAPABILITY];
$type = $pushable->getType();
$members = array();
if ($type == PhabricatorPolicyType::TYPE_PROJECT) {
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->needMembers(true)
->withPHIDs(array($pushable->getPHID()))
->executeOne();
$memberPHIDs = $project->getMemberPHIDs();
foreach ($memberPHIDs as $memberPHID) {
$members = array_merge($members,
handleSingleUserPHID($keydir, $viewer, $memberPHID,
$system_keys, $used_keys));
}
} else if ($type == PhabricatorPolicyType::TYPE_USER) {
$members = handleSingleUserPHID(
$keydir, $viewer, $pushable->getPHID(), $system_keys, $used_keys);
} else if ($type == PhabricatorPolicyType::TYPE_CUSTOM) {
$members = handleCustomPolicy(
$keydir, $viewer, $pushable, $system_keys, $used_keys);
} else {
/* pass */
}
if (count($members)) {
$uri = $repository->getRemoteURI();
$repository_name = basename($uri, '.git');
$escaped_repository_name = escape_name($repository->getName());
$group_name = "PHAB_${escaped_repository_name}";
$values = array();
$values['members'] = join(' ', $members);
$values['readonly'] = join(' ', $all_repositories);
$values['writable'] = $repository_name;
$new_configuration["group $group_name"] = $values;
}
}
// Remove groups from previous automated configuration built
function getCleanOldConfiguration($old_configuration) {
$new_configuration = array();
foreach ($old_configuration as $group => $values) {
if (!startswith($group, 'group PHAB')) {
$new_configuration[$group] = $values;
}
}
return $new_configuration;
}
// Get non-phab keys
function getSystemPublicKeys($keydir) {
$files = scandir($keydir);
$system_keys = array();
foreach ($files as $file) {
if (!startswith($file, "PHAB") && endswith($file, '.pub')) {
$key = file_get_contents("$keydir/$file");
$system_keys[$key] = basename($file, '.pub');
}
}
return $system_keys;
}
// Remove unused public keys
function removeUnusedPublicKeys($keydir, $used_keys) {
$files = scandir($keydir);
foreach ($files as $file) {
if (startswith($file, "PHAB")) {
$member = basename($file, '.pub');
if (!array_key_exists($member, $used_keys)) {
unlink("$keydir/$file");
}
}
}
}
function rebuildConfiguration($gitosis_root) {
$keydir = "$gitosis_root/keydir";
$configuration_file = "$gitosis_root/gitosis.conf";
if (!file_exists($configuration_file)) {
print("Not found: $configuration_file\n");
return false;
}
$viewer = id(new PhabricatorUser())
->loadOneWhere('username = %s', 'sergey');
$old_configuration = parse_ini_file(
$configuration_file, true, INI_SCANNER_RAW);
$new_configuration = getCleanOldConfiguration(
$old_configuration);
// Get "system" keys to re-use if phab account uses the
// same public key
$system_keys = getSystemPublicKeys($keydir);
// Get list of all repos which is awailable for read
$all_repositories = array();
foreach ($old_configuration as $group => $values) {
if (startswith($group, 'repo')) {
$repository_name = substr($group, 5, strlen($group) - 5);
if ($repository_name == 'gitosis-admin')
continue;
$all_repositories[] = $repository_name;
}
}
// Fill in new configuration and keys
$used_keys = array();
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->execute();
foreach ($repositories as $repository_id => $repository) {
$type = $repository->getVersionControlSystem();
if ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) {
handleSingleRepository(
$keydir, $viewer, $repository, $all_repositories, $system_keys,
$new_configuration, $used_keys);
}
}
write_ini_file($new_configuration, $configuration_file);
removeUnusedPublicKeys($keydir, $used_keys);
return true;
}
function getGitCommand($repository) {
$git_dir = realpath("$repository/.git");
$git = "git --git-dir='$git_dir'";
$git .= ' --work-tree='.realpath($repository);
return $git;
}
function runGitCommand($repository, $arguments,
&$output=null, &$return_var=null) {
$git = getGitCommand($repository);
$git .= " $arguments";
exec($git, $output, $return_var);
return $return_var == 0;
}
function runGitSshCommand($repository, $key, $arguments,
&$output=null, &$return_var=null) {
$gitx_ssh = realpath(dirname(__FILE__) . "/gitx-ssh");
$abs_key = realpath($key);
$git = "SSH_KEYFILE=$abs_key GIT_SSH=$gitx_ssh ";
$git .= getGitCommand($repository);
$git .= " $arguments";
exec($git, $output, $return_var);
return $return_var == 0;
}
function repositoryPull($repository, $key) {
return runGitSshCommand($repository, $key, 'pull');
}
function repositoryCommitAll($repository, $author, $message) {
if (!runGitCommand(
$repository, 'ls-files --other --exclude-standard', $untracked_files)) {
return false;
}
if (count($untracked_files)) {
$flat_files = join(' ', $untracked_files);
if (!runGitCommand($repository, "add $flat_files")) {
return false;
}
}
runGitCommand($repository, "update-index -q --refresh", $output);
runGitCommand($repository, "diff-index --name-only HEAD --", $output);
if (count($output)) {
return runGitCommand(
$repository, "commit --author='$author' -a -m '$message'", $output);
}
return true;
}
if (count($argv) != 3) {
print("Usage: {$argv[0]} /path/to/gitosis-admin /path/to/id_rsa.pub\n");
exit(1);
}
$gitosis_root = $argv[1];
$key = $argv[2];
if (!repositoryPull($gitosis_root, $key)) {
print("Failed to pull changes from server.\n");
exit(1);
}
if (!rebuildConfiguration($gitosis_root)) {
exit(1);
}
if (!repositoryCommitAll(
$gitosis_root, 'Rebuild Gitadmin <null@git.blender.org>',
'Update to correspond changes in Phabricator')) {
print("Failed to commit changes.\n");
exit(1);
}
runGitSshCommand($gitosis_root, $key, 'push origin master');
?>

View File

@@ -8,14 +8,10 @@ function init_phabricator_script(array $options) {
ini_set(
'include_path',
$include_path.PATH_SEPARATOR.dirname(__FILE__).'/../../../');
$ok = @include_once 'arcanist/support/init/init-script.php';
if (!$ok) {
echo
'FATAL ERROR: Unable to load the "Arcanist" library. '.
'Put "arcanist/" next to "phabricator/" on disk.';
echo "\n";
@include_once 'libphutil/scripts/__init_script__.php';
if (!@constant('__LIBPHUTIL__')) {
echo "ERROR: Unable to load libphutil. Update your PHP 'include_path' to ".
"include the parent directory of libphutil/.\n";
exit(1);
}

View File

@@ -22,7 +22,7 @@ failed() {
}
ISSUE=`cat /etc/issue`
if [[ $ISSUE != Ubuntu* ]]
if [[ ($ISSUE != Ubuntu*) && ($ISSUE != Debian*) ]];
then
echo "This script is intended for use on Ubuntu, but this system appears";
echo "to be something else. Your results may vary.";

View File

@@ -211,29 +211,21 @@ try {
->setUniqueMethod('getName')
->execute();
$command_list = array_keys($workflows);
$command_list = implode(', ', $command_list);
$error_lines = array();
$error_lines[] = pht('Welcome to Phabricator.');
$error_lines[] = pht(
'You are logged in as %s.',
$user_name);
if (!$original_argv) {
$error_lines[] = pht(
'You have not specified a command to run. This means you are requesting '.
'an interactive shell, but Phabricator does not provide interactive '.
'shells over SSH.');
$error_lines[] = pht(
'(Usually, you should run a command like "git clone" or "hg push" '.
'instead of connecting directly with SSH.)');
$error_lines[] = pht(
'Supported commands are: %s.',
$command_list);
$error_lines = implode("\n\n", $error_lines);
throw new PhutilArgumentUsageException($error_lines);
throw new Exception(
pht(
"Welcome to Phabricator.\n\n".
"You are logged in as %s.\n\n".
"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 SSH.\n\n".
"Usually, you should run a command like `%s` or `%s` ".
"rather than connecting directly with SSH.\n\n".
"Supported commands are: %s.",
$user_name,
'git clone',
'hg push',
implode(', ', array_keys($workflows))));
}
$log_argv = implode(' ', $original_argv);
@@ -255,20 +247,7 @@ try {
$parsed_args = new PhutilArgumentParser($parseable_argv);
if (empty($workflows[$command])) {
$error_lines[] = pht(
'You have specified the command "%s", but that command is not '.
'supported by Phabricator. As received by Phabricator, your entire '.
'argument list was:',
$command);
$error_lines[] = csprintf(' $ ssh ... -- %Ls', $parseable_argv);
$error_lines[] = pht(
'Supported commands are: %s.',
$command_list);
$error_lines = implode("\n\n", $error_lines);
throw new PhutilArgumentUsageException($error_lines);
throw new Exception(pht('Invalid command.'));
}
$workflow = $parsed_args->parseWorkflows($workflows);

View File

@@ -0,0 +1,61 @@
<?php
$ARCHIVED_REPOS = array(
'abstractmesh',
'beast',
# 'bf-blender',
'bf-docboard-es',
'bf-extensions',
'bf-funboard',
'bf-scripts',
# 'bf-translations',
'bfct',
'bforge',
'blend-doc',
'blend2cs',
'blendedmidi',
'blendercad',
'blendxml',
'bzoo',
'docboard',
'drqueue',
'ghost',
'girona',
'guiman',
'lsystem',
'magic',
'makeh',
'mechanicblender',
'neverblender',
'news',
'night',
'nitrox',
'osgexport',
'peerrating',
'piovra',
'pyverse',
'qdune',
'scolblender',
'skined',
'smdio',
'soapyblender',
'soc-2005',
'soc-2006',
'soc-2007',
'soc-2008',
'sourceforge',
'spe',
'stats',
'ter2blend',
'torqueexporter',
'tuhopuu',
'tzuray',
'vectex',
'vectorrender',
'verse',
'vrmlimportexp',
'warblender',
'wpyre',
'yafray',
'yofrankie'
);
?>

View File

@@ -0,0 +1,181 @@
#!/usr/local/bin/php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
function getSVNRepositoryName($repository) {
$uri = $repository->getRemoteURI();
return preg_replace(
'/https?\:\/\/.*?\/svnroot\/([^\/]+)\/?.*/', '$1', $uri);
}
// Get user's heys and put them to the configuration
function handleSingleUserPHID(
$viewer, $userPHID, $repository, &$authfile, &$access) {
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($userPHID))
->executeOne();
if (!$user) {
return;
}
if ($user->getIsDisabled()) {
return;
}
$user_name = $user->getUserName();
if (!array_key_exists($user_name, $authfile)) {
$field_list = PhabricatorCustomField::getObjectFields(
$user,
PhabricatorCustomField::ROLE_STORAGE);
$field_list
->setViewer($user)
->readFieldsFromStorage($user);
$fields = $field_list->getFields();
$htaccess_field = idx($fields, 'std:user:htaccess_password_hash');
if ($htaccess_field) {
$password_hash = $htaccess_field->getValueForStorage();
$authfile[$user_name] = array('hash' => $password_hash,
'email' => $user->loadPrimaryEmailAddress(),
'name' => $user->getRealName());
}
}
$repository_name = getSVNRepositoryName($repository);
$repository_rootpath = $repository_name . ':/';
if (!array_key_exists($repository_rootpath, $access)) {
$access[$repository_rootpath]['RW'] = array();
$access[$repository_rootpath]['RO'] = array();
}
$access[$repository_rootpath]['RO'][] = $user_name;
// Store write access settings to current subath
$subpath = $repository->getDetail('svn-subpath');
$subpath = rtrim($subpath, '/');
$repository_pathname = "$repository_name:/$subpath";
if (!array_key_exists($repository_pathname, $access)) {
$access[$repository_pathname]['RW'] = array();
$access[$repository_pathname]['RO'] = array();
}
$access[$repository_pathname]['RW'][] = $user_name;
// Write access to the tags
$tags_pathname = "$repository_name:/tags";
if (!array_key_exists($tags_pathname, $access)) {
$access[$tags_pathname]['RW'] = array();
$access[$tags_pathname]['RO'] = array();
}
$access[$tags_pathname]['RW'][] = $user_name;
// Write access to the branches.
$branches_pathname = "$repository_name:/branches";
if (!array_key_exists($branches_pathname, $access)) {
$access[$branches_pathname]['RW'] = array();
$access[$branches_pathname]['RO'] = array();
}
$access[$branches_pathname]['RW'][] = $user_name;
}
// Parse repository and put it's members to the config file
function handleSingleRepository(
$viewer, $repository, &$authfile, &$access) {
$policies = PhabricatorPolicyQuery::loadPolicies(
$viewer,
$repository);
$pushable = $policies[DiffusionPushCapability::CAPABILITY];
$type = phid_get_type($pushable->getPHID());
// Make sure repository is always available for read-only access
$repository_rootpath = getSVNRepositoryName($repository) . ':/';
if (!array_key_exists($repository_rootpath, $access)) {
$access[$repository_rootpath]['RW'] = array();
$access[$repository_rootpath]['RO'] = array();
}
if ($type == PhabricatorProjectProjectPHIDType::TYPECONST) {
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->needMembers(true)
->withPHIDs(array($pushable->getPHID()))
->executeOne();
$memberPHIDs = $project->getMemberPHIDs();
foreach ($memberPHIDs as $memberPHID) {
handleSingleUserPHID(
$viewer, $memberPHID, $repository, $authfile, $access);
}
} else if ($type == PhabricatorPolicyType::TYPE_USER) {
handleSingleUserPHID(
$viewer, $pushable->getPHID(), $repository, $authfile, $access);
} else if ($type == PhabricatorPolicyPHIDTypePolicy::TYPECONST) {
/* pass */
} else {
/* pass */
}
}
function rebuildConfiguration($what) {
$viewer = id(new PhabricatorUser())
->loadOneWhere('username = %s', 'sergey');
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->execute();
$authfile = array();
$access = array();
require_once 'archived_repos.php';
foreach ($ARCHIVED_REPOS as $repository) {
$repository_pathname = "$repository:/";
$access[$repository_pathname]['RW'] = array();
$access[$repository_pathname]['RO'] = array();
}
foreach ($repositories as $repository_id => $repository) {
$type = $repository->getVersionControlSystem();
if ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) {
handleSingleRepository(
$viewer, $repository, $authfile, $access);
}
}
if ($what == 'AUTHFILE') {
foreach ($authfile as $user => $data) {
print("$user:${data['hash']}\n");
}
} else if ($what == 'ACCESS') {
foreach ($access as $repository => $users) {
print("[$repository]\n");
$rw_users = array();
foreach ($users['RW'] as $user) {
print("$user = rw\n");
$rw_users[$user] = true;
}
foreach ($users['RO'] as $user) {
if (!array_key_exists($user, $rw_users)) {
print("$user = r\n");
}
}
print("anonsvn = r\n");
print("* = r\n\n");
}
} else if ($what == 'NAMEMAP') {
foreach ($authfile as $user => $data) {
print("$user\t${data['email']}\t${data['name']}\n");
}
}
return true;
}
if (count($argv) != 2 ||
($argv[1] != 'ACCESS' && $argv[1] != 'AUTHFILE' && $argv[1] != 'NAMEMAP')) {
print("Usage: {$argv[0]} ACCESS|AUTHFILE|NAMEMAP\n");
exit(1);
}
rebuildConfiguration($argv[1]);
?>

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,6 @@ final class AphrontRequest extends Phobject {
private $controller;
private $uriData = array();
private $cookiePrefix;
private $submitKey;
public function __construct($host, $path) {
$this->host = $host;
@@ -915,19 +914,5 @@ final class AphrontRequest extends Phobject {
return $future;
}
public function updateEphemeralCookies() {
$submit_cookie = PhabricatorCookies::COOKIE_SUBMIT;
$submit_key = $this->getCookie($submit_cookie);
if (strlen($submit_key)) {
$this->clearCookie($submit_cookie);
$this->submitKey = $submit_key;
}
}
public function getSubmitKey() {
return $this->submitKey;
}
}

View File

@@ -1,85 +0,0 @@
<?php
final class AphrontRoutingMapTestCase
extends PhabricatorTestCase {
public function testRoutingMaps() {
$count = 0;
$sites = AphrontSite::getAllSites();
foreach ($sites as $site) {
$maps = $site->getRoutingMaps();
foreach ($maps as $map) {
foreach ($map->getRoutes() as $rule => $value) {
$this->assertRoutable($site, $map, array(), $rule, $value);
$count++;
}
}
}
if (!$count) {
$this->assertSkipped(
pht('No sites define any routing rules.'));
}
}
private function assertRoutable(
AphrontSite $site,
AphrontRoutingMap $map,
array $path,
$rule,
$value) {
$path[] = $rule;
$site_description = $site->getDescription();
$rule_path = implode(' > ', $path);
$pattern = implode('', $path);
$pattern = '('.$pattern.')';
$ok = @preg_match($pattern, '');
$this->assertTrue(
($ok !== false),
pht(
'Routing rule ("%s", for site "%s") does not compile into a '.
'valid regular expression.',
$rule_path,
$site_description));
if (is_array($value)) {
$this->assertTrue(
(count($value) > 0),
pht(
'Routing rule ("%s", for site "%s") does not have any targets.',
$rule_path,
$site_description));
foreach ($value as $sub_rule => $sub_value) {
$this->assertRoutable($site, $map, $path, $sub_rule, $sub_value);
}
return;
}
if (is_string($value)) {
$this->assertTrue(
class_exists($value),
pht(
'Routing rule ("%s", for site "%s") points at controller ("%s") '.
'which does not exist.',
$rule_path,
$site_description,
$value));
return;
}
$this->assertFailure(
pht(
'Routing rule ("%s", for site "%s") points at unknown value '.
'(of type "%s"), expected a controller class name string.',
$rule_path,
$site_description,
phutil_describe_type($value)));
}
}

View File

@@ -27,8 +27,6 @@ final class AphrontApplicationConfiguration
$request->setApplicationConfiguration($this);
$request->setCookiePrefix($cookie_prefix);
$request->updateEphemeralCookies();
return $request;
}
@@ -773,21 +771,12 @@ final class AphrontApplicationConfiguration
);
}
$raw_input = @file_get_contents('php://input');
if ($raw_input !== false) {
$base64_input = base64_encode($raw_input);
} else {
$base64_input = null;
}
$result = array(
'path' => $path,
'params' => $params,
'user' => idx($_SERVER, 'PHP_AUTH_USER'),
'pass' => idx($_SERVER, 'PHP_AUTH_PW'),
'raw.base64' => $base64_input,
// This just makes sure that the response compresses well, so reasonable
// algorithms should want to gzip or deflate it.
'filler' => str_repeat('Q', 1024 * 16),
@@ -812,22 +801,27 @@ final class AphrontApplicationConfiguration
// if we can. Among other things, this corrects variable names with
// the "." character in them, which PHP normally converts into "_".
// If "enable_post_data_reading" is on, the documentation suggests we
// can not read the body. In practice, we seem to be able to. This may
// need to be resolved at some point, likely by instructing installs
// to disable this option.
// There are two major considerations here: whether the
// `enable_post_data_reading` option is set, and whether the content
// type is "multipart/form-data" or not.
// If `enable_post_data_reading` is off, we're free to read the entire
// raw request body and parse it -- and we must, because $_POST and
// $_FILES are not built for us. If `enable_post_data_reading` is on,
// which is the default, we may not be able to read the body (the
// documentation says we can't, but empirically we can at least some
// of the time).
// If the content type is "multipart/form-data", we need to build both
// $_POST and $_FILES, which is involved. The body itself is also more
// difficult to parse than other requests.
$raw_input = PhabricatorStartup::getRawInput();
$parser = new PhutilQueryStringParser();
if (strlen($raw_input)) {
$content_type = idx($_SERVER, 'CONTENT_TYPE');
$is_multipart = preg_match('@^multipart/form-data@i', $content_type);
if ($is_multipart) {
if ($is_multipart && !ini_get('enable_post_data_reading')) {
$multipart_parser = id(new AphrontMultipartParser())
->setContentType($content_type);

View File

@@ -1,150 +0,0 @@
<?php
final class AphrontHTTPHeaderParser extends Phobject {
private $name;
private $content;
private $pairs;
public function parseRawHeader($raw_header) {
$this->name = null;
$this->content = null;
$parts = explode(':', $raw_header, 2);
$this->name = trim($parts[0]);
if (count($parts) > 1) {
$this->content = trim($parts[1]);
}
$this->pairs = null;
return $this;
}
public function getHeaderName() {
$this->requireParse();
return $this->name;
}
public function getHeaderContent() {
$this->requireParse();
return $this->content;
}
public function getHeaderContentAsPairs() {
$content = $this->getHeaderContent();
$state = 'prekey';
$length = strlen($content);
$pair_name = null;
$pair_value = null;
$pairs = array();
$ii = 0;
while ($ii < $length) {
$c = $content[$ii];
switch ($state) {
case 'prekey';
// We're eating space in front of a key.
if ($c == ' ') {
$ii++;
break;
}
$pair_name = '';
$state = 'key';
break;
case 'key';
// We're parsing a key name until we find "=" or ";".
if ($c == ';') {
$state = 'done';
break;
}
if ($c == '=') {
$ii++;
$state = 'value';
break;
}
$ii++;
$pair_name .= $c;
break;
case 'value':
// We found an "=", so now figure out if the value is quoted
// or not.
if ($c == '"') {
$ii++;
$state = 'quoted';
break;
}
$state = 'unquoted';
break;
case 'quoted':
// We're in a quoted string, parse until we find the closing quote.
if ($c == '"') {
$ii++;
$state = 'done';
break;
}
$ii++;
$pair_value .= $c;
break;
case 'unquoted':
// We're in an unquoted string, parse until we find a space or a
// semicolon.
if ($c == ' ' || $c == ';') {
$state = 'done';
break;
}
$ii++;
$pair_value .= $c;
break;
case 'done':
// We parsed something, so eat any trailing whitespace and semicolons
// and look for a new value.
if ($c == ' ' || $c == ';') {
$ii++;
break;
}
$pairs[] = array(
$pair_name,
$pair_value,
);
$pair_name = null;
$pair_value = null;
$state = 'prekey';
break;
}
}
if ($state == 'quoted') {
throw new Exception(
pht(
'Header has unterminated double quote for key "%s".',
$pair_name));
}
if ($pair_name !== null) {
$pairs[] = array(
$pair_name,
$pair_value,
);
}
return $pairs;
}
private function requireParse() {
if ($this->name === null) {
throw new PhutilInvalidStateException('parseRawHeader');
}
}
}

View File

@@ -1,108 +0,0 @@
<?php
final class AphrontHTTPHeaderParserTestCase extends PhutilTestCase {
public function testHeaderParser() {
$cases = array(
array(
'Key: x; y; z',
'Key',
'x; y; z',
array(
array('x', null),
array('y', null),
array('z', null),
),
),
array(
'Content-Disposition: form-data; name="label"',
'Content-Disposition',
'form-data; name="label"',
array(
array('form-data', null),
array('name', 'label'),
),
),
array(
'Content-Type: multipart/form-data; charset=utf-8',
'Content-Type',
'multipart/form-data; charset=utf-8',
array(
array('multipart/form-data', null),
array('charset', 'utf-8'),
),
),
array(
'Content-Type: application/octet-stream; charset="ut',
'Content-Type',
'application/octet-stream; charset="ut',
false,
),
array(
'Content-Type: multipart/form-data; boundary=ABCDEFG',
'Content-Type',
'multipart/form-data; boundary=ABCDEFG',
array(
array('multipart/form-data', null),
array('boundary', 'ABCDEFG'),
),
),
array(
'Content-Type: multipart/form-data; boundary="ABCDEFG"',
'Content-Type',
'multipart/form-data; boundary="ABCDEFG"',
array(
array('multipart/form-data', null),
array('boundary', 'ABCDEFG'),
),
),
);
foreach ($cases as $case) {
$input = $case[0];
$expect_name = $case[1];
$expect_content = $case[2];
$parser = id(new AphrontHTTPHeaderParser())
->parseRawHeader($input);
$actual_name = $parser->getHeaderName();
$actual_content = $parser->getHeaderContent();
$this->assertEqual(
$expect_name,
$actual_name,
pht('Header name for: %s', $input));
$this->assertEqual(
$expect_content,
$actual_content,
pht('Header content for: %s', $input));
if (isset($case[3])) {
$expect_pairs = $case[3];
$caught = null;
try {
$actual_pairs = $parser->getHeaderContentAsPairs();
} catch (Exception $ex) {
$caught = $ex;
}
if ($expect_pairs === false) {
$this->assertEqual(
true,
($caught instanceof Exception),
pht('Expect exception for header pairs of: %s', $input));
} else {
$this->assertEqual(
$expect_pairs,
$actual_pairs,
pht('Header pairs for: %s', $input));
}
}
}
}
}

View File

@@ -1,249 +0,0 @@
<?php
final class AphrontMultipartParser extends Phobject {
private $contentType;
private $boundary;
private $buffer;
private $body;
private $state;
private $part;
private $parts;
public function setContentType($content_type) {
$this->contentType = $content_type;
return $this;
}
public function getContentType() {
return $this->contentType;
}
public function beginParse() {
$content_type = $this->getContentType();
if ($content_type === null) {
throw new PhutilInvalidStateException('setContentType');
}
if (!preg_match('(^multipart/form-data)', $content_type)) {
throw new Exception(
pht(
'Expected "multipart/form-data" content type when executing a '.
'multipart body read.'));
}
$type_parts = preg_split('(\s*;\s*)', $content_type);
$boundary = null;
foreach ($type_parts as $type_part) {
$matches = null;
if (preg_match('(^boundary=(.*))', $type_part, $matches)) {
$boundary = $matches[1];
break;
}
}
if ($boundary === null) {
throw new Exception(
pht('Received "multipart/form-data" request with no "boundary".'));
}
$this->parts = array();
$this->part = null;
$this->buffer = '';
$this->boundary = $boundary;
// We're looking for a (usually empty) body before the first boundary.
$this->state = 'bodynewline';
}
public function continueParse($bytes) {
$this->buffer .= $bytes;
$continue = true;
while ($continue) {
switch ($this->state) {
case 'endboundary':
// We've just parsed a boundary. Next, we expect either "--" (which
// indicates we've reached the end of the parts) or "\r\n" (which
// indicates we should read the headers for the next part).
if (strlen($this->buffer) < 2) {
// We don't have enough bytes yet, so wait for more.
$continue = false;
break;
}
if (!strncmp($this->buffer, '--', 2)) {
// This is "--" after a boundary, so we're done. We'll read the
// rest of the body (the "epilogue") and discard it.
$this->buffer = substr($this->buffer, 2);
$this->state = 'epilogue';
$this->part = null;
break;
}
if (!strncmp($this->buffer, "\r\n", 2)) {
// This is "\r\n" after a boundary, so we're going to going to
// read the headers for a part.
$this->buffer = substr($this->buffer, 2);
$this->state = 'header';
// Create the object to hold the part we're about to read.
$part = new AphrontMultipartPart();
$this->parts[] = $part;
$this->part = $part;
break;
}
throw new Exception(
pht('Expected "\r\n" or "--" after multipart data boundary.'));
case 'header':
// We've just parsed a boundary, followed by "\r\n". We are going
// to read the headers for this part. They are in the form of HTTP
// headers and terminated by "\r\n". The section is terminated by
// a line with no header on it.
if (strlen($this->buffer) < 2) {
// We don't have enough data to find a "\r\n", so wait for more.
$continue = false;
break;
}
if (!strncmp("\r\n", $this->buffer, 2)) {
// This line immediately began "\r\n", so we're done with parsing
// headers. Start parsing the body.
$this->buffer = substr($this->buffer, 2);
$this->state = 'body';
break;
}
// This is an actual header, so look for the end of it.
$header_len = strpos($this->buffer, "\r\n");
if ($header_len === false) {
// We don't have a full header yet, so wait for more data.
$continue = false;
break;
}
$header_buf = substr($this->buffer, 0, $header_len);
$this->part->appendRawHeader($header_buf);
$this->buffer = substr($this->buffer, $header_len + 2);
break;
case 'body':
// We've parsed a boundary and headers, and are parsing the data for
// this part. The data is terminated by "\r\n--", then the boundary.
// We'll look for "\r\n", then switch to the "bodynewline" state if
// we find it.
$marker = "\r";
$marker_pos = strpos($this->buffer, $marker);
if ($marker_pos === false) {
// There's no "\r" anywhere in the buffer, so we can just read it
// as provided. Then, since we read all the data, we're done until
// we get more.
// Note that if we're in the preamble, we won't have a "part"
// object and will just discard the data.
if ($this->part) {
$this->part->appendData($this->buffer);
}
$this->buffer = '';
$continue = false;
break;
}
if ($marker_pos > 0) {
// If there are bytes before the "\r",
if ($this->part) {
$this->part->appendData(substr($this->buffer, 0, $marker_pos));
}
$this->buffer = substr($this->buffer, $marker_pos);
}
$expect = "\r\n";
$expect_len = strlen($expect);
if (strlen($this->buffer) < $expect_len) {
// We don't have enough bytes yet to know if this is "\r\n"
// or not.
$continue = false;
break;
}
if (strncmp($this->buffer, $expect, $expect_len)) {
// The next two bytes aren't "\r\n", so eat them and go looking
// for more newlines.
if ($this->part) {
$this->part->appendData(substr($this->buffer, 0, $expect_len));
}
$this->buffer = substr($this->buffer, $expect_len);
break;
}
// Eat the "\r\n".
$this->buffer = substr($this->buffer, $expect_len);
$this->state = 'bodynewline';
break;
case 'bodynewline':
// We've parsed a newline in a body, or we just started parsing the
// request. In either case, we're looking for "--", then the boundary.
// If we find it, this section is done. If we don't, we consume the
// bytes and move on.
$expect = '--'.$this->boundary;
$expect_len = strlen($expect);
if (strlen($this->buffer) < $expect_len) {
// We don't have enough bytes yet, so wait for more.
$continue = false;
break;
}
if (strncmp($this->buffer, $expect, $expect_len)) {
// This wasn't the boundary, so return to the "body" state and
// consume it. (But first, we need to append the "\r\n" which we
// ate earlier.)
if ($this->part) {
$this->part->appendData("\r\n");
}
$this->state = 'body';
break;
}
// This is the boundary, so toss it and move on.
$this->buffer = substr($this->buffer, $expect_len);
$this->state = 'endboundary';
break;
case 'epilogue':
// We just discard any epilogue.
$this->buffer = '';
$continue = false;
break;
default:
throw new Exception(
pht(
'Unknown parser state "%s".\n',
$this->state));
}
}
}
public function endParse() {
if ($this->state !== 'epilogue') {
throw new Exception(
pht(
'Expected "multipart/form-data" parse to end '.
'in state "epilogue".'));
}
return $this->parts;
}
}

View File

@@ -1,96 +0,0 @@
<?php
final class AphrontMultipartPart extends Phobject {
private $headers = array();
private $value = '';
private $name;
private $filename;
private $tempFile;
private $byteSize = 0;
public function appendRawHeader($bytes) {
$parser = id(new AphrontHTTPHeaderParser())
->parseRawHeader($bytes);
$header_name = $parser->getHeaderName();
$this->headers[] = array(
$header_name,
$parser->getHeaderContent(),
);
if (strtolower($header_name) === 'content-disposition') {
$pairs = $parser->getHeaderContentAsPairs();
foreach ($pairs as $pair) {
list($key, $value) = $pair;
switch ($key) {
case 'filename':
$this->filename = $value;
break;
case 'name':
$this->name = $value;
break;
}
}
}
return $this;
}
public function appendData($bytes) {
$this->byteSize += strlen($bytes);
if ($this->isVariable()) {
$this->value .= $bytes;
} else {
if (!$this->tempFile) {
$this->tempFile = new TempFile(getmypid().'.upload');
}
Filesystem::appendFile($this->tempFile, $bytes);
}
return $this;
}
public function isVariable() {
return ($this->filename === null);
}
public function getName() {
return $this->name;
}
public function getVariableValue() {
if (!$this->isVariable()) {
throw new Exception(pht('This part is not a variable!'));
}
return $this->value;
}
public function getPHPFileDictionary() {
if (!$this->tempFile) {
$this->appendData('');
}
$mime_type = 'application/octet-stream';
foreach ($this->headers as $header) {
list($name, $value) = $header;
if (strtolower($name) == 'content-type') {
$mime_type = $value;
break;
}
}
return array(
'name' => $this->filename,
'type' => $mime_type,
'tmp_name' => (string)$this->tempFile,
'error' => 0,
'size' => $this->byteSize,
);
}
}

View File

@@ -1,45 +0,0 @@
<?php
final class AphrontMultipartParserTestCase extends PhutilTestCase {
public function testParser() {
$map = array(
array(
'data' => 'simple.txt',
'variables' => array(
array('a', 'b'),
),
),
);
$data_dir = dirname(__FILE__).'/data/';
foreach ($map as $test_case) {
$data = Filesystem::readFile($data_dir.$test_case['data']);
$data = str_replace("\n", "\r\n", $data);
$parser = id(new AphrontMultipartParser())
->setContentType('multipart/form-data; boundary=ABCDEFG');
$parser->beginParse();
$parser->continueParse($data);
$parts = $parser->endParse();
$variables = array();
foreach ($parts as $part) {
if (!$part->isVariable()) {
continue;
}
$variables[] = array(
$part->getName(),
$part->getVariableValue(),
);
}
$expect_variables = idx($test_case, 'variables', array());
$this->assertEqual($expect_variables, $variables);
}
}
}

View File

@@ -1,5 +0,0 @@
--ABCDEFG
Content-Disposition: form-data; name="a"
b
--ABCDEFG--

View File

@@ -1,113 +0,0 @@
<?php
final class AphrontRequestStream extends Phobject {
private $encoding;
private $stream;
private $closed;
private $iterator;
public function setEncoding($encoding) {
$this->encoding = $encoding;
return $this;
}
public function getEncoding() {
return $this->encoding;
}
public function getIterator() {
if (!$this->iterator) {
$this->iterator = new PhutilStreamIterator($this->getStream());
}
return $this->iterator;
}
public function readData() {
if (!$this->iterator) {
$iterator = $this->getIterator();
$iterator->rewind();
} else {
$iterator = $this->getIterator();
}
if (!$iterator->valid()) {
return null;
}
$data = $iterator->current();
$iterator->next();
return $data;
}
private function getStream() {
if (!$this->stream) {
$this->stream = $this->newStream();
}
return $this->stream;
}
private function newStream() {
$stream = fopen('php://input', 'rb');
if (!$stream) {
throw new Exception(
pht(
'Failed to open stream "%s" for reading.',
'php://input'));
}
$encoding = $this->getEncoding();
if ($encoding === 'gzip') {
// This parameter is magic. Values 0-15 express a time/memory tradeoff,
// but the largest value (15) corresponds to only 32KB of memory and
// data encoded with a smaller window size than the one we pass can not
// be decompressed. Always pass the maximum window size.
// Additionally, you can add 16 (to enable gzip) or 32 (to enable both
// gzip and zlib). Add 32 to support both.
$zlib_window = 15 + 32;
$ok = stream_filter_append(
$stream,
'zlib.inflate',
STREAM_FILTER_READ,
array(
'window' => $zlib_window,
));
if (!$ok) {
throw new Exception(
pht(
'Failed to append filter "%s" to input stream while processing '.
'a request with "%s" encoding.',
'zlib.inflate',
$encoding));
}
}
return $stream;
}
public static function supportsGzip() {
if (!function_exists('gzencode') || !function_exists('gzdecode')) {
return false;
}
$has_zlib = false;
// NOTE: At least locally, this returns "zlib.*", which is not terribly
// reassuring. We care about "zlib.inflate".
$filters = stream_get_filters();
foreach ($filters as $filter) {
if (!strncasecmp($filter, 'zlib.', strlen('zlib.'))) {
$has_zlib = true;
break;
}
}
return $has_zlib;
}
}

View File

@@ -61,17 +61,11 @@ final class AphrontHTTPProxyResponse extends AphrontResponse {
// Strip "Transfer-Encoding" headers. Particularly, the server we proxied
// may have chunked the response, but cURL will already have un-chunked it.
// If we emit the header and unchunked data, the response becomes invalid.
// See T13517. Strip "Content-Encoding" and "Content-Length" headers, since
// they may reflect compressed content.
foreach ($headers as $key => $header) {
list($header_head, $header_body) = $header;
$header_head = phutil_utf8_strtolower($header_head);
switch ($header_head) {
case 'transfer-encoding':
case 'content-encoding':
case 'content-length':
unset($headers[$key]);
break;
}

View File

@@ -31,10 +31,10 @@ final class AphrontJSONResponse extends AphrontResponse {
}
public function getHeaders() {
$headers = parent::getHeaders();
$headers[] = array('Content-Type', 'application/json');
$headers = array(
array('Content-Type', 'application/json'),
);
$headers = array_merge(parent::getHeaders(), $headers);
return $headers;
}

View File

@@ -10,7 +10,7 @@ abstract class AphrontResponse extends Phobject {
private $contentSecurityPolicyURIs;
private $disableContentSecurityPolicy;
protected $frameable;
private $headers = array();
public function setRequest($request) {
$this->request = $request;
@@ -49,11 +49,6 @@ abstract class AphrontResponse extends Phobject {
return $this;
}
final public function addHeader($key, $value) {
$this->headers[] = array($key, $value);
return $this;
}
/* -( Content )------------------------------------------------------------ */
@@ -110,10 +105,6 @@ abstract class AphrontResponse extends Phobject {
$headers[] = array('Referrer-Policy', 'no-referrer');
foreach ($this->headers as $header) {
$headers[] = $header;
}
return $headers;
}
@@ -426,19 +417,13 @@ abstract class AphrontResponse extends Phobject {
}
public function willBeginWrite() {
// If we've already sent headers, these "ini_set()" calls will warn that
// they have no effect. Today, this always happens because we're inside
// a unit test, so just skip adjusting the setting.
if (!headers_sent()) {
if ($this->shouldCompressResponse()) {
// Enable automatic compression here. Webservers sometimes do this for
// us, but we now detect the absence of compression and warn users about
// it so try to cover our bases more thoroughly.
ini_set('zlib.output_compression', 1);
} else {
ini_set('zlib.output_compression', 0);
}
if ($this->shouldCompressResponse()) {
// Enable automatic compression here. Webservers sometimes do this for
// us, but we now detect the absence of compression and warn users about
// it so try to cover our bases more thoroughly.
ini_set('zlib.output_compression', 1);
} else {
ini_set('zlib.output_compression', 0);
}
}

View File

@@ -1,76 +0,0 @@
<?php
/**
* NOTE: This is very new and unstable.
*/
final class PhutilSprite extends Phobject {
private $sourceFiles = array();
private $sourceX;
private $sourceY;
private $sourceW;
private $sourceH;
private $targetCSS;
private $spriteSheet;
private $name;
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setTargetCSS($target_css) {
$this->targetCSS = $target_css;
return $this;
}
public function getTargetCSS() {
return $this->targetCSS;
}
public function setSourcePosition($x, $y) {
$this->sourceX = $x;
$this->sourceY = $y;
return $this;
}
public function setSourceSize($w, $h) {
$this->sourceW = $w;
$this->sourceH = $h;
return $this;
}
public function getSourceH() {
return $this->sourceH;
}
public function getSourceW() {
return $this->sourceW;
}
public function getSourceY() {
return $this->sourceY;
}
public function getSourceX() {
return $this->sourceX;
}
public function setSourceFile($source_file, $scale = 1) {
$this->sourceFiles[$scale] = $source_file;
return $this;
}
public function getSourceFile($scale) {
if (empty($this->sourceFiles[$scale])) {
throw new Exception(pht("No source file for scale '%s'!", $scale));
}
return $this->sourceFiles[$scale];
}
}

View File

@@ -1,385 +0,0 @@
<?php
/**
* NOTE: This is very new and unstable.
*/
final class PhutilSpriteSheet extends Phobject {
const MANIFEST_VERSION = 1;
const TYPE_STANDARD = 'standard';
const TYPE_REPEAT_X = 'repeat-x';
const TYPE_REPEAT_Y = 'repeat-y';
private $sprites = array();
private $sources = array();
private $hashes = array();
private $cssHeader;
private $generated;
private $scales = array(1);
private $type = self::TYPE_STANDARD;
private $basePath;
private $css;
private $images;
public function addSprite(PhutilSprite $sprite) {
$this->generated = false;
$this->sprites[] = $sprite;
return $this;
}
public function setCSSHeader($header) {
$this->generated = false;
$this->cssHeader = $header;
return $this;
}
public function setScales(array $scales) {
$this->scales = array_values($scales);
return $this;
}
public function getScales() {
return $this->scales;
}
public function setSheetType($type) {
$this->type = $type;
return $this;
}
public function setBasePath($base_path) {
$this->basePath = $base_path;
return $this;
}
private function generate() {
if ($this->generated) {
return;
}
$multi_row = true;
$multi_col = true;
$margin_w = 1;
$margin_h = 1;
$type = $this->type;
switch ($type) {
case self::TYPE_STANDARD:
break;
case self::TYPE_REPEAT_X:
$multi_col = false;
$margin_w = 0;
$width = null;
foreach ($this->sprites as $sprite) {
if ($width === null) {
$width = $sprite->getSourceW();
} else if ($width !== $sprite->getSourceW()) {
throw new Exception(
pht(
"All sprites in a '%s' sheet must have the same width.",
'repeat-x'));
}
}
break;
case self::TYPE_REPEAT_Y:
$multi_row = false;
$margin_h = 0;
$height = null;
foreach ($this->sprites as $sprite) {
if ($height === null) {
$height = $sprite->getSourceH();
} else if ($height !== $sprite->getSourceH()) {
throw new Exception(
pht(
"All sprites in a '%s' sheet must have the same height.",
'repeat-y'));
}
}
break;
default:
throw new Exception(pht("Unknown sprite sheet type '%s'!", $type));
}
$css = array();
if ($this->cssHeader) {
$css[] = $this->cssHeader;
}
$out_w = 0;
$out_h = 0;
// Lay out the sprite sheet. We attempt to build a roughly square sheet
// so it's easier to manage, since 2000x20 is more cumbersome for humans
// to deal with than 200x200.
//
// To do this, we use a simple greedy algorithm, adding sprites one at a
// time. For each sprite, if the sheet is at least as wide as it is tall
// we create a new row. Otherwise, we try to add it to an existing row.
//
// This isn't optimal, but does a reasonable job in most cases and isn't
// too messy.
// Group the sprites by their sizes. We lay them out in the sheet as
// boxes, but then put them into the boxes in the order they were added
// so similar sprites end up nearby on the final sheet.
$boxes = array();
foreach (array_reverse($this->sprites) as $sprite) {
$s_w = $sprite->getSourceW() + $margin_w;
$s_h = $sprite->getSourceH() + $margin_h;
$boxes[$s_w][$s_h][] = $sprite;
}
$rows = array();
foreach ($this->sprites as $sprite) {
$s_w = $sprite->getSourceW() + $margin_w;
$s_h = $sprite->getSourceH() + $margin_h;
// Choose a row for this sprite.
$maybe = array();
foreach ($rows as $key => $row) {
if ($row['h'] < $s_h) {
// We can only add it to a row if the row is at least as tall as the
// sprite.
continue;
}
// We prefer rows which have the same height as the sprite, and then
// rows which aren't yet very wide.
$wasted_v = ($row['h'] - $s_h);
$wasted_h = ($row['w'] / $out_w);
$maybe[$key] = $wasted_v + $wasted_h;
}
$row_key = null;
if ($maybe && $multi_col) {
// If there were any candidate rows, pick the best one.
asort($maybe);
$row_key = head_key($maybe);
}
if ($row_key !== null && $multi_row) {
// If there's a candidate row, but adding the sprite to it would make
// the sprite wider than it is tall, create a new row instead. This
// generally keeps the sprite square-ish.
if ($rows[$row_key]['w'] + $s_w > $out_h) {
$row_key = null;
}
}
if ($row_key === null) {
// Add a new row.
$rows[] = array(
'w' => 0,
'h' => $s_h,
'boxes' => array(),
);
$row_key = last_key($rows);
$out_h += $s_h;
}
// Add the sprite box to the row.
$row = $rows[$row_key];
$row['w'] += $s_w;
$row['boxes'][] = array($s_w, $s_h);
$rows[$row_key] = $row;
$out_w = max($row['w'], $out_w);
}
$images = array();
foreach ($this->scales as $scale) {
$img = imagecreatetruecolor($out_w * $scale, $out_h * $scale);
imagesavealpha($img, true);
imagefill($img, 0, 0, imagecolorallocatealpha($img, 0, 0, 0, 127));
$images[$scale] = $img;
}
// Put the shorter rows first. At the same height, put the wider rows first.
// This makes the resulting sheet more human-readable.
foreach ($rows as $key => $row) {
$rows[$key]['sort'] = $row['h'] + (1 - ($row['w'] / $out_w));
}
$rows = isort($rows, 'sort');
$pos_x = 0;
$pos_y = 0;
$rules = array();
foreach ($rows as $row) {
$max_h = 0;
foreach ($row['boxes'] as $box) {
$sprite = array_pop($boxes[$box[0]][$box[1]]);
foreach ($images as $scale => $img) {
$src = $this->loadSource($sprite, $scale);
imagecopy(
$img,
$src,
$scale * $pos_x, $scale * $pos_y,
$scale * $sprite->getSourceX(), $scale * $sprite->getSourceY(),
$scale * $sprite->getSourceW(), $scale * $sprite->getSourceH());
}
$rule = $sprite->getTargetCSS();
$cssx = (-$pos_x).'px';
$cssy = (-$pos_y).'px';
$rules[$sprite->getName()] = "{$rule} {\n".
" background-position: {$cssx} {$cssy};\n}";
$pos_x += $sprite->getSourceW() + $margin_w;
$max_h = max($max_h, $sprite->getSourceH());
}
$pos_x = 0;
$pos_y += $max_h + $margin_h;
}
// Generate CSS rules in input order.
foreach ($this->sprites as $sprite) {
$css[] = $rules[$sprite->getName()];
}
$this->images = $images;
$this->css = implode("\n\n", $css)."\n";
$this->generated = true;
}
public function generateImage($path, $scale = 1) {
$this->generate();
$this->log(pht("Writing sprite '%s'...", $path));
imagepng($this->images[$scale], $path);
return $this;
}
public function generateCSS($path) {
$this->generate();
$this->log(pht("Writing CSS '%s'...", $path));
$out = $this->css;
$out = str_replace('{X}', imagesx($this->images[1]), $out);
$out = str_replace('{Y}', imagesy($this->images[1]), $out);
Filesystem::writeFile($path, $out);
return $this;
}
public function needsRegeneration(array $manifest) {
return ($this->buildManifest() !== $manifest);
}
private function buildManifest() {
$output = array();
foreach ($this->sprites as $sprite) {
$output[$sprite->getName()] = array(
'name' => $sprite->getName(),
'rule' => $sprite->getTargetCSS(),
'hash' => $this->loadSourceHash($sprite),
);
}
ksort($output);
$data = array(
'version' => self::MANIFEST_VERSION,
'sprites' => $output,
'scales' => $this->scales,
'header' => $this->cssHeader,
'type' => $this->type,
);
return $data;
}
public function generateManifest($path) {
$data = $this->buildManifest();
$json = new PhutilJSON();
$data = $json->encodeFormatted($data);
Filesystem::writeFile($path, $data);
return $this;
}
private function log($message) {
echo $message."\n";
}
private function loadSourceHash(PhutilSprite $sprite) {
$inputs = array();
foreach ($this->scales as $scale) {
$file = $sprite->getSourceFile($scale);
// If two users have a project in different places, like:
//
// /home/alincoln/project
// /home/htaft/project
//
// ...we want to ignore the `/home/alincoln` part when hashing the sheet,
// since the sprites don't change when the project directory moves. If
// the base path is set, build the hashes using paths relative to the
// base path.
$file_key = $file;
if ($this->basePath) {
$file_key = Filesystem::readablePath($file, $this->basePath);
}
if (empty($this->hashes[$file_key])) {
$this->hashes[$file_key] = md5(Filesystem::readFile($file));
}
$inputs[] = $file_key;
$inputs[] = $this->hashes[$file_key];
}
$inputs[] = $sprite->getSourceX();
$inputs[] = $sprite->getSourceY();
$inputs[] = $sprite->getSourceW();
$inputs[] = $sprite->getSourceH();
return md5(implode(':', $inputs));
}
private function loadSource(PhutilSprite $sprite, $scale) {
$file = $sprite->getSourceFile($scale);
if (empty($this->sources[$file])) {
$data = Filesystem::readFile($file);
$image = imagecreatefromstring($data);
$this->sources[$file] = array(
'image' => $image,
'x' => imagesx($image),
'y' => imagesy($image),
);
}
$s_w = $sprite->getSourceW() * $scale;
$i_w = $this->sources[$file]['x'];
if ($s_w > $i_w) {
throw new Exception(
pht(
"Sprite source for '%s' is too small (expected width %d, found %d).",
$file,
$s_w,
$i_w));
}
$s_h = $sprite->getSourceH() * $scale;
$i_h = $this->sources[$file]['y'];
if ($s_h > $i_h) {
throw new Exception(
pht(
"Sprite source for '%s' is too small (expected height %d, found %d).",
$file,
$s_h,
$i_h));
}
return $this->sources[$file]['image'];
}
}

View File

@@ -1,9 +0,0 @@
<?php
final class AphrontScopedUnguardedWriteCapability extends Phobject {
public function __destruct() {
AphrontWriteGuard::endUnguardedWrites();
}
}

View File

@@ -1,267 +0,0 @@
<?php
/**
* Guard writes against CSRF. The Aphront structure takes care of most of this
* for you, you just need to call:
*
* AphrontWriteGuard::willWrite();
*
* ...before executing a write against any new kind of storage engine. MySQL
* databases and the default file storage engines are already covered, but if
* you introduce new types of datastores make sure their writes are guarded. If
* you don't guard writes and make a mistake doing CSRF checks in a controller,
* a CSRF vulnerability can escape undetected.
*
* If you need to execute writes on a page which doesn't have CSRF tokens (for
* example, because you need to do logging), you can temporarily disable the
* write guard by calling:
*
* AphrontWriteGuard::beginUnguardedWrites();
* do_logging_write();
* AphrontWriteGuard::endUnguardedWrites();
*
* This is dangerous, because it disables the backup layer of CSRF protection
* this class provides. You should need this only very, very rarely.
*
* @task protect Protecting Writes
* @task disable Disabling Protection
* @task manage Managing Write Guards
* @task internal Internals
*/
final class AphrontWriteGuard extends Phobject {
private static $instance;
private static $allowUnguardedWrites = false;
private $callback;
private $allowDepth = 0;
/* -( Managing Write Guards )---------------------------------------------- */
/**
* Construct a new write guard for a request. Only one write guard may be
* active at a time. You must explicitly call @{method:dispose} when you are
* done with a write guard:
*
* $guard = new AphrontWriteGuard($callback);
* // ...
* $guard->dispose();
*
* Normally, you do not need to manage guards yourself -- the Aphront stack
* handles it for you.
*
* This class accepts a callback, which will be invoked when a write is
* attempted. The callback should validate the presence of a CSRF token in
* the request, or abort the request (e.g., by throwing an exception) if a
* valid token isn't present.
*
* @param callable CSRF callback.
* @return this
* @task manage
*/
public function __construct($callback) {
if (self::$instance) {
throw new Exception(
pht(
'An %s already exists. Dispose of the previous guard '.
'before creating a new one.',
__CLASS__));
}
if (self::$allowUnguardedWrites) {
throw new Exception(
pht(
'An %s is being created in a context which permits '.
'unguarded writes unconditionally. This is not allowed and '.
'indicates a serious error.',
__CLASS__));
}
$this->callback = $callback;
self::$instance = $this;
}
/**
* Dispose of the active write guard. You must call this method when you are
* done with a write guard. You do not normally need to call this yourself.
*
* @return void
* @task manage
*/
public function dispose() {
if (!self::$instance) {
throw new Exception(pht(
'Attempting to dispose of write guard, but no write guard is active!'));
}
if ($this->allowDepth > 0) {
throw new Exception(
pht(
'Imbalanced %s: more %s calls than %s calls.',
__CLASS__,
'beginUnguardedWrites()',
'endUnguardedWrites()'));
}
self::$instance = null;
}
/**
* Determine if there is an active write guard.
*
* @return bool
* @task manage
*/
public static function isGuardActive() {
return (bool)self::$instance;
}
/**
* Return on instance of AphrontWriteGuard if it's active, or null
*
* @return AphrontWriteGuard|null
*/
public static function getInstance() {
return self::$instance;
}
/* -( Protecting Writes )-------------------------------------------------- */
/**
* Declare intention to perform a write, validating that writes are allowed.
* You should call this method before executing a write whenever you implement
* a new storage engine where information can be permanently kept.
*
* Writes are permitted if:
*
* - The request has valid CSRF tokens.
* - Unguarded writes have been temporarily enabled by a call to
* @{method:beginUnguardedWrites}.
* - All write guarding has been disabled with
* @{method:allowDangerousUnguardedWrites}.
*
* If none of these conditions are true, this method will throw and prevent
* the write.
*
* @return void
* @task protect
*/
public static function willWrite() {
if (!self::$instance) {
if (!self::$allowUnguardedWrites) {
throw new Exception(
pht(
'Unguarded write! There must be an active %s to perform writes.',
__CLASS__));
} else {
// Unguarded writes are being allowed unconditionally.
return;
}
}
$instance = self::$instance;
if ($instance->allowDepth == 0) {
call_user_func($instance->callback);
}
}
/* -( Disabling Write Protection )----------------------------------------- */
/**
* Enter a scope which permits unguarded writes. This works like
* @{method:beginUnguardedWrites} but returns an object which will end
* the unguarded write scope when its __destruct() method is called. This
* is useful to more easily handle exceptions correctly in unguarded write
* blocks:
*
* // Restores the guard even if do_logging() throws.
* function unguarded_scope() {
* $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
* do_logging();
* }
*
* @return AphrontScopedUnguardedWriteCapability Object which ends unguarded
* writes when it leaves scope.
* @task disable
*/
public static function beginScopedUnguardedWrites() {
self::beginUnguardedWrites();
return new AphrontScopedUnguardedWriteCapability();
}
/**
* Begin a block which permits unguarded writes. You should use this very
* sparingly, and only for things like logging where CSRF is not a concern.
*
* You must pair every call to @{method:beginUnguardedWrites} with a call to
* @{method:endUnguardedWrites}:
*
* AphrontWriteGuard::beginUnguardedWrites();
* do_logging();
* AphrontWriteGuard::endUnguardedWrites();
*
* @return void
* @task disable
*/
public static function beginUnguardedWrites() {
if (!self::$instance) {
return;
}
self::$instance->allowDepth++;
}
/**
* Declare that you have finished performing unguarded writes. You must
* call this exactly once for each call to @{method:beginUnguardedWrites}.
*
* @return void
* @task disable
*/
public static function endUnguardedWrites() {
if (!self::$instance) {
return;
}
if (self::$instance->allowDepth <= 0) {
throw new Exception(
pht(
'Imbalanced %s: more %s calls than %s calls.',
__CLASS__,
'endUnguardedWrites()',
'beginUnguardedWrites()'));
}
self::$instance->allowDepth--;
}
/**
* Allow execution of unguarded writes. This is ONLY appropriate for use in
* script contexts or other contexts where you are guaranteed to never be
* vulnerable to CSRF concerns. Calling this method is EXTREMELY DANGEROUS
* if you do not understand the consequences.
*
* If you need to perform unguarded writes on an otherwise guarded workflow
* which is vulnerable to CSRF, use @{method:beginUnguardedWrites}.
*
* @return void
* @task disable
*/
public static function allowDangerousUnguardedWrites($allow) {
if (self::$instance) {
throw new Exception(
pht(
'You can not unconditionally disable %s by calling %s while a write '.
'guard is active. Use %s to temporarily allow unguarded writes.',
__CLASS__,
__FUNCTION__.'()',
'beginUnguardedWrites()'));
}
self::$allowUnguardedWrites = true;
}
}

View File

@@ -10,15 +10,13 @@ final class AlmanacConsoleController extends AlmanacController {
$viewer = $request->getViewer();
$menu = id(new PHUIObjectItemListView())
->setViewer($viewer)
->setBig(true);
->setUser($viewer);
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Devices'))
->setHref($this->getApplicationURI('device/'))
->setImageIcon('fa-server')
->setClickable(true)
->addAttribute(
pht(
'Create an inventory of physical and virtual hosts and '.
@@ -29,7 +27,6 @@ final class AlmanacConsoleController extends AlmanacController {
->setHeader(pht('Services'))
->setHref($this->getApplicationURI('service/'))
->setImageIcon('fa-plug')
->setClickable(true)
->addAttribute(
pht(
'Create and update services, and map them to interfaces on '.
@@ -40,7 +37,6 @@ final class AlmanacConsoleController extends AlmanacController {
->setHeader(pht('Networks'))
->setHref($this->getApplicationURI('network/'))
->setImageIcon('fa-globe')
->setClickable(true)
->addAttribute(
pht(
'Manage public and private networks.')));
@@ -50,7 +46,6 @@ final class AlmanacConsoleController extends AlmanacController {
->setHeader(pht('Namespaces'))
->setHref($this->getApplicationURI('namespace/'))
->setImageIcon('fa-asterisk')
->setClickable(true)
->addAttribute(
pht('Control who can create new named services and devices.')));
@@ -62,7 +57,6 @@ final class AlmanacConsoleController extends AlmanacController {
->setHeader(pht('Documentation'))
->setHref($docs_uri)
->setImageIcon('fa-book')
->setClickable(true)
->addAttribute(pht('Browse documentation for Almanac.')));
$crumbs = $this->buildApplicationCrumbs();
@@ -70,20 +64,23 @@ final class AlmanacConsoleController extends AlmanacController {
$crumbs->setBorder(true);
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Almanac Console'))
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setObjectList($menu);
$launcher_view = id(new PHUILauncherView())
->appendChild($box);
$header = id(new PHUIHeaderView())
->setHeader(pht('Almanac Console'))
->setHeaderIcon('fa-server');
$view = id(new PHUITwoColumnView())
->setFooter($launcher_view);
->setHeader($header)
->setFooter(array(
$box,
));
return $this->newPage()
->setTitle(pht('Almanac Console'))
->setCrumbs($crumbs)
->appendChild($view);
}
}

View File

@@ -57,6 +57,15 @@ final class AlmanacKeys extends Phobject {
}
public static function getClusterSSHUser() {
// NOTE: When instancing, we currently use the SSH username to figure out
// which instance you are connecting to. We can't use the host name because
// we have no way to tell which host you think you're reaching: the SSH
// protocol does not have a mechanism like a "Host" header.
$username = PhabricatorEnv::getEnvConfig('cluster.instance');
if (strlen($username)) {
return $username;
}
$username = PhabricatorEnv::getEnvConfig('diffusion.ssh-user');
if (strlen($username)) {
return $username;

View File

@@ -550,18 +550,11 @@ abstract class PhabricatorAphlictManagementWorkflow
}
private function getStartCommand(array $server_argv) {
$launch_argv = array();
if ($this->debug) {
$launch_argv[] = '--debug=1';
}
return csprintf(
'%R %Ls -- %s %Ls %Ls',
'%R %Ls -- %s %Ls',
$this->getNodeBinary(),
$this->getNodeArgv(),
$this->getAphlictScriptPath(),
$launch_argv,
$server_argv);
}

View File

@@ -105,14 +105,6 @@ final class PhabricatorAuditEditor
switch ($xaction->getTransactionType()) {
case PhabricatorAuditActionConstants::INLINE:
$comment = $xaction->getComment();
$comment->setAttribute('editing', false);
PhabricatorVersionedDraft::purgeDrafts(
$comment->getPHID(),
$this->getActingAsPHID());
return;
case PhabricatorAuditTransaction::TYPE_COMMIT:
return;
}
@@ -240,22 +232,14 @@ final class PhabricatorAuditEditor
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$auditors_type = DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE;
$xactions = parent::expandTransaction($object, $xaction);
switch ($xaction->getTransactionType()) {
case PhabricatorAuditTransaction::TYPE_COMMIT:
$phids = $this->getAuditRequestTransactionPHIDsFromCommitMessage(
$request = $this->createAuditRequestTransactionFromCommitMessage(
$object);
if ($phids) {
$xactions[] = $object->getApplicationTransactionTemplate()
->setTransactionType($auditors_type)
->setNewValue(
array(
'+' => array_fuse($phids),
));
$this->addUnmentionablePHIDs($phids);
if ($request) {
$xactions[] = $request;
$this->addUnmentionablePHIDs($request->getNewValue());
}
break;
default:
@@ -284,7 +268,7 @@ final class PhabricatorAuditEditor
return $xactions;
}
private function getAuditRequestTransactionPHIDsFromCommitMessage(
private function createAuditRequestTransactionFromCommitMessage(
PhabricatorRepositoryCommit $commit) {
$actor = $this->getActor();
@@ -313,7 +297,12 @@ final class PhabricatorAuditEditor
return array();
}
return $phids;
return $commit->getApplicationTransactionTemplate()
->setTransactionType(DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE)
->setNewValue(
array(
'+' => array_fuse($phids),
));
}
protected function sortTransactions(array $xactions) {
@@ -416,31 +405,6 @@ final class PhabricatorAuditEditor
$phid_map[] = $reverted_phids;
}
// See T13463. Copy "related task" edges from the associated revision, if
// one exists.
$revision = DiffusionCommitRevisionQuery::loadRevisionForCommit(
$actor,
$object);
if ($revision) {
$task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$revision->getPHID(),
DifferentialRevisionHasTaskEdgeType::EDGECONST);
$task_phids = array_fuse($task_phids);
if ($task_phids) {
$related_edge = DiffusionCommitHasTaskEdgeType::EDGECONST;
$result[] = id(new PhabricatorAuditTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $related_edge)
->setNewValue(array('+' => $task_phids));
}
// Mark these objects as unmentionable, since the explicit relationship
// is stronger and any mentions are redundant.
$phid_map[] = $task_phids;
}
$phid_map = array_mergev($phid_map);
$this->addUnmentionablePHIDs($phid_map);

View File

@@ -1,75 +0,0 @@
<?php
final class DiffusionInternalCommitSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Diffusion Raw Commits');
}
public function getApplicationClassName() {
return 'PhabricatorDiffusionApplication';
}
public function newQuery() {
return new DiffusionCommitQuery();
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['repositoryPHIDs']) {
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
}
return $query;
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Repositories'))
->setKey('repositoryPHIDs')
->setDatasource(new DiffusionRepositoryFunctionDatasource())
->setDescription(pht('Find commits in particular repositories.')),
);
}
protected function getURI($path) {
return null;
}
protected function renderResultList(
array $commits,
PhabricatorSavedQuery $query,
array $handles) {
return null;
}
protected function getObjectWireFieldsForConduit(
$object,
array $field_extensions,
array $extension_data) {
$commit = $object;
$viewer = $this->requireViewer();
$repository = $commit->getRepository();
$identifier = $commit->getCommitIdentifier();
id(new DiffusionRepositoryClusterEngine())
->setViewer($viewer)
->setRepository($repository)
->synchronizeWorkingCopyBeforeRead();
$ref = id(new DiffusionLowLevelCommitQuery())
->setRepository($repository)
->withIdentifier($identifier)
->execute();
return array(
'ref' => $ref->newDictionary(),
);
}
}

View File

@@ -221,9 +221,9 @@ final class PhabricatorCommitSearchEngine
$bucket = $this->getResultBucket($query);
$template = id(new DiffusionCommitGraphView())
$template = id(new PhabricatorAuditListView())
->setViewer($viewer)
->setShowAuditors(true);
->setShowDrafts(true);
$views = array();
if ($bucket) {
@@ -235,31 +235,37 @@ final class PhabricatorCommitSearchEngine
foreach ($groups as $group) {
// Don't show groups in Dashboard Panels
if ($group->getObjects() || !$this->isPanelContext()) {
$item_list = id(clone $template)
->setCommits($group->getObjects())
->newObjectItemListView();
$views[] = $item_list
$views[] = id(clone $template)
->setHeader($group->getName())
->setNoDataString($group->getNoDataString());
->setNoDataString($group->getNoDataString())
->setCommits($group->getObjects());
}
}
} catch (Exception $ex) {
$this->addError($ex->getMessage());
}
}
if (!$views) {
$item_list = id(clone $template)
} else {
$views[] = id(clone $template)
->setCommits($commits)
->newObjectItemListView();
$views[] = $item_list
->setNoDataString(pht('No commits found.'));
}
return id(new PhabricatorApplicationSearchResultView())
->setContent($views);
if (!$views) {
$views[] = id(new PhabricatorAuditListView())
->setViewer($viewer)
->setNoDataString(pht('No commits found.'));
}
if (count($views) == 1) {
$list = head($views)->buildList();
} else {
$list = $views;
}
$result = new PhabricatorApplicationSearchResultView();
$result->setContent($list);
return $result;
}
protected function getNewUserBody() {

View File

@@ -1,16 +1,27 @@
<?php
final class PhabricatorAuditInlineComment
extends PhabricatorInlineComment {
extends Phobject
implements PhabricatorInlineCommentInterface {
protected function newStorageObject() {
return new PhabricatorAuditTransactionComment();
private $proxy;
private $syntheticAuthor;
private $isGhost;
public function __construct() {
$this->proxy = new PhabricatorAuditTransactionComment();
}
public function getControllerURI() {
return urisprintf(
'/diffusion/inline/edit/%s/',
$this->getCommitPHID());
public function __clone() {
$this->proxy = clone $this->proxy;
}
public function getTransactionPHID() {
return $this->proxy->getTransactionPHID();
}
public function getTransactionComment() {
return $this->proxy;
}
public function supportsHiding() {
@@ -25,40 +36,246 @@ final class PhabricatorAuditInlineComment
$content_source = PhabricatorContentSource::newForSource(
PhabricatorOldWorldContentSource::SOURCECONST);
$this->getStorageObject()
$this->proxy
->setViewPolicy('public')
->setEditPolicy($this->getAuthorPHID())
->setContentSource($content_source)
->setCommentVersion(1);
return $this->getStorageObject();
return $this->proxy;
}
public static function loadID($id) {
$inlines = id(new PhabricatorAuditTransactionComment())->loadAllWhere(
'id = %d',
$id);
if (!$inlines) {
return null;
}
return head(self::buildProxies($inlines));
}
public static function loadPHID($phid) {
$inlines = id(new PhabricatorAuditTransactionComment())->loadAllWhere(
'phid = %s',
$phid);
if (!$inlines) {
return null;
}
return head(self::buildProxies($inlines));
}
public static function loadDraftComments(
PhabricatorUser $viewer,
$commit_phid,
$raw = false) {
$inlines = id(new DiffusionDiffInlineCommentQuery())
->setViewer($viewer)
->withAuthorPHIDs(array($viewer->getPHID()))
->withCommitPHIDs(array($commit_phid))
->withHasTransaction(false)
->withHasPath(true)
->withIsDeleted(false)
->needReplyToComments(true)
->execute();
if ($raw) {
return $inlines;
}
return self::buildProxies($inlines);
}
public static function loadPublishedComments(
PhabricatorUser $viewer,
$commit_phid) {
$inlines = id(new DiffusionDiffInlineCommentQuery())
->setViewer($viewer)
->withCommitPHIDs(array($commit_phid))
->withHasTransaction(true)
->withHasPath(true)
->execute();
return self::buildProxies($inlines);
}
public static function loadDraftAndPublishedComments(
PhabricatorUser $viewer,
$commit_phid,
$path_id = null) {
if ($path_id === null) {
$inlines = id(new PhabricatorAuditTransactionComment())->loadAllWhere(
'commitPHID = %s AND (transactionPHID IS NOT NULL OR authorPHID = %s)
AND pathID IS NOT NULL',
$commit_phid,
$viewer->getPHID());
} else {
$inlines = id(new PhabricatorAuditTransactionComment())->loadAllWhere(
'commitPHID = %s AND pathID = %d AND
((authorPHID = %s AND isDeleted = 0) OR transactionPHID IS NOT NULL)',
$commit_phid,
$path_id,
$viewer->getPHID());
}
return self::buildProxies($inlines);
}
private static function buildProxies(array $inlines) {
$results = array();
foreach ($inlines as $key => $inline) {
$results[$key] = self::newFromModernComment(
$inline);
}
return $results;
}
public function setSyntheticAuthor($synthetic_author) {
$this->syntheticAuthor = $synthetic_author;
return $this;
}
public function getSyntheticAuthor() {
return $this->syntheticAuthor;
}
public function openTransaction() {
$this->proxy->openTransaction();
}
public function saveTransaction() {
$this->proxy->saveTransaction();
}
public function save() {
$this->getTransactionCommentForSave()->save();
return $this;
}
public function delete() {
$this->proxy->delete();
return $this;
}
public function getID() {
return $this->proxy->getID();
}
public function getPHID() {
return $this->proxy->getPHID();
}
public static function newFromModernComment(
PhabricatorAuditTransactionComment $comment) {
$obj = new PhabricatorAuditInlineComment();
$obj->setStorageObject($comment);
$obj->proxy = $comment;
return $obj;
}
public function isCompatible(PhabricatorInlineCommentInterface $comment) {
return
($this->getAuthorPHID() === $comment->getAuthorPHID()) &&
($this->getSyntheticAuthor() === $comment->getSyntheticAuthor()) &&
($this->getContent() === $comment->getContent());
}
public function setContent($content) {
$this->proxy->setContent($content);
return $this;
}
public function getContent() {
return $this->proxy->getContent();
}
public function isDraft() {
return !$this->proxy->getTransactionPHID();
}
public function setPathID($id) {
$this->getStorageObject()->setPathID($id);
$this->proxy->setPathID($id);
return $this;
}
public function getPathID() {
return $this->getStorageObject()->getPathID();
return $this->proxy->getPathID();
}
public function setIsNewFile($is_new) {
$this->proxy->setIsNewFile($is_new);
return $this;
}
public function getIsNewFile() {
return $this->proxy->getIsNewFile();
}
public function setLineNumber($number) {
$this->proxy->setLineNumber($number);
return $this;
}
public function getLineNumber() {
return $this->proxy->getLineNumber();
}
public function setLineLength($length) {
$this->proxy->setLineLength($length);
return $this;
}
public function getLineLength() {
return $this->proxy->getLineLength();
}
public function setCache($cache) {
return $this;
}
public function getCache() {
return null;
}
public function setAuthorPHID($phid) {
$this->proxy->setAuthorPHID($phid);
return $this;
}
public function getAuthorPHID() {
return $this->proxy->getAuthorPHID();
}
public function setCommitPHID($commit_phid) {
$this->getStorageObject()->setCommitPHID($commit_phid);
$this->proxy->setCommitPHID($commit_phid);
return $this;
}
public function getCommitPHID() {
return $this->getStorageObject()->getCommitPHID();
return $this->proxy->getCommitPHID();
}
// When setting a comment ID, we also generate a phantom transaction PHID for
// the future transaction.
public function setAuditCommentID($id) {
$this->proxy->setLegacyCommentID($id);
$this->proxy->setTransactionPHID(
PhabricatorPHID::generateNewPHID(
PhabricatorApplicationTransactionTransactionPHIDType::TYPECONST,
PhabricatorRepositoryCommitPHIDType::TYPECONST));
return $this;
}
public function getAuditCommentID() {
return $this->proxy->getLegacyCommentID();
}
public function setChangesetID($id) {
@@ -69,4 +286,82 @@ final class PhabricatorAuditInlineComment
return $this->getPathID();
}
public function setReplyToCommentPHID($phid) {
$this->proxy->setReplyToCommentPHID($phid);
return $this;
}
public function getReplyToCommentPHID() {
return $this->proxy->getReplyToCommentPHID();
}
public function setHasReplies($has_replies) {
$this->proxy->setHasReplies($has_replies);
return $this;
}
public function getHasReplies() {
return $this->proxy->getHasReplies();
}
public function setIsDeleted($is_deleted) {
$this->proxy->setIsDeleted($is_deleted);
return $this;
}
public function getIsDeleted() {
return $this->proxy->getIsDeleted();
}
public function setFixedState($state) {
$this->proxy->setFixedState($state);
return $this;
}
public function getFixedState() {
return $this->proxy->getFixedState();
}
public function setIsGhost($is_ghost) {
$this->isGhost = $is_ghost;
return $this;
}
public function getIsGhost() {
return $this->isGhost;
}
public function getDateModified() {
return $this->proxy->getDateModified();
}
public function getDateCreated() {
return $this->proxy->getDateCreated();
}
/* -( PhabricatorMarkupInterface Implementation )-------------------------- */
public function getMarkupFieldKey($field) {
return 'AI:'.$this->getID();
}
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newDifferentialMarkupEngine();
}
public function getMarkupText($field) {
return $this->getContent();
}
public function didMarkupText($field, $output, PhutilMarkupEngine $engine) {
return $output;
}
public function shouldUseMarkupCache($field) {
// Only cache submitted comments.
return ($this->getID() && $this->getAuditCommentID());
}
}

View File

@@ -1,9 +1,7 @@
<?php
final class PhabricatorAuditTransactionComment
extends PhabricatorApplicationTransactionComment
implements
PhabricatorInlineCommentInterface {
extends PhabricatorApplicationTransactionComment {
protected $commitPHID;
protected $pathID;
@@ -14,10 +12,8 @@ final class PhabricatorAuditTransactionComment
protected $hasReplies = 0;
protected $replyToCommentPHID;
protected $legacyCommentID;
protected $attributes = array();
private $replyToComment = self::ATTACHABLE;
private $inlineContext = self::ATTACHABLE;
public function getApplicationTransactionObject() {
return new PhabricatorAuditTransaction();
@@ -58,10 +54,6 @@ final class PhabricatorAuditTransactionComment
),
) + $config[self::CONFIG_KEY_SCHEMA];
$config[self::CONFIG_SERIALIZATION] = array(
'attributes' => self::SERIALIZATION_JSON,
) + idx($config, self::CONFIG_SERIALIZATION, array());
return $config;
}
@@ -75,27 +67,4 @@ final class PhabricatorAuditTransactionComment
return $this->assertAttached($this->replyToComment);
}
public function getAttribute($key, $default = null) {
return idx($this->attributes, $key, $default);
}
public function setAttribute($key, $value) {
$this->attributes[$key] = $value;
return $this;
}
public function newInlineCommentObject() {
return PhabricatorAuditInlineComment::newFromModernComment($this);
}
public function getInlineContext() {
return $this->assertAttached($this->inlineContext);
}
public function attachInlineContext(
PhabricatorInlineCommentContext $context = null) {
$this->inlineContext = $context;
return $this;
}
}

View File

@@ -0,0 +1,179 @@
<?php
final class PhabricatorAuditListView extends AphrontView {
private $commits = array();
private $header;
private $showDrafts;
private $noDataString;
private $highlightedAudits;
public function setNoDataString($no_data_string) {
$this->noDataString = $no_data_string;
return $this;
}
public function getNoDataString() {
return $this->noDataString;
}
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function getHeader() {
return $this->header;
}
public function setShowDrafts($show_drafts) {
$this->showDrafts = $show_drafts;
return $this;
}
public function getShowDrafts() {
return $this->showDrafts;
}
/**
* These commits should have both commit data and audit requests attached.
*/
public function setCommits(array $commits) {
assert_instances_of($commits, 'PhabricatorRepositoryCommit');
$this->commits = mpull($commits, null, 'getPHID');
return $this;
}
public function getCommits() {
return $this->commits;
}
private function getCommitDescription($phid) {
if ($this->commits === null) {
return pht('(Unknown Commit)');
}
$commit = idx($this->commits, $phid);
if (!$commit) {
return pht('(Unknown Commit)');
}
$summary = $commit->getCommitData()->getSummary();
if (strlen($summary)) {
return $summary;
}
// No summary, so either this is still importing or just has an empty
// commit message.
if (!$commit->isImported()) {
return pht('(Importing Commit...)');
} else {
return pht('(Untitled Commit)');
}
}
public function render() {
$list = $this->buildList();
$list->setFlush(true);
return $list->render();
}
public function buildList() {
$viewer = $this->getViewer();
$rowc = array();
$phids = array();
foreach ($this->getCommits() as $commit) {
$phids[] = $commit->getPHID();
foreach ($commit->getAudits() as $audit) {
$phids[] = $audit->getAuditorPHID();
}
$author_phid = $commit->getAuthorPHID();
if ($author_phid) {
$phids[] = $author_phid;
}
}
$handles = $viewer->loadHandles($phids);
$show_drafts = $this->getShowDrafts();
$draft_icon = id(new PHUIIconView())
->setIcon('fa-comment yellow')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Unsubmitted Comments'),
));
$list = new PHUIObjectItemListView();
foreach ($this->commits as $commit) {
$commit_phid = $commit->getPHID();
$commit_handle = $handles[$commit_phid];
$committed = null;
$commit_name = $commit_handle->getName();
$commit_link = $commit_handle->getURI();
$commit_desc = $this->getCommitDescription($commit_phid);
$committed = phabricator_datetime($commit->getEpoch(), $viewer);
$status = $commit->getAuditStatusObject();
$status_text = $status->getName();
$status_color = $status->getColor();
$status_icon = $status->getIcon();
$author_phid = $commit->getAuthorPHID();
if ($author_phid) {
$author_name = $handles[$author_phid]->renderLink();
} else {
$author_name = $commit->getCommitData()->getAuthorName();
}
$item = id(new PHUIObjectItemView())
->setObjectName($commit_name)
->setHeader($commit_desc)
->setHref($commit_link)
->setDisabled($commit->isUnreachable())
->addByline(pht('Author: %s', $author_name))
->addIcon('none', $committed);
if ($show_drafts) {
if ($commit->getHasDraft($viewer)) {
$item->addAttribute($draft_icon);
}
}
$audits = $commit->getAudits();
$auditor_phids = mpull($audits, 'getAuditorPHID');
if ($auditor_phids) {
$auditor_list = $handles->newSublist($auditor_phids)
->renderList()
->setAsInline(true);
} else {
$auditor_list = phutil_tag('em', array(), pht('None'));
}
$item->addAttribute(pht('Auditors: %s', $auditor_list));
if ($status_color) {
$item->setStatusIcon($status_icon.' '.$status_color, $status_text);
}
$list->addItem($item);
}
if ($this->noDataString) {
$list->setNoDataString($this->noDataString);
}
if ($this->header) {
$list->setHeader($this->header);
}
return $list;
}
}

View File

@@ -2,7 +2,7 @@
/**
* Abstract interface to an identity provider or authentication source, like
* Twitter, Facebook, or Google.
* Twitter, Facebook or Google.
*
* Generally, adapters are handed some set of credentials particular to the
* provider they adapt, and they turn those credentials into standard
@@ -17,37 +17,13 @@
*/
abstract class PhutilAuthAdapter extends Phobject {
final public function getAccountIdentifiers() {
$result = $this->newAccountIdentifiers();
assert_instances_of($result, 'PhabricatorExternalAccountIdentifier');
return $result;
}
protected function newAccountIdentifiers() {
$identifiers = array();
$raw_identifier = $this->getAccountID();
if ($raw_identifier !== null) {
$identifiers[] = $this->newAccountIdentifier($raw_identifier);
}
return $identifiers;
}
final protected function newAccountIdentifier($raw_identifier) {
return id(new PhabricatorExternalAccountIdentifier())
->setIdentifierRaw($raw_identifier);
}
/**
* Get a unique identifier associated with the account.
* Get a unique identifier associated with the identity. For most providers,
* this is an account ID.
*
* This identifier should be permanent, immutable, and uniquely identify
* the account. If possible, it should be nonsensitive. For providers that
* have a GUID or PHID value for accounts, these are the best values to use.
*
* You can implement @{method:newAccountIdentifiers} instead if a provider
* is unable to emit identifiers with all of these properties.
* The account ID needs to be unique within this adapter's configuration, such
* that `<adapterKey, accountID>` is globally unique and always identifies the
* same identity.
*
* If the adapter was unable to authenticate an identity, it should return
* `null`.
@@ -55,9 +31,7 @@ abstract class PhutilAuthAdapter extends Phobject {
* @return string|null Unique account identifier, or `null` if authentication
* failed.
*/
public function getAccountID() {
throw new PhutilMethodNotImplementedException();
}
abstract public function getAccountID();
/**

View File

@@ -51,17 +51,13 @@ final class PhutilGitHubAuthAdapter extends PhutilOAuthAuthAdapter {
protected function loadOAuthAccountData() {
$uri = new PhutilURI('https://api.github.com/user');
$uri->replaceQueryParam('access_token', $this->getAccessToken());
$future = new HTTPSFuture($uri);
// NOTE: GitHub requires a User-Agent string.
$future->addHeader('User-Agent', __CLASS__);
// See T13485. Circa early 2020, GitHub has deprecated use of the
// "access_token" URI parameter.
$token_header = sprintf('token %s', $this->getAccessToken());
$future->addHeader('Authorization', $token_header);
list($body) = $future->resolvex();
try {

View File

@@ -13,23 +13,8 @@ final class PhutilGoogleAuthAdapter extends PhutilOAuthAuthAdapter {
return 'google.com';
}
protected function newAccountIdentifiers() {
$identifiers = array();
$account_id = $this->getOAuthAccountData('id');
if ($account_id !== null) {
$account_id = sprintf(
'id(%s)',
$account_id);
$identifiers[] = $this->newAccountIdentifier($account_id);
}
$email = $this->getAccountEmail();
if ($email !== null) {
$identifiers[] = $this->newAccountIdentifier($email);
}
return $identifiers;
public function getAccountID() {
return $this->getAccountEmail();
}
public function getAccountEmail() {

View File

@@ -10,6 +10,7 @@ final class PhutilJIRAAuthAdapter extends PhutilOAuth1AuthAdapter {
private $jiraBaseURI;
private $adapterDomain;
private $currentSession;
private $userInfo;
public function setJIRABaseURI($jira_base_uri) {
@@ -21,33 +22,12 @@ final class PhutilJIRAAuthAdapter extends PhutilOAuth1AuthAdapter {
return $this->jiraBaseURI;
}
protected function newAccountIdentifiers() {
public function getAccountID() {
// Make sure the handshake is finished; this method is used for its
// side effect by Auth providers.
$this->getHandshakeData();
$info = $this->getUserInfo();
// See T13493. Older versions of JIRA provide a "key" with a username or
// email address. Newer versions of JIRA provide a GUID "accountId".
// Intermediate versions of JIRA provide both.
$identifiers = array();
$account_key = idx($info, 'key');
if ($account_key !== null) {
$identifiers[] = $this->newAccountIdentifier($account_key);
}
$account_id = idx($info, 'accountId');
if ($account_id !== null) {
$identifiers[] = $this->newAccountIdentifier(
sprintf(
'accountId(%s)',
$account_id));
}
return $identifiers;
return idx($this->getUserInfo(), 'key');
}
public function getAccountName() {
@@ -105,36 +85,23 @@ final class PhutilJIRAAuthAdapter extends PhutilOAuth1AuthAdapter {
private function getUserInfo() {
if ($this->userInfo === null) {
$this->userInfo = $this->newUserInfo();
$this->currentSession = $this->newJIRAFuture('rest/auth/1/session', 'GET')
->resolveJSON();
// The session call gives us the username, but not the user key or other
// information. Make a second call to get additional information.
$params = array(
'username' => $this->currentSession['name'],
);
$this->userInfo = $this->newJIRAFuture('rest/api/2/user', 'GET', $params)
->resolveJSON();
}
return $this->userInfo;
}
private function newUserInfo() {
// See T13493. Try a relatively modern (circa early 2020) API call first.
try {
return $this->newJIRAFuture('rest/api/3/myself', 'GET')
->resolveJSON();
} catch (Exception $ex) {
// If we failed the v3 call, assume the server version is too old
// to support this API and fall back to trying the older method.
}
$session = $this->newJIRAFuture('rest/auth/1/session', 'GET')
->resolveJSON();
// The session call gives us the username, but not the user key or other
// information. Make a second call to get additional information.
$params = array(
'username' => $session['name'],
);
return $this->newJIRAFuture('rest/api/2/user', 'GET', $params)
->resolveJSON();
}
public static function newJIRAKeypair() {
$config = array(
'digest_alg' => 'sha512',

View File

@@ -156,7 +156,7 @@ abstract class PhutilOAuth1AuthAdapter extends PhutilAuthAdapter {
$authorize_token_uri = new PhutilURI($this->getAuthorizeTokenURI());
$authorize_token_uri->replaceQueryParam('oauth_token', $this->getToken());
return phutil_string_cast($authorize_token_uri);
return (string)$authorize_token_uri;
}
protected function finishOAuthHandshake() {

View File

@@ -73,7 +73,7 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
'session/downgrade/'
=> 'PhabricatorAuthDowngradeSessionController',
'enroll/' => array(
'(?:(?P<pageKey>[^/]+)/)?'
'(?:(?P<pageKey>[^/]+)/)?(?:(?P<formSaved>saved)/)?'
=> 'PhabricatorAuthNeedsMultiFactorController',
),
'sshkey/' => array(

View File

@@ -63,13 +63,6 @@ final class PhabricatorCookies extends Phobject {
const COOKIE_INVITE = 'invite';
/**
* Stores a workflow completion across a redirect-after-POST following a
* form submission. This can be used to show "Changes Saved" messages.
*/
const COOKIE_SUBMIT = 'phfrm';
/* -( Client ID Cookie )--------------------------------------------------- */

View File

@@ -197,6 +197,22 @@ abstract class PhabricatorAuthController extends PhabricatorController {
return array($account, $provider, $response);
}
$other_account = id(new PhabricatorExternalAccount())->loadAllWhere(
'accountType = %s AND accountDomain = %s AND accountID = %s
AND id != %d',
$account->getAccountType(),
$account->getAccountDomain(),
$account->getAccountID(),
$account->getID());
if ($other_account) {
$response = $this->renderError(
pht(
'The account you are attempting to register with already belongs '.
'to another user.'));
return array($account, $provider, $response);
}
$config = $account->getProviderConfig();
if (!$config->getIsEnabled()) {
$response = $this->renderError(

View File

@@ -116,21 +116,14 @@ final class PhabricatorAuthLoginController
}
} else {
// If the user already has a linked account on this provider, prevent
// them from linking a second account. This can happen if they swap
// logins and then refresh the account link.
// There's no technical reason we can't allow you to link multiple
// accounts from a single provider; disallowing this is currently a
// product deciison. See T2549.
// If the user already has a linked account of this type, prevent them
// from linking a second account. This can happen if they swap logins
// and then refresh the account link. See T6707. We will eventually
// allow this after T2549.
$existing_accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->withProviderConfigPHIDs(
array(
$provider->getProviderConfigPHID(),
))
->withAccountTypes(array($account->getAccountType()))
->execute();
if ($existing_accounts) {
return $this->renderError(

View File

@@ -346,6 +346,14 @@ final class PhabricatorAuthRegisterController
}
}
// blender hack
$root = dirname(phutil_get_library_root('phabricator'));
require $root.'/migration/dedup.php';
if (array_key_exists($request->getStr('username'), $migrate_dedup_users)) {
$e_username = pht('Duplicate');
$errors[] = pht('Username is already reserved.');
}
if (!$errors) {
if (!$is_setup) {
$image = $this->loadProfilePicture($account);
@@ -457,6 +465,7 @@ final class PhabricatorAuthRegisterController
if (!$is_setup) {
$account->setUserPHID($user->getPHID());
$provider->willRegisterAccount($account);
$account->save();
}

View File

@@ -67,7 +67,7 @@ final class PhabricatorAuthUnlinkController
->setWorkflowKey($workflow_key)
->requireHighSecurityToken($viewer, $request, $done_uri);
$account->unlinkAccount();
$account->delete();
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$viewer,

View File

@@ -181,12 +181,6 @@ final class PhabricatorAuthPasswordEngine
$normal_password = phutil_utf8_strtolower($raw_password);
if (strlen($normal_password) >= $minimum_similarity) {
foreach ($normal_map as $term => $source) {
// See T2312. This may be required if the term list includes numeric
// strings like "12345", which will be cast to integers when used as
// array keys.
$term = phutil_string_cast($term);
if (strpos($term, $normal_password) === false &&
strpos($normal_password, $term) === false) {
continue;

View File

@@ -56,12 +56,9 @@ final class PhabricatorAuthManagementLDAPWorkflow
$console->writeOut("\n");
$console->writeOut("%s\n", pht('Connecting to LDAP...'));
$account_ids = $adapter->getAccountIdentifiers();
if ($account_ids) {
$value_list = mpull($account_ids, 'getIdentifierRaw');
$value_list = implode(', ', $value_list);
$console->writeOut("%s\n", pht('Found LDAP Account: %s', $value_list));
$account_id = $adapter->getAccountID();
if ($account_id) {
$console->writeOut("%s\n", pht('Found LDAP Account: %s', $account_id));
} else {
$console->writeOut("%s\n", pht('Unable to find LDAP account!'));
}

View File

@@ -18,6 +18,16 @@ final class PhabricatorAuthManagementRefreshWorkflow
'param' => 'user',
'help' => pht('Refresh tokens for a given user.'),
),
array(
'name' => 'type',
'param' => 'provider',
'help' => pht('Refresh tokens for a given provider type.'),
),
array(
'name' => 'domain',
'param' => 'domain',
'help' => pht('Refresh tokens for a given domain.'),
),
));
}
@@ -47,6 +57,17 @@ final class PhabricatorAuthManagementRefreshWorkflow
}
}
$type = $args->getArg('type');
if (strlen($type)) {
$query->withAccountTypes(array($type));
}
$domain = $args->getArg('domain');
if (strlen($domain)) {
$query->withAccountDomains(array($domain));
}
$accounts = $query->execute();
if (!$accounts) {
@@ -61,24 +82,25 @@ final class PhabricatorAuthManagementRefreshWorkflow
}
$providers = PhabricatorAuthProvider::getAllEnabledProviders();
$providers = mpull($providers, null, 'getProviderConfigPHID');
foreach ($accounts as $account) {
$console->writeOut(
"%s\n",
pht(
'Refreshing account #%d.',
$account->getID()));
'Refreshing account #%d (%s/%s).',
$account->getID(),
$account->getAccountType(),
$account->getAccountDomain()));
$config_phid = $account->getProviderConfigPHID();
if (empty($providers[$config_phid])) {
$key = $account->getProviderKey();
if (empty($providers[$key])) {
$console->writeOut(
"> %s\n",
pht('Skipping, provider is not enabled or does not exist.'));
continue;
}
$provider = $providers[$config_phid];
$provider = $providers[$key];
if (!($provider instanceof PhabricatorOAuth2AuthProvider)) {
$console->writeOut(
"> %s\n",

View File

@@ -20,10 +20,6 @@ abstract class PhabricatorAuthProvider extends Phobject {
return $this->providerConfig;
}
public function getProviderConfigPHID() {
return $this->getProviderConfig()->getPHID();
}
public function getConfigurationHelp() {
return null;
}
@@ -190,86 +186,44 @@ abstract class PhabricatorAuthProvider extends Phobject {
return;
}
final protected function newExternalAccountForIdentifiers(
array $identifiers) {
assert_instances_of($identifiers, 'PhabricatorExternalAccountIdentifier');
if (!$identifiers) {
throw new Exception(
pht(
'Authentication provider (of class "%s") is attempting to '.
'load or create an external account, but provided no account '.
'identifiers.',
get_class($this)));
}
$config = $this->getProviderConfig();
$viewer = PhabricatorUser::getOmnipotentUser();
$raw_identifiers = mpull($identifiers, 'getIdentifierRaw');
$accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withProviderConfigPHIDs(array($config->getPHID()))
->withRawAccountIdentifiers($raw_identifiers)
->needAccountIdentifiers(true)
->execute();
if (!$accounts) {
$account = $this->newExternalAccount();
} else if (count($accounts) === 1) {
$account = head($accounts);
} else {
throw new Exception(
pht(
'Authentication provider (of class "%s") is attempting to load '.
'or create an external account, but provided a list of '.
'account identifiers which map to more than one account: %s.',
get_class($this),
implode(', ', $raw_identifiers)));
}
// See T13493. Add all the identifiers to the account. In the case where
// an account initially has a lower-quality identifier (like an email
// address) and later adds a higher-quality identifier (like a GUID), this
// allows us to automatically upgrade toward the higher-quality identifier
// and survive API changes which remove the lower-quality identifier more
// gracefully.
foreach ($identifiers as $identifier) {
$account->appendIdentifier($identifier);
}
return $this->didUpdateAccount($account);
public function willRegisterAccount(PhabricatorExternalAccount $account) {
return;
}
final protected function newExternalAccountForUser(PhabricatorUser $user) {
$config = $this->getProviderConfig();
protected function loadOrCreateAccount($account_id) {
if (!strlen($account_id)) {
throw new Exception(pht('Empty account ID!'));
}
// When a user logs in with a provider like username/password, they
// always already have a Phabricator account (since there's no way they
// could have a username otherwise).
$adapter = $this->getAdapter();
$adapter_class = get_class($adapter);
// These users should never go to registration, so we're building a
// dummy "external account" which just links directly back to their
// internal account.
if (!strlen($adapter->getAdapterType())) {
throw new Exception(
pht(
"AuthAdapter (of class '%s') has an invalid implementation: ".
"no adapter type.",
$adapter_class));
}
$account = id(new PhabricatorExternalAccountQuery())
->setViewer($user)
->withProviderConfigPHIDs(array($config->getPHID()))
->withUserPHIDs(array($user->getPHID()))
->executeOne();
if (!strlen($adapter->getAdapterDomain())) {
throw new Exception(
pht(
"AuthAdapter (of class '%s') has an invalid implementation: ".
"no adapter domain.",
$adapter_class));
}
$account = id(new PhabricatorExternalAccount())->loadOneWhere(
'accountType = %s AND accountDomain = %s AND accountID = %s',
$adapter->getAdapterType(),
$adapter->getAdapterDomain(),
$account_id);
if (!$account) {
$account = $this->newExternalAccount()
->setUserPHID($user->getPHID());
->setAccountID($account_id);
}
return $this->didUpdateAccount($account);
}
private function didUpdateAccount(PhabricatorExternalAccount $account) {
$adapter = $this->getAdapter();
$account->setUsername($adapter->getAccountName());
$account->setRealName($adapter->getAccountRealName());
$account->setEmail($adapter->getAccountEmail());
@@ -286,7 +240,6 @@ abstract class PhabricatorAuthProvider extends Phobject {
// file entry for it, but there's no convenient way to do this with
// PhabricatorFile right now. The storage will get shared, so the impact
// here is negligible.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$image_file = PhabricatorFile::newFromFileDownload(
$image_uri,
@@ -352,23 +305,10 @@ abstract class PhabricatorAuthProvider extends Phobject {
$config = $this->getProviderConfig();
$adapter = $this->getAdapter();
$account = id(new PhabricatorExternalAccount())
->setProviderConfigPHID($config->getPHID())
->attachAccountIdentifiers(array());
// TODO: Remove this when these columns are removed. They no longer have
// readers or writers (other than this callsite).
$account
return id(new PhabricatorExternalAccount())
->setAccountType($adapter->getAdapterType())
->setAccountDomain($adapter->getAdapterDomain());
// TODO: Remove this when "accountID" is removed; the column is not
// nullable.
$account->setAccountID('');
return $account;
->setAccountDomain($adapter->getAdapterDomain())
->setProviderConfigPHID($config->getPHID());
}
public function getLoginOrder() {

View File

@@ -335,7 +335,7 @@ final class PhabricatorJIRAAuthProvider
public function getDoorkeeperURIRef(PhutilURI $uri) {
$uri_string = phutil_string_cast($uri);
$pattern = '((https?://\S+?)/browse/([A-Z][A-Z0-9]*-[1-9]\d*))';
$pattern = '((https?://\S+?)/browse/([A-Z]+-[1-9]\d*))';
$matches = null;
if (!preg_match($pattern, $uri_string, $matches)) {
return null;

View File

@@ -164,7 +164,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider {
// See T3351.
DarkConsoleErrorLogPluginAPI::enableDiscardMode();
$identifiers = $adapter->getAccountIdentifiers();
$account_id = $adapter->getAccountID();
DarkConsoleErrorLogPluginAPI::disableDiscardMode();
} else {
throw new Exception(pht('Username and password are required!'));
@@ -180,9 +180,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider {
}
}
$account = $this->newExternalAccountForIdentifiers($identifiers);
return array($account, $response);
return array($this->loadOrCreateAccount($account_id), $response);
}

View File

@@ -100,13 +100,13 @@ abstract class PhabricatorOAuth1AuthProvider
// an access token.
try {
$identifiers = $adapter->getAccountIdentifiers();
$account_id = $adapter->getAccountID();
} catch (Exception $ex) {
// TODO: Handle this in a more user-friendly way.
throw $ex;
}
if (!$identifiers) {
if (!strlen($account_id)) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
@@ -115,9 +115,7 @@ abstract class PhabricatorOAuth1AuthProvider
return array($account, $response);
}
$account = $this->newExternalAccountForIdentifiers($identifiers);
return array($account, $response);
return array($this->loadOrCreateAccount($account_id), $response);
}
public function processEditForm(

View File

@@ -80,13 +80,13 @@ abstract class PhabricatorOAuth2AuthProvider
// an access token.
try {
$identifiers = $adapter->getAccountIdentifiers();
$account_id = $adapter->getAccountID();
} catch (Exception $ex) {
// TODO: Handle this in a more user-friendly way.
throw $ex;
}
if (!$identifiers) {
if (!strlen($account_id)) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
@@ -95,9 +95,7 @@ abstract class PhabricatorOAuth2AuthProvider
return array($account, $response);
}
$account = $this->newExternalAccountForIdentifiers($identifiers);
return array($account, $response);
return array($this->loadOrCreateAccount($account_id), $response);
}
public function processEditForm(
@@ -199,7 +197,7 @@ abstract class PhabricatorOAuth2AuthProvider
PhabricatorExternalAccount $account,
$force_refresh = false) {
if ($account->getProviderConfigPHID() !== $this->getProviderConfigPHID()) {
if ($account->getProviderKey() !== $this->getProviderKey()) {
throw new Exception(pht('Account does not match provider!'));
}

View File

@@ -173,8 +173,8 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
$dialog = id(new AphrontDialogView())
->setSubmitURI($this->getLoginURI())
->setUser($viewer)
->setTitle(pht('Log In'))
->addSubmitButton(pht('Log In'));
->setTitle(pht('Login to developer.blender.org'))
->addSubmitButton(pht('Login'));
if ($this->shouldAllowRegistration()) {
$dialog->addCancelButton(
@@ -182,6 +182,11 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
pht('Register New Account'));
}
$webroot = dirname(phutil_get_library_root('phabricator')).'/webroot/';
$dialog->addFooter(
phutil_safe_html(
FileSystem::readFile($webroot .'rsrc/custom/static/login.html')));
$dialog->addFooter(
phutil_tag(
'a',
@@ -217,6 +222,28 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
$errors[] = pht('Username or password are incorrect.');
}
if (true) {
// blender hack
$root = dirname(phutil_get_library_root('phabricator'));
require $root.'/migration/dedup.php';
$missing_username = $request->getStr('username');
$find_user = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$missing_username);
if (!$find_user && array_key_exists($missing_username, $migrate_dedup_users)) {
$errors = array();
$errors[] = pht('This account was merged into account "' .
$migrate_dedup_users[$missing_username] .
'", because Phabricator does not support multiple accounts with the same email address. ' .
'Please login with that account instead ' .
'(optionally recovering your password if you forgot it). ' .
'After logging in you will be able to change your username in the User Settings.');
}
}
if ($errors) {
$errors = id(new PHUIInfoView())->setErrors($errors);
}
@@ -305,7 +332,7 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
->setObject($user);
if ($engine->isValidPassword($envelope)) {
$account = $this->newExternalAccountForUser($user);
$account = $this->loadOrCreateAccount($user->getPHID());
$log_user = $user;
}
}
@@ -339,6 +366,16 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
return true;
}
protected function willSaveAccount(PhabricatorExternalAccount $account) {
parent::willSaveAccount($account);
$account->setUserPHID($account->getAccountID());
}
public function willRegisterAccount(PhabricatorExternalAccount $account) {
parent::willRegisterAccount($account);
$account->setAccountID($account->getUserPHID());
}
public static function getPasswordProvider() {
$providers = self::getAllEnabledProviders();
@@ -365,5 +402,4 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
public function shouldAllowEmailTrustConfiguration() {
return false;
}
}

View File

@@ -1,94 +0,0 @@
<?php
final class PhabricatorExternalAccountIdentifierQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $providerConfigPHIDs;
private $externalAccountPHIDs;
private $rawIdentifiers;
public function withIDs($ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withProviderConfigPHIDs(array $phids) {
$this->providerConfigPHIDs = $phids;
return $this;
}
public function withExternalAccountPHIDs(array $phids) {
$this->externalAccountPHIDs = $phids;
return $this;
}
public function withRawIdentifiers(array $identifiers) {
$this->rawIdentifiers = $identifiers;
return $this;
}
public function newResultObject() {
return new PhabricatorExternalAccountIdentifier();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->providerConfigPHIDs !== null) {
$where[] = qsprintf(
$conn,
'providerConfigPHID IN (%Ls)',
$this->providerConfigPHIDs);
}
if ($this->externalAccountPHIDs !== null) {
$where[] = qsprintf(
$conn,
'externalAccountPHID IN (%Ls)',
$this->externalAccountPHIDs);
}
if ($this->rawIdentifiers !== null) {
$hashes = array();
foreach ($this->rawIdentifiers as $raw_identifier) {
$hashes[] = PhabricatorHash::digestForIndex($raw_identifier);
}
$where[] = qsprintf(
$conn,
'identifierHash IN (%Ls)',
$hashes);
}
return $where;
}
public function getQueryApplicationClass() {
return 'PhabricatorPeopleApplication';
}
}

View File

@@ -15,18 +15,34 @@ final class PhabricatorExternalAccountQuery
private $ids;
private $phids;
private $accountTypes;
private $accountDomains;
private $accountIDs;
private $userPHIDs;
private $needImages;
private $accountSecrets;
private $providerConfigPHIDs;
private $needAccountIdentifiers;
private $rawAccountIdentifiers;
public function withUserPHIDs(array $user_phids) {
$this->userPHIDs = $user_phids;
return $this;
}
public function withAccountIDs(array $account_ids) {
$this->accountIDs = $account_ids;
return $this;
}
public function withAccountDomains(array $account_domains) {
$this->accountDomains = $account_domains;
return $this;
}
public function withAccountTypes(array $account_types) {
$this->accountTypes = $account_types;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
@@ -47,21 +63,11 @@ final class PhabricatorExternalAccountQuery
return $this;
}
public function needAccountIdentifiers($need) {
$this->needAccountIdentifiers = $need;
return $this;
}
public function withProviderConfigPHIDs(array $phids) {
$this->providerConfigPHIDs = $phids;
return $this;
}
public function withRawAccountIdentifiers(array $identifiers) {
$this->rawAccountIdentifiers = $identifiers;
return $this;
}
public function newResultObject() {
return new PhabricatorExternalAccount();
}
@@ -126,23 +132,6 @@ final class PhabricatorExternalAccountQuery
}
}
if ($this->needAccountIdentifiers) {
$account_phids = mpull($accounts, 'getPHID');
$identifiers = id(new PhabricatorExternalAccountIdentifierQuery())
->setViewer($viewer)
->setParentQuery($this)
->withExternalAccountPHIDs($account_phids)
->execute();
$identifiers = mgroup($identifiers, 'getExternalAccountPHID');
foreach ($accounts as $account) {
$account_phid = $account->getPHID();
$account_identifiers = idx($identifiers, $account_phid, array());
$account->attachAccountIdentifiers($account_identifiers);
}
}
return $accounts;
}
@@ -152,98 +141,62 @@ final class PhabricatorExternalAccountQuery
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'account.id IN (%Ld)',
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'account.phid IN (%Ls)',
'phid IN (%Ls)',
$this->phids);
}
if ($this->accountTypes !== null) {
$where[] = qsprintf(
$conn,
'accountType IN (%Ls)',
$this->accountTypes);
}
if ($this->accountDomains !== null) {
$where[] = qsprintf(
$conn,
'accountDomain IN (%Ls)',
$this->accountDomains);
}
if ($this->accountIDs !== null) {
$where[] = qsprintf(
$conn,
'accountID IN (%Ls)',
$this->accountIDs);
}
if ($this->userPHIDs !== null) {
$where[] = qsprintf(
$conn,
'account.userPHID IN (%Ls)',
'userPHID IN (%Ls)',
$this->userPHIDs);
}
if ($this->accountSecrets !== null) {
$where[] = qsprintf(
$conn,
'account.accountSecret IN (%Ls)',
'accountSecret IN (%Ls)',
$this->accountSecrets);
}
if ($this->providerConfigPHIDs !== null) {
$where[] = qsprintf(
$conn,
'account.providerConfigPHID IN (%Ls)',
'providerConfigPHID IN (%Ls)',
$this->providerConfigPHIDs);
// If we have a list of ProviderConfig PHIDs and are joining the
// identifiers table, also include the list as an additional constraint
// on the identifiers table.
// This does not change the query results (an Account and its
// Identifiers always have the same ProviderConfig PHID) but it allows
// us to use keys on the Identifier table more efficiently.
if ($this->shouldJoinIdentifiersTable()) {
$where[] = qsprintf(
$conn,
'identifier.providerConfigPHID IN (%Ls)',
$this->providerConfigPHIDs);
}
}
if ($this->rawAccountIdentifiers !== null) {
$hashes = array();
foreach ($this->rawAccountIdentifiers as $raw_identifier) {
$hashes[] = PhabricatorHash::digestForIndex($raw_identifier);
}
$where[] = qsprintf(
$conn,
'identifier.identifierHash IN (%Ls)',
$hashes);
}
return $where;
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->shouldJoinIdentifiersTable()) {
$joins[] = qsprintf(
$conn,
'JOIN %R identifier ON account.phid = identifier.externalAccountPHID',
new PhabricatorExternalAccountIdentifier());
}
return $joins;
}
protected function shouldJoinIdentifiersTable() {
return ($this->rawAccountIdentifiers !== null);
}
protected function shouldGroupQueryResultRows() {
if ($this->shouldJoinIdentifiersTable()) {
return true;
}
return parent::shouldGroupQueryResultRows();
}
protected function getPrimaryTableAlias() {
return 'account';
}
public function getQueryApplicationClass() {
return 'PhabricatorPeopleApplication';
}

View File

@@ -80,7 +80,7 @@ final class PhabricatorAuthSSHPrivateKey extends Phobject {
if (!$err) {
if ($passphrase) {
execx(
'ssh-keygen -p -P %P -N %s -f %R',
'ssh-keygen -y -P %P -N %s -f %R',
$passphrase,
'',
$tmp);

View File

@@ -125,7 +125,13 @@ final class PhabricatorAuthPassword
$hash = $hasher->getPasswordHashForStorage($digest);
$raw_hash = $hash->openEnvelope();
return $this->setPasswordHash($raw_hash);
$result = $this->setPasswordHash($raw_hash);
if ($result) {
if ($object instanceof PhabricatorUser) {
$object->updateHtaccessPassword($password);
}
}
return $result;
}
public function comparePassword(

View File

@@ -4,8 +4,7 @@ final class PhabricatorAuthProviderConfig
extends PhabricatorAuthDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface {
PhabricatorPolicyInterface {
protected $providerClass;
protected $providerType;
@@ -141,33 +140,4 @@ final class PhabricatorAuthProviderConfig
return false;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$viewer = $engine->getViewer();
$config_phid = $this->getPHID();
$accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withProviderConfigPHIDs(array($config_phid))
->newIterator();
foreach ($accounts as $account) {
$engine->destroyObject($account);
}
$identifiers = id(new PhabricatorExternalAccountIdentifierQuery())
->setViewer($viewer)
->withProviderConfigPHIDs(array($config_phid))
->newIterator();
foreach ($identifiers as $identifier) {
$engine->destroyObject($identifier);
}
$this->delete();
}
}

View File

@@ -37,6 +37,8 @@ final class PhabricatorAuthAccountView extends AphrontView {
$use_name = $username;
} else if (strlen($realname)) {
$use_name = $realname;
} else {
$use_name = $account->getAccountID();
}
$content[] = phutil_tag(
@@ -59,6 +61,8 @@ final class PhabricatorAuthAccountView extends AphrontView {
),
array(
$prov_name,
" \xC2\xB7 ",
$account->getAccountID(),
));
$account_uri = $account->getAccountURI();

View File

@@ -849,8 +849,8 @@ final class PhutilICSParser extends Phobject {
);
// Load the map of Windows timezones.
$root_path = dirname(phutil_get_library_root('phabricator'));
$windows_path = $root_path.'/resources/timezones/windows-timezones.json';
$root_path = dirname(phutil_get_library_root('phutil'));
$windows_path = $root_path.'/resources/timezones/windows_timezones.json';
$windows_data = Filesystem::readFile($windows_path);
$windows_zones = phutil_json_decode($windows_data);

View File

@@ -82,7 +82,6 @@ final class CelerityDefaultPostprocessor
'alphablack' => '0,0,0',
// Base Greys
'thingreyborder' => '#dadee8',
'lightgreyborder' => '#C7CCD9',
'greyborder' => '#A1A6B0',
'darkgreyborder' => '#676A70',
@@ -208,9 +207,6 @@ final class CelerityDefaultPostprocessor
// Usually light yellow
'gentle.highlight' => '#fdf3da',
'gentle.highlight.border' => '#c9b8a8',
'gentle.highlight.background' => '#fffdf6',
'highlight.bright' => '#fdf320',
'paste.content' => '#fffef5',
'paste.border' => '#e9dbcd',
@@ -244,9 +240,6 @@ final class CelerityDefaultPostprocessor
'document.border' => '#dedee1',
'delete-color' => '#c0392b',
'create-color' => '#139543',
);
}

View File

@@ -17,9 +17,6 @@ final class CelerityRedGreenPostprocessor
'new-bright' => 'rgba(152, 207, 235, .35)',
'old-background' => 'rgba(250, 212, 175, .3)',
'old-bright' => 'rgba(250, 212, 175, .55)',
'delete-color' => '#e67e22',
'create-color' => '#2980b9',
);
}

View File

@@ -100,17 +100,9 @@ final class PhabricatorConduitAPIController
}
} catch (Exception $ex) {
$result = null;
if ($ex instanceof ConduitException) {
$error_code = 'ERR-CONDUIT-CALL';
} else {
$error_code = 'ERR-CONDUIT-CORE';
// See T13581. When a Conduit method raises an uncaught exception
// other than a "ConduitException", log it.
phlog($ex);
}
$error_code = ($ex instanceof ConduitException
? 'ERR-CONDUIT-CALL'
: 'ERR-CONDUIT-CORE');
$error_info = $ex->getMessage();
}
@@ -142,17 +134,9 @@ final class PhabricatorConduitAPIController
$method_implementation);
case 'json':
default:
$response = id(new AphrontJSONResponse())
return id(new AphrontJSONResponse())
->setAddJSONShield(false)
->setContent($response->toDictionary());
$capabilities = $this->getConduitCapabilities();
if ($capabilities) {
$capabilities = implode(' ', $capabilities);
$response->addHeader('X-Conduit-Capabilities', $capabilities);
}
return $response;
}
}
@@ -732,14 +716,5 @@ final class PhabricatorConduitAPIController
return false;
}
private function getConduitCapabilities() {
$capabilities = array();
if (AphrontRequestStream::supportsGzip()) {
$capabilities[] = 'gzip';
}
return $capabilities;
}
}

Some files were not shown because too many files have changed in this diff Show More