Compare commits

..

182 Commits
5.0.7 ... 5.2

Author SHA1 Message Date
Mario
5eefdc6485 Merge branch '5.2RC' 2021-01-13 09:50:53 +00:00
Mario
4d2bcbc583 release 5.2 2021-01-13 09:48:47 +00:00
Mario
299c4bda1b update changelog 2021-01-13 09:47:44 +00:00
Mario
f4b9291d42 Merge branch 'dev' into 5.2RC 2021-01-13 09:39:55 +00:00
Mario
b7afc905ec Revert "keyid adapt for backward compatibility to current release part 2"
This reverts commit efb8a29b5f.
2021-01-13 09:34:38 +00:00
Mario
01bd846433 Revert "keyid adapt for backward compatibility to current release"
This reverts commit ff100a499a.
2021-01-13 09:34:03 +00:00
Mario
08f717d4fc Merge branch 'dev' into 5.2RC 2021-01-13 09:01:31 +00:00
Mario
b9fd87b004 do not turn the groups own direkt messages into group items - fixes #1510 2021-01-13 08:54:58 +00:00
Mario
1615f2c79a Merge branch 'dev' into 5.2RC 2021-01-12 09:08:28 +00:00
Mario
f430db0de7 changelog 2021-01-12 09:04:32 +00:00
Mario
32eb603643 lukasreschke/id3parser is not maintained anymore. fix it so that composer dump-autoload will not complain 2021-01-12 08:52:08 +00:00
Mario
c3f387f22a more changelog 2021-01-12 08:35:22 +00:00
Mario
8703caff5f restrict length of short profile title/description to 190characters to omit sql warnings about too long string data 2021-01-12 08:25:09 +00:00
Mario
c5d50c9e47 Ãupdate changelog 2021-01-11 20:33:17 +00:00
Mario
fed4e3e03a Ãupdate changelog 2021-01-11 20:32:33 +00:00
Mario
9d1f73e179 5.2RC4 2021-01-11 19:02:26 +00:00
Mario
ba164d9488 Merge branch 'dev' into 5.2RC 2021-01-11 19:01:02 +00:00
Mario
3fe67eb646 more libzotdir
(cherry picked from commit e339e897ff)
2021-01-11 19:55:07 +01:00
Mario
12ba2c30b9 can not access global from statc method 2021-01-11 08:59:16 +00:00
Mario Vavti
7c1b41019e missing constant definition 2021-01-10 22:42:57 +01:00
Mario Vavti
707110e5a7 sync_directories() omit known dead sites 2021-01-10 21:46:31 +01:00
Mario Vavti
ba9a9cb016 undefined variable 2021-01-10 21:32:42 +01:00
Mario
c1c75c4b67 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-01-10 00:32:45 +00:00
Mario
0446349b89 missing use statement 2021-01-10 00:32:34 +00:00
Mario Vavti
915cb44601 typo 2021-01-09 23:06:56 +01:00
Mario
17a153fb6d Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-01-09 21:54:55 +00:00
Mario
e339e897ff more libzotdir 2021-01-09 21:54:41 +00:00
Mario
6b57f163bd fix markup
(cherry picked from commit 4ace4819ff)
2021-01-09 22:05:40 +01:00
Mario
4ace4819ff fix markup 2021-01-09 22:04:47 +01:00
mjfriaza
ce893122af Update Spanish translation
(cherry picked from commit c90d1fc8ef)
2021-01-09 22:02:35 +01:00
Mario
fb5188a8c6 Merge remote-tracking branch 'codeberg/dev' into dev 2021-01-09 21:01:24 +00:00
hubzilla
c17f452fc7 Merge pull request 'Update Spanish translation' (#2) from mjfriaza/hubzilla:dev into dev
Reviewed-on: https://codeberg.org/hubzilla/hubzilla/pulls/2
2021-01-09 21:59:22 +01:00
Mario
71b0f54b7a update sbom 2021-01-09 20:55:40 +00:00
Mario
d387d021fe update sbom 2021-01-09 20:54:48 +00:00
Mario
d6fefc3603 5.2RC3 2021-01-09 20:34:21 +00:00
Mario
efb8a29b5f keyid adapt for backward compatibility to current release part 2 2021-01-09 20:20:48 +00:00
Mario
ff100a499a keyid adapt for backward compatibility to current release 2021-01-09 19:57:55 +00:00
Mario
d89dc65330 remove redundant mail.apd
(cherry picked from commit 898df6287a)
2021-01-09 20:33:12 +01:00
Mario
fa41527f85 fix some php8 fatal errors
(cherry picked from commit 2522d42c71)
2021-01-09 20:32:40 +01:00
Mario
f48d844e42 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-01-09 19:27:46 +00:00
Mario
898df6287a remove redundant mail.apd 2021-01-09 19:27:32 +00:00
Mario
2522d42c71 fix some php8 fatal errors 2021-01-09 20:22:47 +01:00
Mario
39c5e85564 RC2 2021-01-09 15:50:57 +01:00
Mario
1294c05a91 version 2021-01-09 13:27:57 +00:00
Mario
64f802d4f6 fix error in sql query
(cherry picked from commit 0e2af40329)
2021-01-09 14:25:41 +01:00
Mario
61e782389c more streamline keyid 2021-01-09 13:24:32 +00:00
Mario
7cfb230a5e streamline keyid 2021-01-09 13:22:43 +00:00
Mario
0e2af40329 fix error in sql query 2021-01-09 13:05:31 +00:00
Mario
7f51ff0a8d set resource_type = group_item and resource_id = original mid for forum items so we do not need to query the body for the original mid on edit. 2021-01-08 21:27:06 +00:00
Mario Vavti
d1a61c6dce allow deletes for e.g. w2w posts where we are the author but item wall is not set. ownership is checked at the receiving side anyway 2021-01-08 13:12:01 +01:00
mjfriaza
c90d1fc8ef Update Spanish translation 2021-01-08 11:29:32 +01:00
Mario
42cd046e90 RC1 2021-01-06 20:37:04 +01:00
Mario
52fa350138 error in logic
(cherry picked from commit 16082456df)
2021-01-06 20:35:16 +01:00
Mario
4ea8357c6a remove logging
(cherry picked from commit 49cc69ecc5)
2021-01-06 20:35:03 +01:00
Mario
dd0da70b06 filter out self and child folders from the folder list
(cherry picked from commit a0c8e1959a)
2021-01-06 20:34:49 +01:00
Mario
d80f2a621d simplify attach_folder_select_list()
(cherry picked from commit c7010dac3c)
2021-01-06 20:33:59 +01:00
Mario
16082456df error in logic 2021-01-06 19:32:36 +00:00
Mario
49cc69ecc5 remove logging 2021-01-06 19:27:26 +00:00
Mario
a0c8e1959a filter out self and child folders from the folder list 2021-01-06 19:26:40 +00:00
Mario
c7010dac3c simplify attach_folder_select_list() 2021-01-06 19:22:29 +00:00
Mario
99bfc3aaa0 version 5.3 2021-01-05 08:48:30 +00:00
Mario
43b3922803 dump autoload 2021-01-05 08:45:23 +00:00
Mario
ca70ad1a9f version and strings 2021-01-05 08:44:10 +00:00
Mario Vavti
a250419b59 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-01-04 20:36:37 +01:00
Mario Vavti
291644f29d fix issue where categories were not saved on forum wall post 2021-01-04 20:36:24 +01:00
Mario
d4e97ab64c more work on tiles 2021-01-04 13:43:46 +00:00
Mario
e92f98d3c6 rounded corners 2021-01-04 11:14:01 +00:00
Mario
999aac19de missing translateable string and adapt icons 2021-01-04 11:09:12 +00:00
Mario
b2f6e5673d some work on the tiles view 2021-01-04 10:58:41 +00:00
Mario
f0d1c962a7 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-01-03 20:18:05 +00:00
Mario
6999b8d9b4 fix no channel_id provided for contact_remove() in reply_purge(). fix wrong notifier command 2021-01-03 20:17:45 +00:00
Max Kostikov
46e704d507 Merge branch 'dev' into 'dev'
Update Russian translation

See merge request hubzilla/core!1892
2021-01-03 17:05:03 +01:00
Max Kostikov
308e94ea79 Update hstrings.php 2021-01-03 16:43:45 +01:00
Max Kostikov
0c839b2738 Update hmessages.po 2021-01-03 16:43:22 +01:00
Mario
cff5859c59 mod cloud strings 2021-01-03 14:28:02 +00:00
Mario
6d95f5fc98 files_ng: slightly change the way we display link bbcode 2021-01-03 10:07:54 +00:00
Mario
7684861249 do not check against undefined variable 2021-01-02 21:37:06 +00:00
Mario
9759cb7075 fix typo 2021-01-02 21:18:49 +00:00
Mario
b3a5f9bef4 wrong variable 2021-01-02 21:10:47 +00:00
Mario
3e3b6cc1e1 wrong variable 2021-01-02 21:01:42 +00:00
Mario
474103dc82 remove unused code and fix width and height not defined 2021-01-02 20:56:18 +00:00
Mario
092a8f2d05 fix wrong variable 2021-01-02 20:39:58 +00:00
Mario
2bcf0c354a fix wrong variable 2021-01-02 20:39:47 +00:00
Mario
73cc756cac fix wrong variable 2021-01-02 20:38:56 +00:00
Mario
6091c2dba0 fix wrong variable 2021-01-02 20:38:44 +00:00
Mario
2862a73253 fix typo 2021-01-02 20:38:11 +00:00
Mario
850c3f2b6a fix typos 2021-01-02 18:58:20 +00:00
Mario
98f3e4cbd3 set is_owner does not need to be a per item flag. fix post button visible for collections 2021-01-02 18:53:23 +00:00
Mario
efc8ed4845 make share title h3 2021-01-02 11:20:03 +00:00
Mario
0bc4c7d1a0 version 5.0.8 2020-12-30 15:01:17 +00:00
Mario
be2e754d78 changelog
(cherry picked from commit 817e72846e)
2020-12-30 15:59:13 +01:00
Mario
817e72846e changelog 2020-12-30 14:55:00 +00:00
Mario
220768bffc escape both single and double quotes for the notifications title. fixes issue #1503
(cherry picked from commit 11d61a744d)
2020-12-30 15:40:15 +01:00
Mario Vavti
3cd64bcb22 random_profile: return zot6 entries
(cherry picked from commit 5cefdbf985)
2020-12-30 15:40:03 +01:00
Mario Vavti
86bfb07c29 dirserach: return zot6 entries
(cherry picked from commit 5485f96625)
2020-12-30 15:39:48 +01:00
Max Kostikov
ad08bd62aa Revert "Fix sync item with Zot connections"
This reverts commit 3db4aa69440553788d8f46cf4bbfb38ca4f09130


(cherry picked from commit c063fe0720)
2020-12-30 15:39:33 +01:00
Max Kostikov
d79cab0680 Deduplicate contacts list on autocomplete
(cherry picked from commit d889547b26)
2020-12-30 15:38:26 +01:00
Mario
d1d6f7d838 sse: fix issue with direct message notificationss
(cherry picked from commit d5eeb948d5)
2020-12-30 15:38:09 +01:00
Max Kostikov
e31a7e5c9d Do not revalidate cached photos
(cherry picked from commit ca051e943f)
2020-12-30 15:37:47 +01:00
Max Kostikov
e6f0d9887c Implement Imagemagick resources consumption limiting
(cherry picked from commit 36d89d02e1)
2020-12-30 15:37:28 +01:00
Mario
2855d84fba owa specify key
(cherry picked from commit f2258d4202)
2020-12-30 15:36:25 +01:00
Mario
3c19648a56 fix issue where an array was passed to get_key() instead of a string
(cherry picked from commit 81a1aedeb9)
2020-12-30 15:35:38 +01:00
Mario
8db367c743 remove fallback code - it will not be required if compression is dismissed 2020-12-30 14:30:39 +00:00
Mario
2c4fabba35 store zip files without compression 2020-12-30 14:12:08 +00:00
Mario
dff42ffb41 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2020-12-29 20:16:35 +00:00
Mario
11d61a744d escape both single and double quotes for the notifications title. fixes issue #1503 2020-12-29 20:16:23 +00:00
Mario Vavti
5cefdbf985 random_profile: return zot6 entries 2020-12-29 20:37:23 +01:00
Mario Vavti
5485f96625 dirserach: return zot6 entries 2020-12-29 20:36:41 +01:00
Mario
af0f46d23d Merge branch 'cloneissue' into 'dev'
Fix sync comments with clones

See merge request hubzilla/core!1889
2020-12-29 15:50:33 +01:00
Max Kostikov
c063fe0720 Revert "Fix sync item with Zot connections"
This reverts commit 3db4aa69440553788d8f46cf4bbfb38ca4f09130
2020-12-29 15:50:32 +01:00
Mario
203ba7343c Merge branch 'autocomplete' into 'dev'
Deduplicate contacts list on autocomplete

See merge request hubzilla/core!1891
2020-12-29 11:49:52 +01:00
Max Kostikov
d889547b26 Deduplicate contacts list on autocomplete 2020-12-29 11:49:52 +01:00
Mario
b66e19dc43 Merge branch 'photocache' into 'dev'
Do not revalidate cached photos

See merge request hubzilla/core!1890
2020-12-29 11:49:33 +01:00
Mario
c84e3334bf Merge branch 'tuneimagick' into 'dev'
Implement Imagemagick resources consumption limiting

See merge request hubzilla/core!1888
2020-12-29 11:49:11 +01:00
Mario
d5eeb948d5 sse: fix issue with direct message notificationss 2020-12-28 13:19:40 +00:00
Mario
9670833a5d files_ng: provide a fallback in case the server timed out on compressing the zip file 2020-12-28 10:12:25 +00:00
Max Kostikov
ca051e943f Do not revalidate cached photos 2020-12-26 14:24:04 +01:00
Max Kostikov
36d89d02e1 Implement Imagemagick resources consumption limiting 2020-12-26 14:10:56 +01:00
Mario
f4bfa77942 version bump 2020-12-23 09:16:45 +00:00
Mario
f2258d4202 owa specify key 2020-12-23 08:32:59 +00:00
Mario
d56bf34326 fix javascript warning 2020-12-23 08:22:31 +00:00
Mario
9fa3dee522 remove deprecated forum autocomplete code 2020-12-23 08:19:11 +00:00
Mario
7ee2192c29 provide some info and remove unused code 2020-12-22 13:33:54 +00:00
Mario
3a38292bab files_ng: improve download handlÃing 2020-12-22 13:23:20 +00:00
Mario
60cbb65d84 files_ng: omit parent folders of a download 2020-12-22 09:01:52 +00:00
Mario
d118ab71e6 files_ng: implement directory and bulk file download 2020-12-21 21:37:10 +00:00
Mario
81a1aedeb9 fix issue where an array was passed to get_key() instead of a string 2020-12-21 21:31:52 +00:00
Mario
638f7a1c89 changelog 2020-12-21 08:03:17 +00:00
Max Kostikov
5de4c3cc3f Merge branch 'dev' into 'dev'
Use Zot6 for CardDAV and CalDAV sync between clones

See merge request hubzilla/core!1886
2020-12-20 00:01:50 +01:00
Max Kostikov
6579007ca0 Fix CardDAV address book ID 2020-12-19 23:57:14 +01:00
Max Kostikov
c596fb14bb Use Zot6 for CardDAV and CalDAV sync between clones 2020-12-19 22:58:17 +01:00
Max Kostikov
116fc4e00d Merge branch 'dev' into 'dev'
Dev sync

See merge request kostikov/core!4
2020-12-19 22:02:51 +01:00
Mario
57f6f54f29 refresh_all will be called from the notifier in Daemon/Directory 2020-12-19 15:21:34 +00:00
Mario
8640e6d1df deprecate ! and !! forum tags 2020-12-18 13:26:51 +00:00
Mario
801583fd07 flag forums where we do not have post permissions and filter them from the acl selector 2020-12-18 12:40:36 +00:00
Mario Vavti
4987534eba Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2020-12-18 10:02:43 +01:00
Mario Vavti
ed4bf1c13d handle owa with hubloc_id_url only 2020-12-18 10:02:13 +01:00
Mario
5b24225251 do not json_encode the attach array - fixes #1499 2020-12-17 18:57:04 +00:00
Mario
269efb9c1e changelog for version 5.0.6 2020-12-17 13:45:14 +00:00
Mario
4c054d6de9 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2020-12-17 12:27:49 +00:00
Mario Vavti
34a58369f2 update to fix empty hubloc_id_url caused by clone import bug for zot hublocs which was fixed in 4693069a06 2020-12-17 13:27:11 +01:00
Mario Vavti
59b4764a15 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2020-12-17 12:46:33 +01:00
Mario Vavti
7ebba75fa2 restrict owa to zot6 and zot network until the hubloc madness with activitypub and possibly also diaspora will be solved 2020-12-17 12:46:22 +01:00
Mario
b2e80efe3f files_ng: provide bbcode snippets in the info panel 2020-12-17 10:04:24 +00:00
Mario
b4b566318a files_ng: fix regression in finding the right path in certain situations and add a info panel with attach and zrl bbcode 2020-12-16 16:57:56 +00:00
Mario
27df896a9c files_ng: implement lockview 2020-12-16 08:26:19 +00:00
Mario
8188a551f3 fix regression in files aclselect 2020-12-15 12:12:38 +00:00
Mario
f13bff2a76 fix issue with abconfig when cloning a channel 2020-12-15 11:29:45 +00:00
Mario
59f1c038fe use refresh_all in mod import 2020-12-15 10:04:22 +00:00
Mario
006a409eb8 owa: dismiss deleted hublocs 2020-12-15 08:40:02 +00:00
Mario
dec4ceabb5 try to fix more hubloc anomalies: use refresh_all in remove_obsolete_hublocs() and use Libzot::encode_locations() instead of zot_encode_locations() in notifier 2020-12-15 08:36:14 +00:00
Mario
4a902dbbbe fix file sync issues 2020-12-14 22:06:47 +00:00
Mario
8e488e2913 handle removal of terms in attach_delete() when deleting a ressource 2020-12-14 21:56:19 +00:00
Mario
e58e27ce22 sync categories 2020-12-14 21:38:49 +00:00
Mario
78c0926a64 improve when to show link to parent path 2020-12-14 20:22:13 +00:00
Mario
4693069a06 fix missing hubloc_id_url for zot hubloc when importing a channel 2020-12-14 14:06:07 +00:00
Mario
70e529ef5d add another parent element to wrap the tools 2020-12-14 11:17:36 +00:00
Mario
2a154f8c9a merge branch files_ng into dev 2020-12-14 11:02:20 +00:00
Mario
634ace552d try to prevent hubloc confusion in some places
(cherry picked from commit 61cfeb5bdb749319357912d958cd13304b895bce)
2020-12-12 22:41:38 +01:00
Mario
751a1ba969 make gprobe look for zot6 only
(cherry picked from commit 1d3437aa419adde319d2a3f5d98e7f8fe4418f27)
2020-12-12 22:41:11 +01:00
Mario
badae90051 Merge branch 'dev' into 'dev'
Remove duplicate delete terms query

See merge request hubzilla/core!1885
2020-12-12 19:44:01 +01:00
Max Kostikov
a27c593a2a Remove duplicate delete terms query 2020-12-12 19:43:57 +01:00
Max Kostikov
600dcdfc58 Remove duplicate delete terms query 2020-12-12 10:10:32 +01:00
Max Kostikov
a4b83327c2 Merge branch 'dev' into 'dev'
Dev sync

See merge request kostikov/core!3
2020-12-12 10:07:39 +01:00
Mario
8c1c49a45e fix hubloc issue in mod getfile
(cherry picked from commit b0b9b9f28974f9016e47491e81876224c57a7e3b)
2020-12-11 12:35:54 +01:00
Mario
b1f0014429 Merge branch 'dev' into 'dev'
Skip profile photo query from addons for undefined channel id

See merge request hubzilla/core!1884
2020-12-07 11:08:41 +01:00
Max Kostikov
d984918c75 Skip profile photo query from addons for undefined channel id 2020-12-07 11:08:41 +01:00
Mario
21ac4b5139 remove announce activity from response activites array
(cherry picked from commit 52c78c757ae73082ed29e48707b2313e65020db3)
2020-12-07 11:03:03 +01:00
Max Kostikov
92eb7a0be4 Formatting 2020-12-04 14:45:27 +01:00
Max Kostikov
b26b1c0820 Skip profile photo query from addons for undefined channel id 2020-12-04 14:36:29 +01:00
Max Kostikov
a9dd6d6bdb Merge branch 'dev' into 'dev'
Dev sync

See merge request kostikov/core!2
2020-12-04 14:33:34 +01:00
Mario
9c7ec55b40 changelog 2020-12-01 08:43:59 +00:00
Mario
ce4d664abc Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2020-12-01 08:31:50 +00:00
Mario
45b41f0787 missing parenthesis 2020-12-01 08:31:27 +00:00
Mario
c47434634d Ãfix regerssion in updating the primary 2020-12-01 08:31:15 +00:00
Mario
b89355b9d8 dismiss title in response activity
(cherry picked from commit eb2ad3fc438544192cda95d88a4c2a54ec1e0014)
2020-11-28 11:40:03 +01:00
Mario
3f40d6f7b7 run composer install --no-dev 2020-11-27 11:09:48 +00:00
Mario
155daac8ad run composer install --no-dev 2020-11-27 11:08:45 +00:00
Mario
f9d24d07dd composer autoload dump 2020-11-27 10:50:46 +00:00
Mario
d91e35e197 bring back require but with the correct path 2020-11-27 10:47:24 +00:00
Mario
a967dd9d13 fix languagedetect warnming 2020-11-27 08:04:41 +00:00
Mario
f4bb7bcbff update composer libs 2020-11-27 08:04:00 +00:00
466 changed files with 31517 additions and 12307 deletions

View File

@@ -1,3 +1,63 @@
Hubzilla 5.2 (2021-01-13)
- Use libzotdir for directory
- Streamline usage of channel url for keyId
- Basic work on PHP8 compatibility
- Improve performance for forum post edit action
- Improve File App tiles view
- Update es strings
- Update ru strings
- Implement directory download via zip files in Files App
- Implement bulk file download via zip files in Files App
- Deprecate ! and !! forum tags - use direct messages to post to forums
- Do not show forums where we do not have permission to post in ACL selector
- Implement lockview for Files App
- Implement file and directory rename functionality Files App
- Implement file and directory copy functionality in Files App
- Implement file and directory move functionality in Files App
- Implement categories for files and directories in Files App
- Implement drag and drop move action for files and directories in Files App
- Implement bulk file actions for move, copy, categories and permissions
- Implement a files categories widget
- Implement a breadcrumb view for file paths
- Rewrite xchan_vcard template
- Update composer libs
Bugfixes
- Fix direct messages by forum channel turned into group item
- Fix ID3Parser composer autoload
- Fix issue where directory_fallback_servers were not accessible from static function
- Fix missing constant defenition for HUBLOC_OFFLINE
- Fix sync_directories() including known dead sites
- Fix undefined variable in poller preventing Onedirsync from running
- Fix w2w posts not editable/deletable from local server
- Fix issue where categories were not saved on forum wall posts
- Fix no channel_id provided for contact_remove() in reply_purge()
- Fix typo in notifier command
- Fix profile title/description allowed more than 191 characters
Addons
- Queueworker: cleanup whitespace
- Queueworker: add some tweaks to prevent deadlocks for postgresql
- Flashcards: compatibility for the changes in the Files App
- Openstreemap: fix hostname parsing from URL
- Openstreemap: fix content security policy
- Pubcrawl: fix peertube video display
- Pubcrawl: deliver updates to anyone owning the item
Hubzilla 5.0.8 (2020-12-30)
- Fix single quotes not escaped in the notifications title (issue 1503)
- Return zot6 xchans for random_profile()
- Return zot6 entries in dirsearch
- Fix comment sync issue
- Fix duplicate entries in contact autocomplete
- Fix issue where direct message notifications where not displayed for wall items
- Do not revalidate cached photos
- Implement imagemagic resource consumption limiting
- Specify key in mod owa
- Fix issue where array was passed to get_key()
Hubzilla 5.0.7 (2020-12-21)
- Fix CardDAV addressbook ID
- Use Zot6 for CardDAV and CalDAV sync between clones

27
SBOM.md
View File

@@ -3,27 +3,28 @@
|Name|Version|License|Source|
|----|-------|-------|------|
|blueimp/jquery-file-upload|10.31.0.0|MIT|https://github.com/vkhramtsov/jQuery-File-Upload.git|
|brick/math|0.9.1.0|MIT|https://github.com/brick/math.git|
|bshaffer/oauth2-server-php|1.11.1.0|MIT|https://github.com/bshaffer/oauth2-server-php.git|
|commerceguys/intl|1.0.5.0|MIT|https://github.com/commerceguys/intl.git|
|commerceguys/intl|1.0.7.0|MIT|https://github.com/commerceguys/intl.git|
|desandro/imagesloaded|4.1.4.0|MIT|https://github.com/desandro/imagesloaded.git|
|ezyang/htmlpurifier|4.13.0.0|LGPL-2.1-or-later|https://github.com/ezyang/htmlpurifier.git|
|league/html-to-markdown|4.10.0.0|MIT|https://github.com/thephpleague/html-to-markdown.git|
|lukasreschke/id3parser|0.0.3.0|GPL|https://github.com/LukasReschke/ID3Parser.git|
|michelf/php-markdown|1.9.0.0|BSD-3-Clause|https://github.com/michelf/php-markdown.git|
|paragonie/random_compat|9.99.99.0|MIT|https://github.com/paragonie/random_compat.git|
|pear/text_languagedetect|1.0.0.0|BSD-2-Clause|https://github.com/pear/Text_LanguageDetect.git|
|pear/text_languagedetect|1.0.1.0|BSD-2-Clause|https://github.com/pear/Text_LanguageDetect.git|
|psr/log|1.1.3.0|MIT|https://github.com/php-fig/log.git|
|ramsey/uuid|3.9.3.0|MIT|https://github.com/ramsey/uuid.git|
|sabre/dav|4.1.1.0|BSD-3-Clause|https://github.com/sabre-io/dav.git|
|sabre/event|5.1.0.0|BSD-3-Clause|https://github.com/sabre-io/event.git|
|sabre/http|5.1.0.0|BSD-3-Clause|https://github.com/sabre-io/http.git|
|sabre/uri|2.2.0.0|BSD-3-Clause|https://github.com/sabre-io/uri.git|
|sabre/vobject|4.3.1.0|BSD-3-Clause|https://github.com/sabre-io/vobject.git|
|sabre/xml|2.2.1.0|BSD-3-Clause|https://github.com/sabre-io/xml.git|
|simplepie/simplepie|1.5.5.0|BSD-3-Clause|https://github.com/simplepie/simplepie.git|
|ramsey/collection|1.1.1.0|MIT|https://github.com/ramsey/collection.git|
|ramsey/uuid|4.1.1.0|MIT|https://github.com/ramsey/uuid.git|
|sabre/dav|4.1.3.0|BSD-3-Clause|https://github.com/sabre-io/dav.git|
|sabre/event|5.1.2.0|BSD-3-Clause|https://github.com/sabre-io/event.git|
|sabre/http|5.1.1.0|BSD-3-Clause|https://github.com/sabre-io/http.git|
|sabre/uri|2.2.1.0|BSD-3-Clause|https://github.com/sabre-io/uri.git|
|sabre/vobject|4.3.3.0|BSD-3-Clause|https://github.com/sabre-io/vobject.git|
|sabre/xml|2.2.3.0|BSD-3-Clause|https://github.com/sabre-io/xml.git|
|simplepie/simplepie|1.5.6.0|BSD-3-Clause|https://github.com/simplepie/simplepie.git|
|smarty/smarty|3.1.36.0|LGPL-3.0|https://github.com/smarty-php/smarty.git|
|symfony/polyfill-ctype|1.13.1.0|MIT|https://github.com/symfony/polyfill-ctype.git|
|twbs/bootstrap|4.5.2.0|MIT|https://github.com/twbs/bootstrap.git|
|symfony/polyfill-ctype|1.20.0.0|MIT|https://github.com/symfony/polyfill-ctype.git|
|twbs/bootstrap|4.5.3.0|MIT|https://github.com/twbs/bootstrap.git|
|fullcalendar/fullcalendar|4.4.2.0|MIT|https://github.com/fullcalendar/fullcalendar.git|
|miromannino/Justified-Gallery|3.8.1.0|MIT|https://github.com/miromannino/Justified-Gallery.git|
|fengyuanchen/cropperjs|1.5.7.0|MIT|https://github.com/fengyuanchen/cropperjs.git|

View File

@@ -2,6 +2,8 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Libzotdir;
class Cron_daily {
static public function run($argc,$argv) {
@@ -14,12 +16,11 @@ class Cron_daily {
*/
require_once('include/dir_fns.php');
check_upstream_directory();
Libzotdir::check_upstream_directory();
// Fire off the Cron_weekly process if it's the correct day.
$d3 = intval(datetime_convert('UTC','UTC','now','N'));
if($d3 == 7) {
Master::Summon(array('Cron_weekly'));
@@ -80,15 +81,14 @@ class Cron_daily {
downgrade_accounts();
// If this is a directory server, request a sync with an upstream
// directory at least once a day, up to once every poll interval.
// directory at least once a day, up to once every poll interval.
// Pull remote changes and push local changes.
// potential issue: how do we keep from creating an endless update loop?
// potential issue: how do we keep from creating an endless update loop?
$dirmode = get_config('system','directory_mode');
if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
require_once('include/dir_fns.php');
sync_directories($dirmode);
Libzotdir::sync_directories($dirmode);
}

View File

@@ -466,13 +466,6 @@ class Notifier {
// FIXME add any additional recipients such as mentions, etc.
// don't send deletions onward for other people's stuff
// TODO verify this is needed - copied logic from same place in old code
if(intval($target_item['item_deleted']) && (! intval($target_item['item_wall']))) {
logger('notifier: ignoring delete notification for non-wall item', LOGGER_NORMAL, LOG_NOTICE);
return;
}
}
}

View File

@@ -3,6 +3,7 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Libzotdir;
require_once('include/zot.php');
require_once('include/dir_fns.php');
@@ -74,7 +75,7 @@ class Onedirsync {
if($h && ! in_array($h['hubloc_network'], ['zot6', 'zot']))
return;
update_directory_entry($r[0]);
Libzotdir::update_directory_entry($r[0]);
return;
}

View File

@@ -55,7 +55,6 @@ class Poller {
$force = true;
}
$sql_extra = (($manual_id) ? " AND abook_id = " . intval($manual_id) . " " : "");
reload_plugins();
@@ -86,7 +85,6 @@ class Poller {
);
if($contacts) {
foreach($contacts as $contact) {
$update = false;
@@ -174,6 +172,8 @@ class Poller {
}
}
$dirmode = intval(get_config('system', 'directory_mode'));
if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
$r = q("SELECT u.ud_addr, u.ud_id, u.ud_last FROM updates AS u INNER JOIN (SELECT ud_addr, max(ud_id) AS ud_id FROM updates WHERE ( ud_flags & %d ) = 0 AND ud_addr != '' AND ( ud_last <= '%s' OR ud_last > %s - INTERVAL %s ) GROUP BY ud_addr) AS s ON s.ud_id = u.ud_id ",
intval(UPDATE_FLAGS_UPDATED),

View File

@@ -49,7 +49,7 @@ class ActivityStreams {
if($this->data) {
// verify and unpack JSalmon signature if present
if(is_array($this->data) && array_key_exists('signed',$this->data)) {
$ret = JSalmon::verify($this->data);
$tmp = JSalmon::unpack($this->data['data']);
@@ -103,7 +103,7 @@ class ActivityStreams {
}
// fetch recursive or embedded activities
if ($this->obj && is_array($this->obj) && array_key_exists('object',$this->obj)) {
$this->obj['object'] = $this->get_compound_property($this->obj['object']);
}
@@ -115,10 +115,10 @@ class ActivityStreams {
$this->parent_id = $this->get_property_obj('inReplyTo');
if((! $this->parent_id) && is_array($this->obj)) {
if((! $this->parent_id) && is_array($this->obj)) {
$this->parent_id = $this->obj['inReplyTo'];
}
if((! $this->parent_id) && is_array($this->obj)) {
if((! $this->parent_id) && is_array($this->obj)) {
$this->parent_id = $this->obj['id'];
}
}
@@ -286,7 +286,7 @@ class ActivityStreams {
if (! $s) {
return false;
}
return (in_array($s, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact' ]));
return (in_array($s, [ 'Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact' ]));
}
/**
@@ -302,7 +302,7 @@ class ActivityStreams {
$x = $this->get_property_obj($property, $base, $namespace);
if($this->is_url($x)) {
// SECURITY: If we have already stored the actor profile, re-generate it
// SECURITY: If we have already stored the actor profile, re-generate it
// from cached data - don't refetch it from the network
$r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' limit 1",
@@ -344,7 +344,7 @@ class ActivityStreams {
}
// verify and unpack JSalmon signature if present
if(is_array($x) && array_key_exists('signed',$x)) {
$ret = JSalmon::verify($x);
$tmp = JSalmon::unpack($x['data']);

View File

@@ -64,7 +64,7 @@ class Enotify {
$sitename = get_config('system','sitename');
$site_admin = sprintf( t('%s Administrator'), $sitename);
$opt_out1 = sprintf( t('This email was sent by %1$s at %2$s.'), t('$Projectname'), \App::get_hostname());
$opt_out2 = sprintf( t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings');
$opt_out2 = sprintf( t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings');
$hopt_out2 = sprintf( t('To stop receiving these messages, please adjust your %s.'), '<a href="' . z_root() . '/settings' . '">' . t('Notification Settings') . '</a>');
$sender_name = $product;
$hostname = \App::get_hostname();
@@ -80,7 +80,7 @@ class Enotify {
$sender_email = get_config('system','from_email');
if(! $sender_email)
$sender_email = 'Administrator' . '@' . $hostname;
$sender_name = get_config('system','from_email_name');
if(! $sender_name)
$sender_name = \Zotlabs\Lib\System::get_site_name();
@@ -108,7 +108,7 @@ class Enotify {
logger('notification invoked for an old item which may have been refetched.',LOGGER_DEBUG,LOG_INFO);
return;
}
}
}
else {
$title = $body = '';
}
@@ -181,7 +181,7 @@ class Enotify {
pop_lang();
return;
}
// if it's a post figure out who's post it is.
@@ -219,7 +219,7 @@ class Enotify {
$itemlink,
$p[0]['author']['xchan_name'],
$item_post_type);
// "your post"
if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall']))
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
@@ -230,15 +230,15 @@ class Enotify {
// Some mail softwares relies on subject field for threading.
// So, we cannot have different subjects for notifications of the same thread.
// Before this we have the name of the replier on the subject rendering
// Before this we have the name of the replier on the subject rendering
// differents subjects for messages on the same thread.
if($moderated)
$subject = sprintf( t('[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
else
$subject = sprintf( t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
$preamble = sprintf( t('%1$s commented on an item/conversation you have been following.'), $sender['xchan_name']);
$epreamble = $dest_str;
$preamble = sprintf( t('%1$s commented on an item/conversation you have been following.'), $sender['xchan_name']);
$epreamble = $dest_str;
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
$tsitelink = sprintf( $sitelink, $siteurl );
@@ -247,7 +247,7 @@ class Enotify {
$tsitelink .= "\n\n" . sprintf( t('Please visit %s to approve or reject this comment.'), z_root() . '/moderate' );
$hsitelink .= "<br><br>" . sprintf( t('Please visit %s to approve or reject this comment.'), '<a href="' . z_root() . '/moderate">' . z_root() . '/moderate</a>' );
}
}
if ($params['type'] == NOTIFY_LIKE) {
@@ -278,7 +278,7 @@ class Enotify {
pop_lang();
return;
}
// if it's a post figure out who's post it is.
@@ -314,12 +314,12 @@ class Enotify {
// Some mail softwares relies on subject field for threading.
// So, we cannot have different subjects for notifications of the same thread.
// Before this we have the name of the replier on the subject rendering
// Before this we have the name of the replier on the subject rendering
// differents subjects for messages on the same thread.
$subject = sprintf( t('[$Projectname:Notify] Like received to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
$preamble = sprintf( t('%1$s liked an item/conversation you created.'), $sender['xchan_name']);
$epreamble = $dest_str;
$preamble = sprintf( t('%1$s liked an item/conversation you created.'), $sender['xchan_name']);
$epreamble = $dest_str;
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
$tsitelink = sprintf( $sitelink, $siteurl );
@@ -335,7 +335,7 @@ class Enotify {
$epreamble = sprintf( t('%1$s posted to [zrl=%2$s]your wall[/zrl]') ,
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$params['link']);
$params['link']);
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
$tsitelink = sprintf( $sitelink, $siteurl );
@@ -355,12 +355,12 @@ class Enotify {
pop_lang();
return;
}
$subject = sprintf( t('[$Projectname:Notify] %s tagged you') , $sender['xchan_name']);
$preamble = sprintf( t('%1$s tagged you at %2$s') , $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s [zrl=%2$s]tagged you[/zrl].') ,
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$params['link']);
$params['link']);
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
$tsitelink = sprintf( $sitelink, $siteurl );
@@ -373,7 +373,7 @@ class Enotify {
$preamble = sprintf( t('%1$s poked you at %2$s') , $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s [zrl=%2$s]poked you[/zrl].') ,
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$params['link']);
$params['link']);
$subject = str_replace('poked', t($params['activity']), $subject);
$preamble = str_replace('poked', t($params['activity']), $preamble);
@@ -390,7 +390,7 @@ class Enotify {
$preamble = sprintf( t('%1$s tagged your post at %2$s'),$sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s tagged [zrl=%2$s]your post[/zrl]') ,
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$itemlink);
$itemlink);
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
$tsitelink = sprintf( $sitelink, $siteurl );
@@ -400,10 +400,10 @@ class Enotify {
if ($params['type'] == NOTIFY_INTRO) {
$subject = sprintf( t('[$Projectname:Notify] Introduction received'));
$preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
$preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'),
$siteurl . '/connections/ifpending',
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
$body = sprintf( t('You may visit their profile at %s'),$sender['xchan_url']);
$sitelink = t('Please visit %s to approve or reject the connection request.');
@@ -414,11 +414,11 @@ class Enotify {
if ($params['type'] == NOTIFY_SUGGEST) {
$subject = sprintf( t('[$Projectname:Notify] Friend suggestion received'));
$preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
$preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s.'),
$itemlink,
'[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]',
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
$body = t('Name:') . ' ' . $params['item']['name'] . "\n";
$body .= t('Photo:') . ' ' . $params['item']['photo'] . "\n";
@@ -462,7 +462,7 @@ class Enotify {
$sitelink = $h['sitelink'];
$tsitelink = $h['tsitelink'];
$hsitelink = $h['hsitelink'];
$itemlink = $h['itemlink'];
$itemlink = $h['itemlink'];
require_once('include/html2bbcode.php');
@@ -510,7 +510,7 @@ class Enotify {
// Mark some notifications as seen right away
// Note! The notification have to be created, because they are used to send emails
// So easiest solution to hide them from Notices is to mark them as seen right away.
// Another option would be to not add them to the DB, and change how emails are handled
// Another option would be to not add them to the DB, and change how emails are handled
// (probably would be better that way)
if (!$always_show_in_notices) {
@@ -555,7 +555,7 @@ class Enotify {
// wretched hack, but we don't want to duplicate all the preamble variations and we also don't want to screw up a translation
if ((\App::$language === 'en' || (! \App::$language)) && strpos($msg,', '))
$msg = substr($msg,strpos($msg,', ')+1);
$msg = substr($msg,strpos($msg,', ')+1);
$datarray['id'] = $notify_id;
$datarray['msg'] = $msg;
@@ -587,12 +587,12 @@ class Enotify {
$htmlversion = bbcode(stripslashes(str_replace(array("\\r","\\n"), array("","<br />\n"),$body)));
// use $_SESSION['zid_override'] to force zid() to use
// use $_SESSION['zid_override'] to force zid() to use
// the recipient address instead of the current observer
$_SESSION['zid_override'] = channel_reddress($recip);
$_SESSION['zrl_override'] = z_root() . '/channel/' . $recip['channel_address'];
$textversion = zidify_links($textversion);
$htmlversion = zidify_links($htmlversion);
@@ -754,7 +754,7 @@ class Enotify {
return $params['result'];
}
$fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8');
$fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8');
$messageSubject = email_header_encode(html_entity_decode($params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8');
// generate a mime boundary
@@ -767,8 +767,8 @@ class Enotify {
$messageHeader =
$params['additionalMailHeader'] .
"From: $fromName <{$params['fromEmail']}>" . PHP_EOL .
"Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL .
"MIME-Version: 1.0" . PHP_EOL .
"Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL .
"MIME-Version: 1.0" . PHP_EOL .
"Content-Type: multipart/alternative; boundary=\"{$mimeBoundary}\"";
// assemble the final multipart message body with the text and html types included
@@ -782,7 +782,7 @@ class Enotify {
$textBody . PHP_EOL .
"--" . $mimeBoundary . PHP_EOL . // text/html section
"Content-Type: text/html; charset=UTF-8" . PHP_EOL .
"Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL .
"Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL .
$htmlBody . PHP_EOL .
"--" . $mimeBoundary . "--" . PHP_EOL; // message ending
@@ -803,7 +803,7 @@ class Enotify {
require_once('include/conversation.php');
// Call localize_item to get a one line status for activities.
// Call localize_item to get a one line status for activities.
// This should set $item['localized'] to indicate we have a brief summary.
// and perhaps $item['shortlocalized'] for an even briefer summary
@@ -860,7 +860,7 @@ class Enotify {
//'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])),
'thread_top' => (($item['item_thread_top']) ? true : false),
'message' => bbcode(escape_tags($itemem_text)),
'body' => htmlentities(html2plain(bbcode($item['body']), 75, true), ENT_COMPAT, 'UTF-8', false),
'body' => htmlentities(html2plain(bbcode($item['body']), 75, true), ENT_QUOTES, 'UTF-8', false),
// these are for the superblock addon
'hash' => $item[$who]['xchan_hash'],
'uid' => $item['uid'],

View File

@@ -40,15 +40,15 @@ class JSalmon {
$ret = [ 'results' => [] ];
if(! is_array($x)) {
return $false;
return false;
}
if(! ( array_key_exists('signed',$x) && $x['signed'])) {
return $false;
return false;
}
$signed_data = preg_replace('/\s+/','',$x['data']) . '.'
. base64url_encode($x['data_type'],true) . '.'
. base64url_encode($x['encoding'],true) . '.'
$signed_data = preg_replace('/\s+/','',$x['data']) . '.'
. base64url_encode($x['data_type'],true) . '.'
. base64url_encode($x['encoding'],true) . '.'
. base64url_encode($x['alg'],true);
$key = HTTPSig::get_key(EMPTY_STR,'zot6',base64url_decode($x['sigs']['key_id']));

View File

@@ -713,7 +713,7 @@ class Libsync {
if($arr['locations']) {
if($absolute)
self::check_location_move($sender['hash'],$arr['locations']);
Libzot::check_location_move($sender['hash'],$arr['locations']);
$xisting = q("select * from hubloc where hubloc_hash = '%s'",
dbesc($sender['hash'])

View File

@@ -421,7 +421,7 @@ class Libzot {
if($new_connection) {
if(! Permissions::PermsCompare($new_perms,$previous_perms))
Master::Summon([ 'Notifier', 'permissions_create', $new_connection[0]['abook_id'] ]);
Master::Summon([ 'Notifier', 'permission_create', $new_connection[0]['abook_id'] ]);
Enotify::submit(
[
'type' => NOTIFY_INTRO,

View File

@@ -19,7 +19,6 @@ class Libzotdir {
*/
static function find_upstream_directory($dirmode) {
global $DIRECTORY_FALLBACK_SERVERS;
$preferred = get_config('system','directory_server');
@@ -31,7 +30,7 @@ class Libzotdir {
);
if(($r) && ($r[0]['site_flags'] & DIRECTORY_MODE_STANDALONE)) {
$preferred = '';
}
}
}
@@ -42,19 +41,21 @@ class Libzotdir {
* from our list of directory servers. However, if we're a directory
* server ourself, point at the local instance
* We will then set this value so this should only ever happen once.
* Ideally there will be an admin setting to change to a different
* Ideally there will be an admin setting to change to a different
* directory server if you don't like our choice or if circumstances change.
*/
$directory_fallback_servers = get_directory_fallback_servers();
$dirmode = intval(get_config('system','directory_mode'));
if ($dirmode == DIRECTORY_MODE_NORMAL) {
$toss = mt_rand(0,count($DIRECTORY_FALLBACK_SERVERS));
$preferred = $DIRECTORY_FALLBACK_SERVERS[$toss];
$toss = mt_rand(0,count($directory_fallback_servers));
$preferred = $directory_fallback_servers[$toss];
if(! $preferred) {
$preferred = DIRECTORY_FALLBACK_MASTER;
}
set_config('system','directory_server',$preferred);
}
}
else {
set_config('system','directory_server',z_root());
}
@@ -108,7 +109,7 @@ class Libzotdir {
$ret = get_config('directory', $setting);
// 'safemode' is the default if there is no observer or no established preference.
// 'safemode' is the default if there is no observer or no established preference.
if($setting === 'safemode' && $ret === false)
$ret = 1;
@@ -175,8 +176,8 @@ class Libzotdir {
*
* Checks the directory mode of this hub to see if it is some form of directory server. If it is,
* get the directory realm of this hub. Fetch a list of all other directory servers in this realm and request
* a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB.
* In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored
* a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB.
* In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored
* directly if the rater's signature matches.
*
* @param int $dirmode;
@@ -188,16 +189,17 @@ class Libzotdir {
return;
$realm = get_directory_realm();
if ($realm == DIRECTORY_REALM) {
$r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_type = %d and ( site_realm = '%s' or site_realm = '') ",
$r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_type = %d and ( site_realm = '%s' or site_realm = '') and site_dead = 0",
intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY),
dbesc(z_root()),
intval(SITE_TYPE_ZOT),
dbesc($realm)
);
}
}
else {
$r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_realm like '%s' and site_type = %d ",
$r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_realm like '%s' and site_type = %d and site_dead = 0",
intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY),
dbesc(z_root()),
dbesc(protect_sprintf('%' . $realm . '%')),
@@ -205,6 +207,8 @@ class Libzotdir {
);
}
// If there are no directory servers, setup the fallback master
/** @FIXME What to do if we're in a different realm? */
@@ -214,14 +218,14 @@ class Libzotdir {
[
'site_url' => DIRECTORY_FALLBACK_MASTER,
'site_flags' => DIRECTORY_MODE_PRIMARY,
'site_update' => NULL_DATE,
'site_update' => NULL_DATE,
'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch',
'site_realm' => DIRECTORY_REALM,
'site_valid' => 1,
]
);
$r = q("select * from site where site_flags in (%d, %d) and site_url != '%s' and site_type = %d ",
$r = q("select * from site where site_flags in (%d, %d) and site_url != '%s' and site_type = %d and site_dead = 0",
intval(DIRECTORY_MODE_PRIMARY),
intval(DIRECTORY_MODE_SECONDARY),
dbesc(z_root()),
@@ -245,7 +249,6 @@ class Libzotdir {
$syncdate = (($rr['site_sync'] <= NULL_DATE) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
$x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate) . (($token) ? '&t=' . $token : ''));
if (! $x['success'])
continue;
@@ -273,7 +276,7 @@ class Libzotdir {
$ud_flags |= UPDATE_FLAGS_DELETED;
if (is_array($t['flags']) && in_array('forced',$t['flags']))
$ud_flags |= UPDATE_FLAGS_FORCED;
$z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr )
values ( '%s', '%s', '%s', %d, '%s' ) ",
dbesc($t['hash']),
@@ -338,7 +341,7 @@ class Libzotdir {
static function local_dir_update($uid, $force) {
logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG);
$p = q("select channel.channel_hash, channel_address, channel_timezone, channel_portable_id, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1",
@@ -354,7 +357,7 @@ class Libzotdir {
$profile['description'] = $p[0]['pdesc'];
$profile['birthday'] = $p[0]['dob'];
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
$profile['age'] = $age;
$profile['gender'] = $p[0]['gender'];
@@ -415,7 +418,7 @@ class Libzotdir {
dbesc($legacy_hash)
);
}
}
$ud_hash = random_string() . '@' . \App::get_hostname();
@@ -446,7 +449,7 @@ class Libzotdir {
$arr['xprof_hash'] = $hash;
$arr['xprof_dob'] = (($profile['birthday'] === '0000-00-00') ? $profile['birthday'] : datetime_convert('','',$profile['birthday'],'Y-m-d')); // !!!! check this for 0000 year
$arr['xprof_age'] = (($profile['age']) ? intval($profile['age']) : 0);
$arr['xprof_desc'] = (($profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT,'UTF-8',false) : '');
$arr['xprof_desc'] = (($profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT,'UTF-8',false) : '');
$arr['xprof_gender'] = (($profile['gender']) ? htmlspecialchars($profile['gender'], ENT_COMPAT,'UTF-8',false) : '');
$arr['xprof_marital'] = (($profile['marital']) ? htmlspecialchars($profile['marital'], ENT_COMPAT,'UTF-8',false) : '');
$arr['xprof_sexual'] = (($profile['sexual']) ? htmlspecialchars($profile['sexual'], ENT_COMPAT,'UTF-8',false) : '');
@@ -641,7 +644,7 @@ class Libzotdir {
dbesc(datetime_convert()),
intval($flags),
dbesc($addr)
);
);
}
else {
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ",

View File

@@ -9,7 +9,7 @@ define ( 'NWIKI_ITEM_RESOURCE_TYPE', 'nwiki' );
class NativeWiki {
static public function listwikis($channel, $observer_hash) {
public static function listwikis($channel, $observer_hash) {
$sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash);
$wikis = q("SELECT * FROM item
@@ -40,7 +40,7 @@ class NativeWiki {
}
function create_wiki($channel, $observer_hash, $wiki, $acl) {
public static function create_wiki($channel, $observer_hash, $wiki, $acl) {
$resource_id = new_uuid();
$uuid = new_uuid();
@@ -101,7 +101,7 @@ class NativeWiki {
}
}
function update_wiki($channel_id, $observer_hash, $arr, $acl) {
public static function update_wiki($channel_id, $observer_hash, $arr, $acl) {
$w = self::get_wiki($channel_id, $observer_hash, $arr['resource_id']);
$item = $w['wiki'];
@@ -156,7 +156,7 @@ class NativeWiki {
}
}
static public function sync_a_wiki_item($uid,$id,$resource_id) {
public static function sync_a_wiki_item($uid,$id,$resource_id) {
$r = q("SELECT * from item WHERE uid = %d AND ( id = %d OR ( resource_type = '%s' and resource_id = '%s' )) ",
@@ -185,7 +185,7 @@ class NativeWiki {
}
}
function delete_wiki($channel_id,$observer_hash,$resource_id) {
public static function delete_wiki($channel_id,$observer_hash,$resource_id) {
$w = self::get_wiki($channel_id,$observer_hash,$resource_id);
$item = $w['wiki'];
@@ -202,7 +202,7 @@ class NativeWiki {
}
static public function get_wiki($channel_id, $observer_hash, $resource_id) {
public static function get_wiki($channel_id, $observer_hash, $resource_id) {
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
@@ -236,7 +236,7 @@ class NativeWiki {
}
static public function exists_by_name($uid, $urlName) {
public static function exists_by_name($uid, $urlName) {
$sql_extra = item_permissions_sql($uid);
@@ -258,7 +258,7 @@ class NativeWiki {
}
static public function get_permissions($resource_id, $owner_id, $observer_hash) {
public static function get_permissions($resource_id, $owner_id, $observer_hash) {
// TODO: For now, only the owner can edit
$sql_extra = item_permissions_sql($owner_id, $observer_hash);

View File

@@ -2,6 +2,8 @@
namespace Zotlabs\Module;
use Zotlabs\Lib\Libzotdir;
require_once 'include/acl_selectors.php';
require_once 'include/group.php';
@@ -46,20 +48,20 @@ class Acl extends \Zotlabs\Web\Controller {
// 'a' => autocomplete connections (mod_connections, mod_poke, mod_sources, mod_photos)
// 'x' => nav search bar autocomplete (match any xchan)
// $_REQUEST['query'] contains autocomplete search text.
// List of channels whose connections to also suggest,
// List of channels whose connections to also suggest,
// e.g. currently viewed channel or channels mentioned in a post
$extra_channels = (x($_REQUEST,'extra_channels') ? $_REQUEST['extra_channels'] : array());
// The different autocomplete libraries use different names for the search text
// parameter. Internally we'll use $search to represent the search text no matter
// what request variable it was attached to.
// what request variable it was attached to.
if(array_key_exists('query',$_REQUEST)) {
$search = $_REQUEST['query'];
}
if( (! local_channel()) && (! in_array($type, [ 'x', 'c', 'f' ])))
killme();
@@ -68,7 +70,7 @@ class Acl extends \Zotlabs\Web\Controller {
if(in_array($type, [ 'm', 'a', 'c', 'f' ])) {
// These queries require permission checking. We'll create a simple array of xchan_hash for those with
// the requisite permissions which we can check against.
// the requisite permissions which we can check against.
$x = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = '%s' and v = '1'",
intval(local_channel()),
@@ -85,34 +87,34 @@ class Acl extends \Zotlabs\Web\Controller {
$sql_extra2 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
$sql_extra2_xchan = "AND ( xchan_name LIKE " . protect_sprintf( "'" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
// This horrible mess is needed because position also returns 0 if nothing is found.
// This horrible mess is needed because position also returns 0 if nothing is found.
// Would be MUCH easier if it instead returned a very large value
// Otherwise we could just
// Otherwise we could just
// order by LEAST(POSITION($search IN xchan_name),POSITION($search IN xchan_addr)).
$order_extra2 = "CASE WHEN xchan_name LIKE "
. protect_sprintf( "'%" . dbesc($search) . "%'" )
. " then POSITION('" . protect_sprintf(dbesc($search))
$order_extra2 = "CASE WHEN xchan_name LIKE "
. protect_sprintf( "'%" . dbesc($search) . "%'" )
. " then POSITION('" . protect_sprintf(dbesc($search))
. "' IN xchan_name) else position('" . protect_sprintf(dbesc(punify($search))) . "' IN xchan_addr) end, ";
$sql_extra3 = "AND ( xchan_addr like " . protect_sprintf( "'%" . dbesc(punify($search)) . "%'" ) . " OR xchan_name like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ) ";
}
else {
$sql_extra = $sql_extra2 = $sql_extra3 = "";
}
$groups = array();
$contacts = array();
if($type == '' || $type == 'g') {
// virtual groups based on private profile viewing ability
$r = q("select id, profile_guid, profile_name from profile where is_default = 0 and uid = %d",
intval(local_channel())
);
);
if($r) {
foreach($r as $rv) {
$groups[] = array(
@@ -130,19 +132,19 @@ class Acl extends \Zotlabs\Web\Controller {
// Normal privacy groups
$r = q("SELECT pgrp.id, pgrp.hash, pgrp.gname
FROM pgrp, pgrp_member
WHERE pgrp.deleted = 0 AND pgrp.uid = %d
FROM pgrp, pgrp_member
WHERE pgrp.deleted = 0 AND pgrp.uid = %d
AND pgrp_member.gid = pgrp.id
$sql_extra
GROUP BY pgrp.id
ORDER BY pgrp.gname
ORDER BY pgrp.gname
LIMIT %d OFFSET %d",
intval(local_channel()),
intval($count),
intval($start)
);
if($r) {
if($r) {
foreach($r as $g){
// logger('acl: group: ' . $g['gname'] . ' members: ' . group_get_members_xchan($g['id']));
$groups[] = array(
@@ -157,10 +159,10 @@ class Acl extends \Zotlabs\Web\Controller {
}
}
}
if($type == '' || $type == 'c' || $type === 'f') {
$extra_channels_sql = '';
$extra_channels_sql = '';
// Only include channels who allow the observer to view their connections
if($extra_channels) {
@@ -172,7 +174,7 @@ class Acl extends \Zotlabs\Web\Controller {
}
}
}
// Getting info from the abook is better for local users because it contains info about permissions
if(local_channel()) {
if($extra_channels_sql != '')
@@ -199,7 +201,7 @@ class Acl extends \Zotlabs\Web\Controller {
$r2 = array();
foreach($r1 as $rr) {
$x = atoken_xchan($rr);
$r2[] = [
$r2[] = [
'id' => 'a' . $rr['atoken_id'] ,
'hash' => $x['xchan_hash'],
'name' => $x['xchan_name'],
@@ -211,12 +213,12 @@ class Acl extends \Zotlabs\Web\Controller {
'abook_self' => 0
];
}
}
}
// add connections
$r = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, xchan_pubforum, abook_flags, abook_self
FROM abook left join xchan on abook_xchan = xchan_hash
$r = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, xchan_pubforum, abook_flags, abook_self
FROM abook left join xchan on abook_xchan = xchan_hash
WHERE (abook_channel = %d $extra_channels_sql) AND abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc" ,
intval(local_channel())
);
@@ -225,28 +227,28 @@ class Acl extends \Zotlabs\Web\Controller {
}
else { // Visitors
$r = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
$r = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
FROM xchan left join xlink on xlink_link = xchan_hash
WHERE xlink_xchan = '%s' AND xchan_deleted = 0 $sql_extra2_xchan order by $order_extra2 xchan_name asc" ,
dbesc(get_observer_hash())
);
// Find contacts of extra channels
// This is probably more complicated than it needs to be
if($extra_channels_sql) {
// Build a list of hashes that we got previously so we don't get them again
$known_hashes = array("'".get_observer_hash()."'");
if($r)
foreach($r as $rr)
foreach($r as $rr)
$known_hashes[] = "'".$rr['hash']."'";
$known_hashes_sql = 'AND xchan_hash not in ('.join(',',$known_hashes).')';
$r2 = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, abook_flags, abook_self
FROM abook left join xchan on abook_xchan = xchan_hash
$r2 = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, abook_flags, abook_self
FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel IN ($extra_channels_sql) $known_hashes_sql AND abook_blocked = 0 and abook_pending = 0 and abook_hidden = 0 and xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc");
if($r2)
$r = array_merge($r,$r2);
// Sort accoring to match position, then alphabetically. This could be avoided if the above two SQL queries could be combined into one, and the sorting could be done on the SQl server (like in the case of a local user)
$matchpos = function($x) use($search) {
$namepos = strpos($x['name'],$search);
@@ -269,22 +271,22 @@ class Acl extends \Zotlabs\Web\Controller {
}
}
if((count($r) < 100) && $type == 'c') {
$r2 = q("SELECT substr(xchan_hash,1,18) as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
FROM xchan
$r2 = q("SELECT substr(xchan_hash,1,18) as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
FROM xchan
WHERE xchan_deleted = 0 and not xchan_network in ('rss','anon','unknown') $sql_extra2_xchan order by $order_extra2 xchan_name asc"
);
if($r2) {
$r = array_merge($r,$r2);
$r = unique_multidim_array($r,'hash');
}
}
}
}
elseif($type == 'm') {
$r = array();
$z = q("SELECT xchan_hash as hash, xchan_name as name, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url
$z = q("SELECT xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url
FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d
WHERE abook_channel = %d
and xchan_deleted = 0
and xchan_network IN ('zot', 'diaspora', 'friendica-over-diaspora')
$sql_extra3
@@ -298,18 +300,18 @@ class Acl extends \Zotlabs\Web\Controller {
}
}
}
}
elseif($type == 'a') {
$r = q("SELECT abook_id as id, xchan_name as name, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_network as network, xchan_url as url, xchan_addr as attag , abook_their_perms FROM abook left join xchan on abook_xchan = xchan_hash
$r = q("SELECT abook_id as id, xchan_name as name, xchan_network as net, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url, xchan_addr as attag , abook_their_perms FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d
and xchan_deleted = 0
$sql_extra3
ORDER BY xchan_name ASC ",
intval(local_channel())
);
}
elseif($type == 'x') {
$r = $this->navbar_complete($a);
@@ -323,7 +325,7 @@ class Acl extends \Zotlabs\Web\Controller {
);
}
}
$o = array(
'start' => $start,
'count' => $count,
@@ -334,27 +336,34 @@ class Acl extends \Zotlabs\Web\Controller {
}
else
$r = array();
if($r) {
$i = count($contacts);
$x = [];
foreach($r as $g) {
if(in_array($g['network'],['rss','anon','unknown']) && ($type != 'a'))
if(in_array($g['net'],['rss','anon','unknown']) && ($type != 'a'))
continue;
$g['hash'] = urlencode($g['hash']);
if(! $g['nick']) {
$g['nick'] = $g['url'];
}
$clink = ($g['nick']) ? $g['nick'] : $g['url'];
$lkey = md5($clink);
if (! array_key_exists($lkey, $x))
$x[$lkey] = $i;
if(in_array($g['hash'],$permitted) && $type === 'f' && (! $noforums)) {
$contacts[] = array(
$contacts[$i] = array(
"type" => "c",
"photo" => "images/twopeople.png",
"name" => $g['name'],
"id" => urlencode($g['id']),
"xid" => $g['hash'],
"link" => (($g['nick']) ? $g['nick'] : $g['url']),
"link" => $clink,
"nick" => substr($g['nick'],0,strpos($g['nick'],'@')),
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
"taggable" => 'taggable',
@@ -362,24 +371,28 @@ class Acl extends \Zotlabs\Web\Controller {
);
}
if($type !== 'f') {
$contacts[] = array(
"type" => "c",
"photo" => $g['micro'],
"name" => $g['name'],
"id" => urlencode($g['id']),
"xid" => $g['hash'],
"link" => (($g['nick']) ? $g['nick'] : $g['url']),
"nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']),
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
"taggable" => '',
"label" => '',
);
if (! array_key_exists($x[$lkey], $contacts) || ($contacts[$x[$lkey]]['net'] !== 'zot6' && ($g['net'] == 'zot6' || $g['net'] == 'zot'))) {
$contacts[$x[$lkey]] = array(
"type" => "c",
"photo" => $g['micro'],
"name" => $g['name'],
"id" => urlencode($g['id']),
"xid" => $g['hash'],
"link" => $clink,
"nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']),
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
"taggable" => '',
"label" => '',
"net" => $g['net']
);
}
}
}
$i++;
}
}
$items = array_merge($groups, $contacts);
$o = array(
'start' => $start,
'count' => $count,
@@ -393,50 +406,50 @@ class Acl extends \Zotlabs\Web\Controller {
function navbar_complete(&$a) {
// logger('navbar_complete');
if(observer_prohibited()) {
return;
}
$dirmode = intval(get_config('system','directory_mode'));
$search = ((x($_REQUEST,'search')) ? htmlentities($_REQUEST['search'],ENT_COMPAT,'UTF-8',false) : '');
if(! $search || mb_strlen($search) < 2)
return array();
$star = false;
$address = false;
if(substr($search,0,1) === '@')
$search = substr($search,1);
if(substr($search,0,1) === '*') {
$star = true;
$search = substr($search,1);
}
if(strpos($search,'@') !== false) {
$address = true;
}
if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) {
$url = z_root() . '/dirsearch';
}
if(! $url) {
require_once("include/dir_fns.php");
$directory = find_upstream_directory($dirmode);
$directory = Libzotdir::find_upstream_directory($dirmode);
$url = $directory['url'] . '/dirsearch';
}
$token = get_config('system','realm_token');
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100);
if($url) {
$query = $url . '?f=' . (($token) ? '&t=' . urlencode($token) : '');
$query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode(punify($search)) : '');
$x = z_fetch_url($query);
if($x['success']) {
$t = 0;

View File

@@ -1,61 +1,187 @@
<?php
namespace Zotlabs\Module;
use ZipArchive;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Verify;
require_once('include/security.php');
require_once('include/attach.php');
class Attach extends Controller {
class Attach extends \Zotlabs\Web\Controller {
function post() {
$attach_ids = ((x($_REQUEST, 'attach_ids')) ? $_REQUEST['attach_ids'] : []);
$attach_path = ((x($_REQUEST, 'attach_path')) ? $_REQUEST['attach_path'] : '');
$channel_id = ((x($_REQUEST, 'channel_id')) ? intval($_REQUEST['channel_id']) : 0);
$channel = channelx_by_n($channel_id);
if (! $channel) {
notice(t('Channel not found.') . EOL);
return;
}
$strip_str = '/cloud/' . $channel['channel_address'] . '/';
$count = strlen($strip_str);
$attach_path = substr($attach_path, $count);
if ($attach_ids) {
$zip_dir = 'store/[data]/' . $channel['channel_address'] . '/tmp';
if (! is_dir($zip_dir))
mkdir($zip_dir, STORAGE_DEFAULT_PERMISSIONS, true);
$token = random_string(32);
$zip_file = 'download_' . $token . '.zip';
$zip_path = $zip_dir . '/' . $zip_file;
$zip = new ZipArchive();
if ($zip->open($zip_path, ZipArchive::CREATE) === true) {
$zip_filename = self::zip_archive_handler($zip, $attach_ids, $attach_path);
$zip->close();
$meta = [
'zip_filename' => $zip_filename,
'zip_path' => $zip_path
];
Verify::create('zip_token', 0, $token, json_encode($meta));
json_return_and_die([
'success' => true,
'token' => $token
]);
}
}
}
function get() {
function init() {
if(argc() < 2) {
notice( t('Item not available.') . EOL);
return;
}
$token = ((x($_REQUEST, 'token')) ? $_REQUEST['token'] : '');
if(argv(1) === 'download') {
$meta = Verify::get_meta('zip_token', 0, $token);
if(! $meta)
killme();
$meta = json_decode($meta, true);
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="'. $meta['zip_filename'] . '"');
header('Content-Length: ' . filesize($meta['zip_path']));
$istream = fopen($meta['zip_path'], 'rb');
$ostream = fopen('php://output', 'wb');
if($istream && $ostream) {
pipe_streams($istream, $ostream);
fclose($istream);
fclose($ostream);
}
unlink($meta['zip_path']);
killme();
}
$r = attach_by_hash(argv(1),get_observer_hash(),((argc() > 2) ? intval(argv(2)) : 0));
if(! $r['success']) {
notice( $r['message'] . EOL);
return;
}
$c = q("select channel_address from channel where channel_id = %d limit 1",
intval($r['data']['uid'])
);
if(! $c)
return;
$unsafe_types = array('text/html','text/css','application/javascript');
if(in_array($r['data']['filetype'],$unsafe_types) && (! channel_codeallowed($r['data']['uid']))) {
header('Content-type: text/plain');
header('Content-Type: text/plain');
}
else {
header('Content-type: ' . $r['data']['filetype']);
header('Content-Type: ' . $r['data']['filetype']);
}
header('Content-disposition: attachment; filename="' . $r['data']['filename'] . '"');
header('Content-Disposition: attachment; filename="' . $r['data']['filename'] . '"');
if(intval($r['data']['os_storage'])) {
$fname = dbunescbin($r['data']['content']);
$fname = $r['data']['content'];
if(strpos($fname,'store') !== false)
$istream = fopen($fname,'rb');
else
$istream = fopen('store/' . $c[0]['channel_address'] . '/' . $fname,'rb');
$ostream = fopen('php://output','wb');
if($istream && $ostream) {
pipe_streams($istream,$ostream);
pipe_streams($istream, $ostream);
fclose($istream);
fclose($ostream);
}
}
else
echo dbunescbin($r['data']['content']);
echo $r['data']['content'];
killme();
}
public function zip_archive_handler($zip, $attach_ids, $attach_path, $pass = 1) {
$observer_hash = get_observer_hash();
$single = ((count($attach_ids) == 1) ? true : false);
$download_name = 'download.zip';
foreach($attach_ids as $attach_id) {
$r = attach_by_id($attach_id, $observer_hash);
if (! $r['success']) {
continue;
}
if ($r['data']['is_dir'] && $single && $pass === 1)
$download_name = $r['data']['filename'] . '.zip';
$zip_path = $r['data']['display_path'];
if ($attach_path) {
$strip_str = $attach_path . '/';
$count = strlen($strip_str);
$zip_path = substr($r['data']['display_path'], $count);
}
if ($r['data']['is_dir']) {
$zip->addEmptyDir($zip_path);
$d = q("SELECT id FROM attach WHERE folder = '%s'",
dbesc($r['data']['hash'])
);
$attach_ids = ids_to_array($d);
self::zip_archive_handler($zip, $attach_ids, $attach_path, $pass++);
}
else {
$file_path = $r['data']['content'];
$zip->addFile($file_path, $zip_path);
// compressing can be ressource intensive - just store the data
$zip->setCompressionName($zip_path, ZipArchive::CM_STORE);
}
}
return $download_name;
}
}

View File

@@ -0,0 +1,203 @@
<?php
namespace Zotlabs\Module;
/**
* @file Zotlabs/Module/Attach_edit.php
*
*/
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Libsync;
use Zotlabs\Access\AccessList;
class Attach_edit extends Controller {
function post() {
if (!local_channel() && !remote_channel()) {
return;
}
$attach_ids = ((x($_POST, 'attach_ids')) ? $_POST['attach_ids'] : []);
$attach_id = ((x($_POST, 'attach_id')) ? intval($_POST['attach_id']) : 0);
$channel_id = ((x($_POST, 'channel_id')) ? intval($_POST['channel_id']) : 0);
$dnd = ((x($_POST, 'dnd')) ? intval($_POST['dnd']) : 0);
$permissions = ((x($_POST, 'permissions')) ? intval($_POST['permissions']) : 0);
$return_path = ((x($_POST, 'return_path')) ? notags($_POST['return_path']) : 'cloud');
$delete = ((x($_POST, 'delete')) ? intval($_POST['delete']) : 0);
$newfolder = ((x($_POST, 'newfolder_' . $attach_id)) ? notags($_POST['newfolder_' . $attach_id]) : '');
if(! $newfolder)
$newfolder = ((x($_POST, 'newfolder')) ? notags($_POST['newfolder']) : '');
$newfilename = ((x($_POST, 'newfilename_' . $attach_id)) ? notags($_POST['newfilename_' . $attach_id]) : '');
$recurse = ((x($_POST, 'recurse_' . $attach_id)) ? intval($_POST['recurse_' . $attach_id]) : 0);
if(! $recurse)
$recurse = ((x($_POST, 'recurse')) ? intval($_POST['recurse']) : 0);
$notify = ((x($_POST, 'notify_edit_' . $attach_id)) ? intval($_POST['notify_edit_' . $attach_id]) : 0);
$copy = ((x($_POST, 'copy_' . $attach_id)) ? intval($_POST['copy_' . $attach_id]) : 0);
if(! $copy)
$copy = ((x($_POST, 'copy')) ? intval($_POST['copy']) : 0);
$categories = ((x($_POST, 'categories_' . $attach_id)) ? notags($_POST['categories_' . $attach_id]) : '');
if(! $categories)
$categories = ((x($_POST, 'categories')) ? notags($_POST['categories']) : '');
if($attach_id)
$attach_ids[] = $attach_id;
$single = ((count($attach_ids) === 1) ? true : false);
$channel = channelx_by_n($channel_id);
if (! $channel) {
notice(t('Channel not found.') . EOL);
return;
}
$nick = $channel['channel_address'];
$observer = App::get_observer();
$observer_hash = (($observer) ? $observer['xchan_hash'] : '');
$is_owner = ((local_channel() == $channel_id) ? true : false);
$ids_str = implode(',', $attach_ids);
$r = q("SELECT id, uid, hash, creator, folder, filename, is_photo, is_dir FROM attach WHERE id IN ( %s ) AND uid = %d",
dbesc($ids_str),
intval($channel_id)
);
if (! $r) {
notice(t('File not found.') . EOL);
return;
}
foreach ($r as $rr) {
$actions_done = '';
$attach_id = $rr['id'];
$resource = $rr['hash'];
$creator = $rr['creator'];
$folder = $rr['folder'];
$filename = $rr['filename'];
$is_photo = intval($rr['is_photo']);
$is_dir = intval($rr['is_dir']);
$admin_delete = false;
$is_creator = (($creator == $observer_hash) ? true : false);
$move = ((! $copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false);
$perms = get_all_perms($channel_id, $observer_hash);
if (! ($perms['view_storage'] || is_site_admin())) {
notice( t('Permission denied.') . EOL);
continue;
}
if (! $perms['write_storage']) {
if (is_site_admin()) {
$admin_delete = true;
}
else {
notice( t('Permission denied.') . EOL);
continue;
}
}
if (!$is_owner && !$admin_delete) {
if(! $is_creator) {
notice( t('Permission denied.') . EOL);
continue;
}
}
if ($delete) {
attach_delete($channel_id, $resource, $is_photo);
$actions_done .= 'delete,';
}
if ($copy) {
if($is_dir && $resource == $newfolder) {
notice( t('Can not copy folder into itself.') . EOL);
continue;
}
$x = attach_copy($channel_id, $resource, $newfolder, (($single) ? $newfilename : ''));
if ($x['success'])
$resource = $x['resource_id'];
$actions_done .= 'copy,';
}
if ($move) {
if($is_dir && $resource == $newfolder) {
notice( sprintf(t('Can not move folder "%s" into itself.'), $filename) . EOL);
continue;
}
$x = attach_move($channel_id, $resource, $newfolder, (($single) ? $newfilename : ''));
$actions_done .= 'move,';
}
if(! $delete && ! $dnd) {
if ($single || (! $single && $categories)) {
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel_id),
intval($attach_id),
intval(TERM_OBJ_FILE)
);
$cat = explode(',', $categories);
if ($cat) {
foreach($cat as $term) {
$term = trim(escape_tags($term));
if ($term) {
$term_link = z_root() . '/cloud/' . $nick . '/?cat=' . $term;
store_item_tag($channel_id, $attach_id, TERM_OBJ_FILE, TERM_CATEGORY, $term, $term_link);
}
}
$actions_done .= 'cat_add,';
}
}
else {
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel_id),
intval($attach_id),
intval(TERM_OBJ_FILE)
);
$actions_done .= 'cat_remove,';
}
if ($is_owner && ($single || (! $single && $permissions))) {
$acl = new AccessList($channel);
$acl->set_from_array($_REQUEST);
$x = $acl->get();
attach_change_permissions($channel_id, $resource, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], $recurse, true);
$actions_done .= 'permissions,';
if ($notify) {
attach_store_item($channel, $observer, $resource);
$actions_done .= 'notify,';
}
}
}
if (! $admin_delete && $actions_done) {
$sync = attach_export_data($channel, $resource, (($delete) ? true : false));
if ($sync) {
Libsync::build_sync_packet($channel_id, ['file' => [$sync]]);
}
}
logger('attach_edit: ' . $actions_done);
}
if($dnd || $delete) {
json_return_and_die([ 'success' => true ]);
}
goaway($return_path);
}
}

View File

@@ -50,7 +50,7 @@ class Cdav extends Controller {
if($sigblock) {
$keyId = str_replace('acct:','',$sigblock['keyId']);
if($keyId) {
$r = q("select * from hubloc where hubloc_addr = '%s'",
$r = q("select * from hubloc where hubloc_id_url = '%s'",
dbesc($keyId)
);
if($r) {

View File

@@ -8,7 +8,11 @@ namespace Zotlabs\Module;
*/
use Sabre\DAV as SDAV;
use \Zotlabs\Storage;
use \Zotlabs\Web\Controller;
use \Zotlabs\Storage\BasicAuth;
use \Zotlabs\Storage\Directory;
use \Zotlabs\Storage\Browser;
// composer autoloader for SabreDAV
require_once('vendor/autoload.php');
@@ -20,7 +24,7 @@ require_once('include/attach.php');
* @brief Cloud Module.
*
*/
class Cloud extends \Zotlabs\Web\Controller {
class Cloud extends Controller {
/**
* @brief Fires up the SabreDAV server.
@@ -42,7 +46,7 @@ class Cloud extends \Zotlabs\Web\Controller {
$auth = new \Zotlabs\Storage\BasicAuth();
$auth = new BasicAuth();
$ob_hash = get_observer_hash();
@@ -72,7 +76,7 @@ class Cloud extends \Zotlabs\Web\Controller {
if($x !== \App::$query_string)
goaway(z_root() . '/' . $x);
$rootDirectory = new \Zotlabs\Storage\Directory('/', $auth);
$rootDirectory = new Directory('/', [], $auth);
// A SabreDAV server-object
$server = new SDAV\Server($rootDirectory);
@@ -85,7 +89,7 @@ class Cloud extends \Zotlabs\Web\Controller {
$is_readable = false;
// provide a directory view for the cloud in Hubzilla
$browser = new \Zotlabs\Storage\Browser($auth);
$browser = new Browser($auth);
$auth->setBrowserPlugin($browser);
$server->addPlugin($browser);
@@ -105,13 +109,13 @@ class Cloud extends \Zotlabs\Web\Controller {
if($browser->build_page)
construct_page();
killme();
}
function DAVException($err) {
if($err instanceof \Sabre\DAV\Exception\NotFound) {
notice( t('Not found') . EOL);
}
@@ -126,7 +130,7 @@ class Cloud extends \Zotlabs\Web\Controller {
}
construct_page();
killme();
}

View File

@@ -51,7 +51,7 @@ class Dav extends \Zotlabs\Web\Controller {
if($sigblock) {
$keyId = str_replace('acct:','',$sigblock['keyId']);
if($keyId) {
$r = q("select * from hubloc where hubloc_addr = '%s'",
$r = q("select * from hubloc where hubloc_id_url = '%s'",
dbesc($keyId)
);
if($r) {
@@ -100,7 +100,7 @@ class Dav extends \Zotlabs\Web\Controller {
$auth->setRealm(ucfirst(\Zotlabs\Lib\System::get_platform_name()) . ' ' . 'WebDAV');
$rootDirectory = new \Zotlabs\Storage\Directory('/', $auth);
$rootDirectory = new \Zotlabs\Storage\Directory('/', [], $auth);
// A SabreDAV server-object
$server = new SDAV\Server($rootDirectory);

View File

@@ -4,6 +4,8 @@ namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Libzotdir;
require_once('include/socgraph.php');
require_once('include/dir_fns.php');
@@ -15,7 +17,7 @@ class Directory extends Controller {
function init() {
App::set_pager_itemspage(30);
if(local_channel() && x($_GET,'ignore')) {
q("insert into xign ( uid, xchan ) values ( %d, '%s' ) ",
intval(local_channel()),
@@ -26,12 +28,12 @@ class Directory extends Controller {
if(local_channel())
App::$profile_uid = local_channel();
$observer = get_observer_hash();
$global_changed = false;
$safe_changed = false;
$pubforums_changed = false;
if(array_key_exists('global',$_REQUEST)) {
$globaldir = intval($_REQUEST['global']);
$global_changed = true;
@@ -41,7 +43,7 @@ class Directory extends Controller {
if($observer)
set_xconfig($observer,'directory','globaldir',$globaldir);
}
if(array_key_exists('safe',$_REQUEST)) {
$safemode = intval($_REQUEST['safe']);
$safe_changed = true;
@@ -51,8 +53,8 @@ class Directory extends Controller {
if($observer)
set_xconfig($observer,'directory','safemode',$safemode);
}
if(array_key_exists('pubforums',$_REQUEST)) {
$pubforums = intval($_REQUEST['pubforums']);
$pubforums_changed = true;
@@ -64,52 +66,52 @@ class Directory extends Controller {
}
}
function get() {
if(observer_prohibited()) {
notice( t('Public access denied.') . EOL);
return;
}
if(get_config('system','block_public_directory',false) && (! get_observer_hash())) {
notice( t('Public access denied.') . EOL);
return;
}
$observer = get_observer_hash();
$globaldir = get_directory_setting($observer, 'globaldir');
$globaldir = Libzotdir::get_directory_setting($observer, 'globaldir');
// override your personal global search pref if we're doing a navbar search of the directory
if(intval($_REQUEST['navsearch']))
$globaldir = 1;
$safe_mode = get_directory_setting($observer, 'safemode');
$pubforums = get_directory_setting($observer, 'pubforums');
$safe_mode = Libzotdir::get_directory_setting($observer, 'safemode');
$pubforums = Libzotdir::get_directory_setting($observer, 'pubforums');
$o = '';
nav_set_selected('Directory');
if(x($_POST,'search'))
$search = notags(trim($_POST['search']));
else
$search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : '');
if(strpos($search,'=') && local_channel() && feature_enabled(local_channel(), 'advanced_dirsearch'))
$advanced = $search;
$keywords = (($_GET['keywords']) ? $_GET['keywords'] : '');
// Suggest channels if no search terms or keywords are given
$suggest = (local_channel() && x($_REQUEST,'suggest')) ? $_REQUEST['suggest'] : '';
if($suggest) {
// the directory options have no effect in suggestion mode
$globaldir = 1;
$safe_mode = 1;
$type = 0;
@@ -120,7 +122,7 @@ class Directory extends Controller {
notice( t('No default suggestions were found.') . EOL);
return;
}
// Remember in which order the suggestions were
$addresses = array();
$common = array();
@@ -129,7 +131,7 @@ class Directory extends Controller {
$common[$rr['xchan_addr']] = ((intval($rr['total']) > 0) ? intval($rr['total']) - 1 : 0);
$addresses[$rr['xchan_addr']] = $index++;
}
// Build query to get info about suggested people
$advanced = '';
foreach(array_keys($addresses) as $address) {
@@ -137,13 +139,13 @@ class Directory extends Controller {
}
// Remove last space in the advanced query
$advanced = rtrim($advanced);
}
$tpl = get_markup_template('directory_header.tpl');
$dirmode = intval(get_config('system','directory_mode'));
$directory_admin = false;
if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) {
@@ -154,19 +156,19 @@ class Directory extends Controller {
}
if(! $url) {
$directory = find_upstream_directory($dirmode);
$directory = Libzotdir::find_upstream_directory($dirmode);
if((! $directory) || (! array_key_exists('url',$directory)) || (! $directory['url']))
logger('CRITICAL: No directory server URL');
$url = $directory['url'] . '/dirsearch';
}
$token = get_config('system','realm_token');
logger('mod_directory: URL = ' . $url, LOGGER_DEBUG);
$contacts = array();
if(local_channel()) {
$x = q("select abook_xchan from abook where abook_channel = %d",
intval(local_channel())
@@ -176,24 +178,24 @@ class Directory extends Controller {
$contacts[] = $xx['abook_xchan'];
}
}
if($url) {
$numtags = get_config('system','directorytags');
$kw = ((intval($numtags) > 0) ? intval($numtags) : 50);
if(get_config('system','disable_directory_keywords'))
$kw = 0;
$query = $url . '?f=&kw=' . $kw . (($safe_mode != 1) ? '&safe=' . $safe_mode : '');
if($token)
$query .= '&t=' . $token;
if(! $globaldir)
$query .= '&hub=' . App::get_hostname();
if($search)
$query .= '&name=' . urlencode($search) . '&keywords=' . urlencode($search);
if(strpos($search,'@'))
@@ -204,29 +206,29 @@ class Directory extends Controller {
$query .= '&query=' . urlencode($advanced);
if(! is_null($pubforums))
$query .= '&pubforums=' . intval($pubforums);
$directory_sort_order = get_config('system','directory_sort_order');
if(! $directory_sort_order)
$directory_sort_order = 'date';
$sort_order = ((x($_REQUEST,'order')) ? $_REQUEST['order'] : $directory_sort_order);
if($sort_order)
$query .= '&order=' . urlencode($sort_order);
if(App::$pager['page'] != 1)
$query .= '&p=' . App::$pager['page'];
logger('mod_directory: query: ' . $query);
$x = z_fetch_url($query);
logger('directory: return from upstream: ' . print_r($x,true), LOGGER_DATA);
if($x['success']) {
$t = 0;
$j = json_decode($x['body'],true);
if($j) {
if($j['results']) {
$results = $j['results'];
@@ -235,23 +237,23 @@ class Directory extends Controller {
}
$entries = array();
$photo = 'thumb';
foreach($results as $rr) {
$profile_link = chanlink_url($rr['url']);
$pdesc = (($rr['description']) ? $rr['description'] . '<br />' : '');
$connect_link = ((local_channel()) ? z_root() . '/follow?f=&url=' . urlencode($rr['address']) : '');
$connect_link = ((local_channel()) ? z_root() . '/follow?f=&url=' . urlencode($rr['address']) : '');
// Checking status is disabled ATM until someone checks the performance impact more carefully
//$online = remote_online_status($rr['address']);
$online = '';
if(in_array($rr['hash'],$contacts))
$connect_link = '';
$location = '';
if(strlen($rr['locale']))
$location .= $rr['locale'];
@@ -265,53 +267,53 @@ class Directory extends Controller {
$location .= ', ';
$location .= $rr['country'];
}
$age = '';
if(strlen($rr['birthday'])) {
if(($years = age($rr['birthday'],'UTC','')) > 0)
$age = $years;
}
$page_type = '';
$rating_enabled = get_config('system','rating_enabled');
if($rr['total_ratings'] && $rating_enabled)
$total_ratings = sprintf( tt("%d rating", "%d ratings", $rr['total_ratings']), $rr['total_ratings']);
else
$total_ratings = '';
$profile = $rr;
if ((x($profile,'locale') == 1)
|| (x($profile,'region') == 1)
|| (x($profile,'postcode') == 1)
|| (x($profile,'country') == 1))
$gender = ((x($profile,'gender') == 1) ? t('Gender: ') . $profile['gender']: False);
$marital = ((x($profile,'marital') == 1) ? t('Status: ') . $profile['marital']: False);
$homepage = ((x($profile,'homepage') == 1) ? t('Homepage: ') : False);
$homepageurl = ((x($profile,'homepage') == 1) ? html2plain($profile['homepage']) : '');
$homepageurl = ((x($profile,'homepage') == 1) ? html2plain($profile['homepage']) : '');
$hometown = ((x($profile,'hometown') == 1) ? html2plain($profile['hometown']) : False);
$about = ((x($profile,'about') == 1) ? zidify_links(bbcode($profile['about'], ['tryoembed' => false])) : False);
if ($about && $safe_mode) {
$about = html2plain($about);
}
$keywords = ((x($profile,'keywords')) ? $profile['keywords'] : '');
$out = '';
if($keywords) {
$keywords = str_replace(',',' ', $keywords);
$keywords = str_replace(' ',' ', $keywords);
$karr = explode(' ', $keywords);
if($karr) {
if(local_channel()) {
$r = q("select keywords from profile where uid = %d and is_default = 1 limit 1",
@@ -332,9 +334,9 @@ class Directory extends Controller {
$out .= '<a href="' . z_root() . '/directory/f=&keywords=' . urlencode($k) .'">' . $k . '</a>';
}
}
}
$entry = array(
'id' => ++$t,
'profile_link' => $profile_link,
@@ -366,7 +368,7 @@ class Directory extends Controller {
'about' => $about,
'about_label' => t('About:'),
'conn_label' => t('Connect'),
'forum_label' => t('Public Forum:'),
'forum_label' => t('Public Forum:'),
'connect' => $connect_link,
'online' => $online,
'kw' => (($out) ? t('Keywords: ') : ''),
@@ -378,36 +380,36 @@ class Directory extends Controller {
'common_count' => intval($common[$rr['address']]),
'safe' => $safe_mode
);
$arr = array('contact' => $rr, 'entry' => $entry);
call_hooks('directory_item', $arr);
unset($profile);
unset($location);
if(! $arr['entry']) {
continue;
}
}
if($sort_order == '' && $suggest) {
$entries[$addresses[$rr['address']]] = $arr['entry']; // Use the same indexes as originally to get the best suggestion first
}
else {
$entries[] = $arr['entry'];
}
}
ksort($entries); // Sort array by key so that foreach-constructs work as expected
if($j['keywords']) {
App::$data['directory_keywords'] = $j['keywords'];
}
logger('mod_directory: entries: ' . print_r($entries,true), LOGGER_DATA);
if($_REQUEST['aj']) {
if($entries) {
$o = replace_macros(get_markup_template('directajax.tpl'),array(
@@ -422,9 +424,9 @@ class Directory extends Controller {
}
else {
$maxheight = 94;
$dirtitle = (($globaldir) ? t('Global Directory') : t('Local Directory'));
$o .= "<script> var page_query = '" . escape_tags(urlencode($_GET['q'])) . "'; var extra_args = '" . extra_query_args() . "' ; divmore_height = " . intval($maxheight) . "; </script>";
$o .= replace_macros($tpl, array(
'$search' => $search,
@@ -442,10 +444,10 @@ class Directory extends Controller {
'$reversedate' => t('Oldest to Newest'),
'$suggest' => $suggest ? '&suggest=1' : ''
));
}
}
else {
if($_REQUEST['aj']) {
@@ -463,7 +465,7 @@ class Directory extends Controller {
}
return $o;
}
static public function reorder_results($results,$suggests) {
if(! $suggests)

View File

@@ -192,7 +192,7 @@ class Dirsearch extends Controller {
else {
$qlimit = " LIMIT " . intval($perpage) . " OFFSET " . intval($startrec);
if($return_total) {
$r = q("SELECT COUNT(xchan_hash) AS total FROM xchan left join xprof on xchan_hash = xprof_hash where $logic $sql_extra and xchan_network = 'zot' and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 $safesql ");
$r = q("SELECT COUNT(xchan_hash) AS total FROM xchan left join xprof on xchan_hash = xprof_hash where $logic $sql_extra and xchan_network = 'zot6' and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 $safesql ");
if($r) {
$ret['total_items'] = $r[0]['total'];
}
@@ -261,7 +261,7 @@ class Dirsearch extends Controller {
else {
$r = q("SELECT xchan.*, xprof.* from xchan left join xprof on xchan_hash = xprof_hash
where ( $logic $sql_extra ) $hub_query and xchan_network = 'zot' and xchan_system = 0 and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0
where ( $logic $sql_extra ) $hub_query and xchan_network = 'zot6' and xchan_system = 0 and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0
$safesql $order $qlimit "
);

View File

@@ -11,17 +11,16 @@ require_once('include/photos.php');
class File_upload extends \Zotlabs\Web\Controller {
function post() {
logger('file upload: ' . print_r($_REQUEST,true));
logger('file upload: ' . print_r($_FILES,true));
$channel = (($_REQUEST['channick']) ? channelx_by_nick($_REQUEST['channick']) : null);
if(! $channel) {
logger('channel not found');
killme();
}
$_REQUEST['source'] = 'file_upload';
if($channel['channel_id'] != local_channel()) {
@@ -40,13 +39,11 @@ class File_upload extends \Zotlabs\Web\Controller {
$r = attach_mkdir($channel, get_observer_hash(), $_REQUEST);
if($r['success']) {
$hash = $r['data']['hash'];
$sync = attach_export_data($channel,$hash);
if($sync) {
Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync)));
}
goaway(z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']);
goaway(z_root() . '/' . $_REQUEST['return_url']);
}
}
else {
@@ -54,8 +51,6 @@ class File_upload extends \Zotlabs\Web\Controller {
$matches = [];
$partial = false;
if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) {
$pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches);
if($pm) {
@@ -69,7 +64,7 @@ class File_upload extends \Zotlabs\Web\Controller {
if($x['partial']) {
header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
json_return_and_die($result);
json_return_and_die($x);
}
else {
header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0));
@@ -83,7 +78,7 @@ class File_upload extends \Zotlabs\Web\Controller {
];
}
}
else {
else {
if(! array_key_exists('userfile',$_FILES)) {
$_FILES['userfile'] = [
'name' => $_FILES['files']['name'],
@@ -103,8 +98,9 @@ class File_upload extends \Zotlabs\Web\Controller {
}
}
goaway(z_root() . '/' . $_REQUEST['return_url']);
}
}

View File

@@ -11,6 +11,9 @@ class Filestorage extends \Zotlabs\Web\Controller {
function post() {
notice( t('Deprecated!') . EOL);
return;
$channel_id = ((x($_POST, 'uid')) ? intval($_POST['uid']) : 0);
if((! $channel_id) || (! local_channel()) || ($channel_id != local_channel())) {
@@ -47,6 +50,9 @@ class Filestorage extends \Zotlabs\Web\Controller {
function get() {
notice( t('Deprecated!') . EOL);
return;
if(argc() > 1)
$which = argv(1);
else {
@@ -88,7 +94,7 @@ class Filestorage extends \Zotlabs\Web\Controller {
}
else {
notice( t('Permission denied.') . EOL);
if($json_return)
if($json_return)
json_return_and_die([ 'success' => false ]);
return;
}
@@ -102,24 +108,23 @@ class Filestorage extends \Zotlabs\Web\Controller {
if(! $r) {
notice( t('File not found.') . EOL);
if($json_return)
if($json_return)
json_return_and_die([ 'success' => false ]);
goaway(z_root() . '/cloud/' . $which);
}
if(local_channel() !== $owner) {
if((local_channel() !== $owner) && !$admin_delete) {
if($r[0]['creator'] && $r[0]['creator'] !== $ob_hash) {
notice( t('Permission denied.') . EOL);
if($json_return)
if($json_return)
json_return_and_die([ 'success' => false ]);
goaway(z_root() . '/cloud/' . $which);
}
}
$f = $r[0];
$channel = channelx_by_n($owner);
@@ -138,7 +143,7 @@ class Filestorage extends \Zotlabs\Web\Controller {
if($json_return)
json_return_and_die([ 'success' => true ]);
goaway(dirname($url));
//goaway(dirname($url));
}

View File

@@ -6,20 +6,20 @@ use Zotlabs\Lib\Libzot;
/**
* module: getfile
*
*
* used for synchronising files and photos across clones
*
*
* The site initiating the file operation will send a sync packet to known clones.
* They will respond by building the DB structures they require, then will provide a
* post request to this site to grab the file data. This is sent as a stream direct to
* disk at the other end, avoiding memory issues.
*
* Since magic-auth cannot easily be used by the CURL process at the other end,
* we will require a signed request which includes a timestamp. This should not be
* used without SSL and is potentially vulnerable to replay if an attacker decrypts
* we will require a signed request which includes a timestamp. This should not be
* used without SSL and is potentially vulnerable to replay if an attacker decrypts
* the SSL traffic fast enough. The amount of time slop is configurable but defaults
* to 3 minutes.
*
*
*/
@@ -54,13 +54,13 @@ class Getfile extends \Zotlabs\Web\Controller {
$keyId = $sigblock['keyId'];
if($keyId) {
$r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash
where hubloc_addr = '%s'",
$r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash
where hubloc_id_url = '%s'",
dbesc(str_replace('acct:','',$keyId))
);
if($r) {
$hubloc = Libzot::zot_record_preferred($r);
$verified = HTTPSig::verify('',$hubloc['xchan_pubkey']);
$verified = HTTPSig::verify('',$hubloc['xchan_pubkey']);
if($verified && $verified['header_signed'] && $verified['header_valid'] && $hash == $hubloc['hubloc_hash']) {
$header_verified = true;
}
@@ -74,15 +74,15 @@ class Getfile extends \Zotlabs\Web\Controller {
logger('post: ' . print_r($_POST,true),LOGGER_DEBUG,LOG_INFO);
if($header_verified) {
logger('HTTPSig verified');
}
}
$channel = channelx_by_hash($hash);
if((! $channel) || (! $time) || (! $sig)) {
logger('error: missing info');
killme();
}
if(isset($_POST['resolution']))
$resolution = intval($_POST['resolution']);
elseif(substr($resource,-2,1) == '-') {
@@ -91,21 +91,21 @@ class Getfile extends \Zotlabs\Web\Controller {
}
else {
$resolution = (-1);
}
}
$slop = intval(get_pconfig($channel['channel_id'],'system','getfile_time_slop'));
if($slop < 1)
$slop = 3;
$d1 = datetime_convert('UTC','UTC',"now + $slop minutes");
$d2 = datetime_convert('UTC','UTC',"now - $slop minutes");
$d2 = datetime_convert('UTC','UTC',"now - $slop minutes");
if(! $header_verified) {
if(($time > $d1) || ($time < $d2)) {
logger('time outside allowable range');
killme();
}
if(! rsa_verify($hash . '.' . $time,base64url_decode($sig),$channel['channel_pubkey'])) {
logger('verify failed.');
killme();
@@ -137,20 +137,20 @@ class Getfile extends \Zotlabs\Web\Controller {
else {
echo dbunescbin($r[0]['content']);
}
}
}
killme();
}
$r = attach_by_hash($resource,$channel['channel_hash'],$revision);
if(! $r['success']) {
logger('attach_by_hash failed: ' . $r['message']);
notice( $r['message'] . EOL);
return;
}
$unsafe_types = array('text/html','text/css','application/javascript');
if(in_array($r['data']['filetype'],$unsafe_types) && (! channel_codeallowed($channel['channel_id']))) {
header('Content-type: text/plain');
}

View File

@@ -585,11 +585,6 @@ class Import extends \Zotlabs\Web\Controller {
if(array_key_exists('item_id',$data) && $data['item_id'])
import_item_ids($channel,$data['item_id']);
// send out refresh requests
// notify old server that it may no longer be primary.
\Zotlabs\Daemon\Master::Summon(array('Notifier','refresh_all',$channel['channel_id']));
// This will indirectly perform a refresh_all *and* update the directory
\Zotlabs\Daemon\Master::Summon(array('Directory', $channel['channel_id']));

View File

@@ -976,7 +976,7 @@ class Item extends Controller {
$item_unseen = ((local_channel() != $profile_uid) ? 1 : 0);
$item_wall = (($post_type === 'wall' || $post_type === 'wall-comment') ? 1 : 0);
$item_wall = (($_REQUEST['type'] === 'wall' || $_REQUEST['type'] === 'wall-comment') ? 1 : 0);
$item_origin = (($origin) ? 1 : 0);
$item_consensus = (($consensus) ? 1 : 0);
$item_nocomment = (($nocomment) ? 1 : 0);
@@ -1337,7 +1337,7 @@ class Item extends Controller {
killme();
}
if(($parent == $post_id) || ($datarray['item_private'] == 1)) {
if($parent || $datarray['item_private'] == 1) {
$r = q("select * from item where id = %d",
intval($post_id)
);

View File

@@ -21,7 +21,7 @@ class Like extends \Zotlabs\Web\Controller {
'abstain' => ACTIVITY_ABSTAIN ,
'attendyes' => ACTIVITY_ATTEND ,
'attendno' => ACTIVITY_ATTENDNO ,
'attendmaybe' => ACTIVITY_ATTENDMAYBE
'attendmaybe' => ACTIVITY_ATTENDMAYBE
];
// unlike (etc.) reactions are an undo of positive reactions, rather than a negative action.
@@ -81,7 +81,7 @@ class Like extends \Zotlabs\Web\Controller {
}
public function get() {
$o = EMPTY_STR;
$sys_channel = get_sys_channel();
@@ -92,7 +92,7 @@ class Like extends \Zotlabs\Web\Controller {
if((! $observer) || ($interactive)) {
$o .= '<h1>' . t('Like/Dislike') . '</h1>';
$o .= EOL . EOL;
if(! $observer) {
$_SESSION['return_url'] = \App::$query_string;
$o .= t('This action is restricted to members.') . EOL;
@@ -100,17 +100,17 @@ class Like extends \Zotlabs\Web\Controller {
return $o;
}
}
$verb = notags(trim($_GET['verb']));
$mode = (($_GET['conv_mode'] === 'channel') ? 'channel' : 'network');
if(! $verb)
$verb = 'like';
$activity = $this->reaction_to_activity($verb);
if(! $activity) {
return EMPTY_STR;
return EMPTY_STR;
}
$is_rsvp = false;
@@ -123,23 +123,23 @@ class Like extends \Zotlabs\Web\Controller {
$object = $target = null;
$post_type = EMPTY_STR;
$objtype = EMPTY_STR;
if(argc() == 3) {
if(! $observer)
killme();
$extended_like = true;
$obj_type = argv(1);
$obj_id = argv(2);
$public = true;
if($obj_type == 'profile') {
$r = q("select * from profile where profile_guid = '%s' limit 1",
dbesc(argv(2))
);
if(! $r)
killme();
killme();
$owner_uid = $r[0]['uid'];
if($r[0]['is_default'])
$public = true;
@@ -165,54 +165,54 @@ class Like extends \Zotlabs\Web\Controller {
}
$post_type = t('channel');
$objtype = ACTIVITY_OBJ_PROFILE;
$profile = $r[0];
}
elseif($obj_type == 'thing') {
$r = q("select * from obj where obj_type = %d and obj_obj = '%s' limit 1",
intval(TERM_OBJ_THING),
dbesc(argv(2))
);
if(! $r) {
if($interactive) {
notice( t('Invalid request.') . EOL);
return $o;
}
killme();
killme();
}
$owner_uid = $r[0]['obj_channel'];
$allow_cid = $r[0]['allow_cid'];
$allow_gid = $r[0]['allow_gid'];
$deny_cid = $r[0]['deny_cid'];
$deny_gid = $r[0]['deny_gid'];
if($allow_cid || $allow_gid || $deny_cid || $deny_gid)
if($allow_cid || $allow_gid || $deny_cid || $deny_gid)
$public = false;
$post_type = t('thing');
$objtype = ACTIVITY_OBJ_PROFILE;
$tgttype = ACTIVITY_OBJ_THING;
$links = array();
$links[] = array('rel' => 'alternate', 'type' => 'text/html',
'href' => z_root() . '/thing/' . $r[0]['obj_obj']);
if($r[0]['imgurl'])
if($r[0]['imgurl'])
$links[] = array('rel' => 'photo', 'href' => $r[0]['obj_imgurl']);
$target = json_encode(array(
'type' => $tgttype,
'title' => $r[0]['obj_term'],
'id' => z_root() . '/thing/' . $r[0]['obj_obj'],
'link' => $links
));
$plink = '[zrl=' . z_root() . '/thing/' . $r[0]['obj_obj'] . ']' . $r[0]['obj_term'] . '[/zrl]';
}
if(! ($owner_uid && $r)) {
if($interactive) {
notice( t('Invalid request.') . EOL);
@@ -220,11 +220,11 @@ class Like extends \Zotlabs\Web\Controller {
}
killme();
}
// The resultant activity is going to be a wall-to-wall post, so make sure this is allowed
$perms = get_all_perms($owner_uid,$observer['xchan_hash']);
if(! ($perms['post_like'] && $perms['view_profile'])) {
if($interactive) {
notice( t('Permission denied.') . EOL);
@@ -232,7 +232,7 @@ class Like extends \Zotlabs\Web\Controller {
}
killme();
}
$ch = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1",
intval($owner_uid)
);
@@ -243,14 +243,14 @@ class Like extends \Zotlabs\Web\Controller {
}
killme();
}
if(! $plink)
$plink = '[zrl=' . z_root() . '/profile/' . $ch[0]['channel_address'] . ']' . $post_type . '[/zrl]';
$object = json_encode(Activity::fetch_profile([ 'id' => channel_url($ch[0]) ]));
// second like of the same thing is "undo" for the first like
$z = q("select * from likes where channel_id = %d and liker = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' limit 1",
intval($ch[0]['channel_id']),
dbesc($observer['xchan_hash']),
@@ -258,11 +258,11 @@ class Like extends \Zotlabs\Web\Controller {
dbesc(($tgttype)?$tgttype:$objtype),
dbesc($obj_id)
);
if($z) {
$z[0]['deleted'] = 1;
Libsync::build_sync_packet($ch[0]['channel_id'],array('likes' => $z));
q("delete from likes where id = %d",
intval($z[0]['id'])
);
@@ -285,17 +285,17 @@ class Like extends \Zotlabs\Web\Controller {
if(! $observer)
killme();
// this is used to like an item or comment
$item_id = ((argc() == 2) ? notags(trim(argv(1))) : 0);
logger('like: verb ' . $verb . ' item ' . $item_id, LOGGER_DEBUG);
// get the item. Allow linked photos (which are normally hidden) to be liked
$r = q("SELECT * FROM item WHERE id = %d
and item_type in (0,6,7) and item_deleted = 0 and item_unpublished = 0
$r = q("SELECT * FROM item WHERE id = %d
and item_type in (0,6,7) and item_deleted = 0 and item_unpublished = 0
and item_delayed = 0 and item_pending_remove = 0 and item_blocked = 0 LIMIT 1",
intval($item_id)
);
@@ -351,12 +351,12 @@ class Like extends \Zotlabs\Web\Controller {
killme();
$verbs = " '".dbesc($activity)."' ";
$multi_undo = false;
$multi_undo = false;
// event participation and consensus items are essentially radio toggles. If you make a subsequent choice,
// we need to eradicate your first choice.
// we need to eradicate your first choice.
if($activity === ACTIVITY_ATTEND || $activity === ACTIVITY_ATTENDNO || $activity === ACTIVITY_ATTENDMAYBE) {
$verbs = " '" . dbesc(ACTIVITY_ATTEND) . "','" . dbesc(ACTIVITY_ATTENDNO) . "','" . dbesc(ACTIVITY_ATTENDMAYBE) . "' ";
$multi_undo = 1;
@@ -365,16 +365,16 @@ class Like extends \Zotlabs\Web\Controller {
$verbs = " '" . dbesc(ACTIVITY_AGREE) . "','" . dbesc(ACTIVITY_DISAGREE) . "','" . dbesc(ACTIVITY_ABSTAIN) . "' ";
$multi_undo = true;
}
$item_normal = item_normal();
$r = q("SELECT id, parent, uid, verb FROM item WHERE verb in ( $verbs ) $item_normal
AND author_xchan = '%s' AND thr_parent = '%s' and uid = %d ",
dbesc($observer['xchan_hash']),
dbesc($item['mid']),
intval($owner_uid)
);
if($r) {
// already liked it. Drop that item.
require_once('include/items.php');
@@ -386,27 +386,27 @@ class Like extends \Zotlabs\Web\Controller {
intval($rr['parent']),
intval($rr['uid'])
);
// Prior activity was a duplicate of the one we're submitting, just undo it;
// Prior activity was a duplicate of the one we're submitting, just undo it;
// don't fall through and create another
if(activity_match($rr['verb'],$activity))
$multi_undo = false;
// drop_item was not done interactively, so we need to invoke the notifier
// in order to push the changes to connections
\Zotlabs\Daemon\Master::Summon(array('Notifier','drop',$rr['id']));
}
if($interactive)
return;
if(! $multi_undo) {
$ret = self::like_response([
'item' => $item,
'orig_item_id' => $item_id,
'orig_item_id' => $item_id,
'owner_xchan' => $thread_owner,
'conv_mode' => $mode
]);
@@ -416,11 +416,11 @@ class Like extends \Zotlabs\Web\Controller {
}
}
$uuid = item_message_id();
$arr = array();
$arr['uuid'] = $uuid;
$arr['mid'] = z_root() . (($is_rsvp) ? '/activity/' : '/item/') . $uuid;
@@ -433,38 +433,38 @@ class Like extends \Zotlabs\Web\Controller {
$post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status'));
if($item['obj_type'] === ACTIVITY_OBJ_EVENT)
$post_type = t('event');
$links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $item['plink']));
$objtype = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
$objtype = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
if($objtype === ACTIVITY_OBJ_NOTE && (! intval($item['item_thread_top'])))
$objtype = ACTIVITY_OBJ_COMMENT;
$body = $item['body'];
$object = json_encode(Activity::fetch_item( [ 'id' => $item['mid'] ]));
if(! intval($item['item_thread_top']))
$post_type = 'comment';
$post_type = 'comment';
$arr['item_origin'] = 1;
$arr['item_notshown'] = 1;
$arr['item_type'] = $item['item_type'];
if(intval($item['item_wall']))
$arr['item_wall'] = 1;
// if this was a linked photo and was hidden, unhide it.
if(intval($item['item_hidden'])) {
$r = q("update item set item_hidden = 0 where id = %d",
intval($item['id'])
);
}
}
}
if($verb === 'like')
$bodyverb = t('%1$s likes %2$s\'s %3$s');
if($verb === 'dislike')
@@ -481,12 +481,12 @@ class Like extends \Zotlabs\Web\Controller {
$bodyverb = t('%1$s is not attending %2$s\'s %3$s');
if($verb === 'attendmaybe')
$bodyverb = t('%1$s may attend %2$s\'s %3$s');
if(! isset($bodyverb))
killme();
killme();
if($extended_like) {
$ulink = '[zrl=' . $ch[0]['xchan_url'] . '][bdi]' . $ch[0]['xchan_name'] . '[/bdi][/zrl]';
$alink = '[zrl=' . $observer['xchan_url'] . '][bdi]' . $observer['xchan_name'] . '[/bdi][/zrl]';
@@ -503,64 +503,64 @@ class Like extends \Zotlabs\Web\Controller {
$deny_cid = $item['deny_cid'];
$deny_gid = $item['deny_gid'];
$private = $item['private'];
}
$arr['aid'] = (($extended_like) ? $ch[0]['channel_account_id'] : $owner_aid);
$arr['uid'] = $owner_uid;
$arr['item_flags'] = $item_flags;
$arr['item_wall'] = $item_wall;
$arr['item_flags'] = $item['item_flags'];
$arr['item_wall'] = $item['item_wall'];
$arr['parent_mid'] = (($extended_like) ? $arr['mid'] : $item['mid']);
$arr['owner_xchan'] = (($extended_like) ? $ch[0]['xchan_hash'] : $thread_owner['xchan_hash']);
$arr['author_xchan'] = $observer['xchan_hash'];
$arr['body'] = sprintf( $bodyverb, $alink, $ulink, $plink );
if($obj_type === 'thing' && $r[0]['imgurl']) {
$arr['body'] .= "\n\n[zmg=80x80]" . $r[0]['imgurl'] . '[/zmg]';
}
}
if($obj_type === 'profile') {
if($public) {
$arr['body'] .= "\n\n" . '[embed]' . z_root() . '/profile/' . $ch[0]['channel_address'] . '[/embed]';
$arr['body'] .= "\n\n" . '[embed]' . z_root() . '/profile/' . $ch[0]['channel_address'] . '[/embed]';
}
else
$arr['body'] .= "\n\n[zmg=80x80]" . $profile['thumb'] . '[/zmg]';
}
}
$arr['verb'] = $activity;
$arr['obj_type'] = $objtype;
$arr['obj'] = $object;
if($target) {
$arr['tgt_type'] = $tgttype;
$arr['target'] = $target;
}
$arr['allow_cid'] = $allow_cid;
$arr['allow_gid'] = $allow_gid;
$arr['deny_cid'] = $deny_cid;
$arr['deny_gid'] = $deny_gid;
$arr['item_private'] = $private;
call_hooks('post_local',$arr);
$post = item_store($arr);
$post = item_store($arr);
$post_id = $post['item_id'];
// save the conversation from expiration
if(local_channel() && array_key_exists('item',$post) && (intval($post['item']['id']) != intval($post['item']['parent'])))
retain_item($post['item']['parent']);
retain_item($post['item']['parent']);
$arr['id'] = $post_id;
call_hooks('post_local_end', $arr);
if($extended_like) {
$r = q("insert into likes (channel_id,liker,likee,iid,i_mid,verb,target_type,target_id,target) values (%d,'%s','%s',%d,'%s','%s','%s','%s','%s')",
intval($ch[0]['channel_id']),
@@ -582,12 +582,12 @@ class Like extends \Zotlabs\Web\Controller {
dbesc($obj_id)
);
if($r)
Libsync::build_sync_packet($ch[0]['channel_id'],array('likes' => $r));
Libsync::build_sync_packet($ch[0]['channel_id'],array('likes' => $r));
}
\Zotlabs\Daemon\Master::Summon(array('Notifier','like',$post_id));
if($interactive) {
notice( t('Action completed.') . EOL);
$o .= t('Thank you.');
@@ -596,12 +596,12 @@ class Like extends \Zotlabs\Web\Controller {
$ret = self::like_response([
'item' => $item,
'orig_item_id' => $item_id,
'orig_item_id' => $item_id,
'owner_xchan' => $thread_owner,
'conv_mode' => $mode
]);
json_return_and_die($ret);
}
}

View File

@@ -19,22 +19,22 @@ class Lockview extends \Zotlabs\Web\Controller {
}
}
}
$type = ((argc() > 1) ? argv(1) : 0);
if (is_numeric($type)) {
$item_id = intval($type);
$type='item';
}
}
else {
$item_id = ((argc() > 2) ? intval(argv(2)) : 0);
}
if(! $item_id)
killme();
if (! in_array($type, array('item', 'photo', 'attach', 'event', 'menu_item', 'chatroom')))
killme();
// we have different naming in in menu_item table and chatroom table
switch($type) {
case 'menu_item':
@@ -47,17 +47,17 @@ class Lockview extends \Zotlabs\Web\Controller {
$id = 'id';
break;
}
$r = q("SELECT * FROM %s WHERE $id = %d LIMIT 1",
dbesc($type),
intval($item_id)
);
if(! $r)
killme();
$item = $r[0];
//we have different naming in in menu_item table and chatroom table
switch($type) {
case 'menu_item':
@@ -70,37 +70,37 @@ class Lockview extends \Zotlabs\Web\Controller {
$uid = $item['uid'];
break;
}
if($uid != local_channel()) {
echo '<div class="dropdown-item">' . t('Remote privacy information not available.') . '</div>';
killme();
}
if(intval($item['item_private']) && (! strlen($item['allow_cid'])) && (! strlen($item['allow_gid']))
if(intval($item['item_private']) && (! strlen($item['allow_cid'])) && (! strlen($item['allow_gid']))
&& (! strlen($item['deny_cid'])) && (! strlen($item['deny_gid']))) {
// if the post is private, but public_policy is blank ("visible to the internet"), and there aren't any
// specific recipients, we're the recipient of a post with "bcc" or targeted recipients; so we'll just show it
// as unknown specific recipients. The sender will have the visibility list and will fall through to the
// next section.
echo '<div class="dropdown-item">' . translate_scope((! $item['public_policy']) ? 'specific' : $item['public_policy']) . '</div>';
killme();
}
$allowed_users = expand_acl($item['allow_cid']);
$allowed_groups = expand_acl($item['allow_gid']);
$deny_users = expand_acl($item['deny_cid']);
$deny_groups = expand_acl($item['deny_gid']);
$o = '<div class="dropdown-item">' . t('Visible to:') . '</div>';
$l = array();
stringify_array_elms($allowed_groups,true);
stringify_array_elms($allowed_users,true);
stringify_array_elms($deny_groups,true);
stringify_array_elms($deny_users,true);
$profile_groups = [];
if($allowed_groups) {
@@ -113,24 +113,24 @@ class Lockview extends \Zotlabs\Web\Controller {
if(count($profile_groups)) {
$r = q("SELECT profile_name FROM profile WHERE profile_guid IN ( " . implode(', ', $profile_groups) . " )");
if($r)
foreach($r as $rr)
foreach($r as $rr)
$l[] = '<div class="dropdown-item"><b>' . t('Profile','acl') . ' ' . $rr['profile_name'] . '</b></div>';
}
if(count($allowed_groups)) {
$r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $allowed_groups) . " )");
if($r)
foreach($r as $rr)
foreach($r as $rr)
$l[] = '<div class="dropdown-item"><b>' . $rr['gname'] . '</b></div>';
}
if(count($allowed_users)) {
$r = q("SELECT xchan_name FROM xchan WHERE xchan_hash IN ( " . implode(', ',$allowed_users) . " )");
if($r)
foreach($r as $rr)
foreach($r as $rr)
$l[] = '<div class="dropdown-item">' . $rr['xchan_name'] . '</div>';
if($atokens) {
foreach($atokens as $at) {
if(in_array("'" . $at['xchan_hash'] . "'",$allowed_users)) {
if(in_array("'" . $at['xchan_hash'] . "'",$allowed_users)) {
$l[] = '<div class="dropdown-item">' . $at['xchan_name'] . '</div>';
}
}
@@ -149,7 +149,7 @@ class Lockview extends \Zotlabs\Web\Controller {
if(count($profile_groups)) {
$r = q("SELECT profile_name FROM profile WHERE profile_guid IN ( " . implode(', ', $profile_groups) . " )");
if($r)
foreach($r as $rr)
foreach($r as $rr)
$l[] = '<div class="dropdown-item"><b><strike>' . t('Profile','acl') . ' ' . $rr['profile_name'] . '</strike></b></div>';
}
@@ -158,18 +158,18 @@ class Lockview extends \Zotlabs\Web\Controller {
if(count($deny_groups)) {
$r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $deny_groups) . " )");
if($r)
foreach($r as $rr)
foreach($r as $rr)
$l[] = '<div class="dropdown-item"><b><strike>' . $rr['gname'] . '</strike></b></div>';
}
if(count($deny_users)) {
$r = q("SELECT xchan_name FROM xchan WHERE xchan_hash IN ( " . implode(', ', $deny_users) . " )");
if($r)
foreach($r as $rr)
foreach($r as $rr)
$l[] = '<div class="dropdown-item"><strike>' . $rr['xchan_name'] . '</strike></div>';
if($atokens) {
foreach($atokens as $at) {
if(in_array("'" . $at['xchan_hash'] . "'",$deny_users)) {
if(in_array("'" . $at['xchan_hash'] . "'",$deny_users)) {
$l[] = '<div class="dropdown-item"><strike>' . $at['xchan_name'] . '</strike></div>';
}
}
@@ -177,11 +177,11 @@ class Lockview extends \Zotlabs\Web\Controller {
}
echo $o . implode($l);
killme();
}
}

View File

@@ -39,14 +39,14 @@ class Owa extends Controller {
$found = discover_by_webbie(str_replace('acct:','',$keyId));
if ($found) {
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
WHERE OR hubloc_id_url = '%s'",
WHERE hubloc_id_url = '%s'",
dbesc($keyId)
);
}
}
if ($r) {
foreach ($r as $hubloc) {
$verified = HTTPSig::verify(file_get_contents('php://input'));
$verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']);
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
logger('OWA header: ' . print_r($verified,true),LOGGER_DATA);
logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA);

View File

@@ -35,7 +35,7 @@ class Photo extends \Zotlabs\Web\Controller {
call_hooks('cache_mode_hook', $cache_mode);
$observer_xchan = get_observer_hash();
$cachecontrol = '';
$cachecontrol = ', no-cache';
if(isset($type)) {
@@ -81,18 +81,18 @@ class Photo extends \Zotlabs\Web\Controller {
else
$data = dbunescbin($r[0]['content']);
}
}
if(! $data) {
$d = [ 'imgscale' => $resolution, 'channel_id' => $uid, 'default' => $default, 'data' => '', 'mimetype' => '' ];
call_hooks('get_profile_photo',$d);
$resolution = $d['imgscale'];
$uid = $d['channel_id'];
$default = $d['default'];
$data = $d['data'];
$mimetype = $d['mimetype'];
$modified = 0;
if(! $data) {
$d = [ 'imgscale' => $resolution, 'channel_id' => $uid, 'default' => $default, 'data' => '', 'mimetype' => '' ];
call_hooks('get_profile_photo',$d);
$resolution = $d['imgscale'];
$uid = $d['channel_id'];
$default = $d['default'];
$data = $d['data'];
$mimetype = $d['mimetype'];
$modified = 0;
}
}
if(! $data) {
@@ -102,7 +102,7 @@ class Photo extends \Zotlabs\Web\Controller {
$modified = filemtime($default);
}
$cachecontrol = ', must-revalidate';
$cachecontrol .= ', must-revalidate';
}
else {
@@ -169,6 +169,7 @@ class Photo extends \Zotlabs\Web\Controller {
$url = z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($url);
goaway($url);
}
$cachecontrol = '';
}
}
}
@@ -271,7 +272,7 @@ class Photo extends \Zotlabs\Web\Controller {
// in the event that infrastructure caching is present.
$smaxage = intval($maxage/12);
header("Cache-Control: no-cache, s-maxage=" . $smaxage . ", max-age=" . $maxage . $cachecontrol);
header("Cache-Control: s-maxage=" . $smaxage . ", max-age=" . $maxage . $cachecontrol);
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,11 +12,11 @@ class Profiles extends \Zotlabs\Web\Controller {
function init() {
nav_set_selected('Profiles', 'settings/profiles');
if(! local_channel()) {
return;
}
if((argc() > 2) && (argv(1) === "drop") && intval(argv(2))) {
$r = q("SELECT * FROM profile WHERE id = %d AND uid = %d AND is_default = 0 LIMIT 1",
intval(argv(2)),
@@ -28,11 +28,11 @@ class Profiles extends \Zotlabs\Web\Controller {
return; // NOTREACHED
}
$profile_guid = $r['profile_guid'];
check_form_security_token_redirectOnErr('/profiles', 'profile_drop', 't');
// move every contact using this profile as their default to the user default
$r = q("UPDATE abook SET abook_profile = (SELECT profile_guid FROM profile WHERE is_default = 1 AND uid = %d LIMIT 1) WHERE abook_profile = '%s' AND abook_channel = %d ",
intval(local_channel()),
dbesc($profile_guid),
@@ -44,34 +44,34 @@ class Profiles extends \Zotlabs\Web\Controller {
);
if($r)
info( t('Profile deleted.') . EOL);
// @fixme this is a much more complicated sync - add any changed abook entries and
// @fixme this is a much more complicated sync - add any changed abook entries and
// also add deleted flag to profile structure
// profiles_build_sync is just here as a placeholder - it doesn't work at all here
// profiles_build_sync(local_channel());
goaway(z_root() . '/profiles');
return; // NOTREACHED
}
if((argc() > 1) && (argv(1) === 'new')) {
// check_form_security_token_redirectOnErr('/profiles', 'profile_new', 't');
$r0 = q("SELECT id FROM profile WHERE uid = %d",
intval(local_channel()));
$num_profiles = count($r0);
$name = t('Profile-') . ($num_profiles + 1);
$r1 = q("SELECT fullname, photo, thumb FROM profile WHERE uid = %d AND is_default = 1 LIMIT 1",
intval(local_channel()));
$r2 = profile_store_lowlevel(
[
'aid' => intval(get_account_id()),
@@ -83,27 +83,27 @@ class Profiles extends \Zotlabs\Web\Controller {
'thumb' => $r1[0]['thumb']
]
);
$r3 = q("SELECT id FROM profile WHERE uid = %d AND profile_name = '%s' LIMIT 1",
intval(local_channel()),
dbesc($name)
);
info( t('New profile created.') . EOL);
if(count($r3) == 1)
goaway(z_root() . '/profiles/' . $r3[0]['id']);
goaway(z_root() . '/profiles');
}
}
if((argc() > 2) && (argv(1) === 'clone')) {
check_form_security_token_redirectOnErr('/profiles', 'profile_clone', 't');
$r0 = q("SELECT id FROM profile WHERE uid = %d",
intval(local_channel()));
$num_profiles = count($r0);
$name = t('Profile-') . ($num_profiles + 1);
$r1 = q("SELECT * FROM profile WHERE uid = %d AND id = %d LIMIT 1",
intval(local_channel()),
@@ -116,30 +116,30 @@ class Profiles extends \Zotlabs\Web\Controller {
}
unset($r1[0]['id']);
$r1[0]['is_default'] = 0;
$r1[0]['publish'] = 0;
$r1[0]['publish'] = 0;
$r1[0]['profile_name'] = dbesc($name);
$r1[0]['profile_guid'] = dbesc(random_string());
create_table_from_array('profile', $r1[0]);
$r3 = q("SELECT id FROM profile WHERE uid = %d AND profile_name = '%s' LIMIT 1",
intval(local_channel()),
dbesc($name)
);
info( t('New profile created.') . EOL);
profiles_build_sync(local_channel());
if(($r3) && (count($r3) == 1))
goaway(z_root() . '/profiles/' . $r3[0]['id']);
goaway(z_root() . '/profiles');
return; // NOTREACHED
}
if((argc() > 2) && (argv(1) === 'export')) {
$r1 = q("SELECT * FROM profile WHERE uid = %d AND id = %d LIMIT 1",
intval(local_channel()),
intval(argv(2))
@@ -151,7 +151,7 @@ class Profiles extends \Zotlabs\Web\Controller {
}
header('content-type: application/octet_stream');
header('content-disposition: attachment; filename="' . $r1[0]['profile_name'] . '.json"' );
unset($r1[0]['id']);
unset($r1[0]['aid']);
unset($r1[0]['uid']);
@@ -162,10 +162,10 @@ class Profiles extends \Zotlabs\Web\Controller {
echo json_encode($r1[0]);
killme();
}
// Run profile_load() here to make sure the theme is set before
// we start loading content
if(((argc() > 1) && (intval(argv(1)))) || !feature_enabled(local_channel(),'multi_profiles')) {
@@ -187,28 +187,28 @@ class Profiles extends \Zotlabs\Web\Controller {
\App::$error = 404;
return;
}
$chan = \App::get_channel();
profile_load($chan['channel_address'],$r[0]['id']);
}
}
function post() {
if(! local_channel()) {
notice( t('Permission denied.') . EOL);
return;
}
require_once('include/activities.php');
$namechanged = false;
// import from json export file.
// Only import fields that are allowed on this hub
if(x($_FILES,'userfile')) {
$src = $_FILES['userfile']['tmp_name'];
$filesize = intval($_FILES['userfile']['size']);
@@ -230,10 +230,10 @@ class Profiles extends \Zotlabs\Web\Controller {
}
}
}
call_hooks('profile_post', $_POST);
if((argc() > 1) && (argv(1) !== "new") && intval(argv(1))) {
$orig = q("SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1",
intval(\App::$argv[1]),
@@ -243,26 +243,26 @@ class Profiles extends \Zotlabs\Web\Controller {
notice( t('Profile not found.') . EOL);
return;
}
check_form_security_token_redirectOnErr('/profiles', 'profile_edit');
$is_default = (($orig[0]['is_default']) ? 1 : 0);
$profile_name = notags(trim($_POST['profile_name']));
if(! strlen($profile_name)) {
notice( t('Profile Name is required.') . EOL);
return;
}
$dob = $_POST['dob'] ? escape_tags(trim($_POST['dob'])) : '0000-00-00'; // FIXME: Needs to be validated?
$y = substr($dob,0,4);
if((! ctype_digit($y)) || ($y < 1900))
$ignore_year = true;
else
$ignore_year = false;
if($dob != '0000-00-00') {
if(strpos($dob,'0000-') === 0) {
$ignore_year = true;
@@ -272,12 +272,12 @@ class Profiles extends \Zotlabs\Web\Controller {
if($ignore_year)
$dob = '0000-' . $dob;
}
$name = escape_tags(trim($_POST['name']));
if($orig[0]['fullname'] != $name) {
$namechanged = true;
$v = validate_channelname($name);
if($v) {
notice($v);
@@ -285,7 +285,7 @@ class Profiles extends \Zotlabs\Web\Controller {
$name = $orig[0]['fullname'];
}
}
$pdesc = escape_tags(trim($_POST['pdesc']));
$gender = escape_tags(trim($_POST['gender']));
$address = escape_tags(trim($_POST['address']));
@@ -301,10 +301,10 @@ class Profiles extends \Zotlabs\Web\Controller {
$hometown = escape_tags(trim($_POST['hometown']));
$politic = escape_tags(trim($_POST['politic']));
$religion = escape_tags(trim($_POST['religion']));
$likes = fix_mce_lf(escape_tags(trim($_POST['likes'])));
$dislikes = fix_mce_lf(escape_tags(trim($_POST['dislikes'])));
$about = fix_mce_lf(escape_tags(trim($_POST['about'])));
$interest = fix_mce_lf(escape_tags(trim($_POST['interest'])));
$contact = fix_mce_lf(escape_tags(trim($_POST['contact'])));
@@ -316,11 +316,11 @@ class Profiles extends \Zotlabs\Web\Controller {
$romance = fix_mce_lf(escape_tags(trim($_POST['romance'])));
$work = fix_mce_lf(escape_tags(trim($_POST['work'])));
$education = fix_mce_lf(escape_tags(trim($_POST['education'])));
$hide_friends = ((intval($_POST['hide_friends'])) ? 1: 0);
// start fresh and create a new vcard. TODO: preserve the original guid or whatever else needs saving
// $orig_vcard = (($orig[0]['profile_vcard']) ? \Sabre\VObject\Reader::read($orig[0]['profile_vcard']) : null);
// $orig_vcard = (($orig[0]['profile_vcard']) ? \Sabre\VObject\Reader::read($orig[0]['profile_vcard']) : null);
$orig_vcard = null;
@@ -347,7 +347,7 @@ class Profiles extends \Zotlabs\Web\Controller {
5 => $postal_code,
6 => $country_name
];
$profile_vcard = update_vcard($defcard,$orig_vcard);
$orig_vcard = \Sabre\VObject\Reader::read($profile_vcard);
@@ -370,19 +370,19 @@ class Profiles extends \Zotlabs\Web\Controller {
linkify_tags($romance, local_channel());
linkify_tags($work, local_channel());
linkify_tags($education, local_channel());
$with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : '');
if(! strlen($howlong))
$howlong = NULL_DATE;
else
$howlong = datetime_convert(date_default_timezone_get(),'UTC',$howlong);
// linkify the relationship target if applicable
$withchanged = false;
if(strlen($with)) {
if($with != strip_tags($orig[0]['partner'])) {
$withchanged = true;
@@ -392,7 +392,7 @@ class Profiles extends \Zotlabs\Web\Controller {
$lookup = substr($lookup,1);
$lookup = str_replace('_',' ', $lookup);
$newname = $lookup;
$r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1",
dbesc($newname),
intval(local_channel())
@@ -407,8 +407,8 @@ class Profiles extends \Zotlabs\Web\Controller {
$prf = $r[0]['xchan_url'];
$newname = $r[0]['xchan_name'];
}
if($prf) {
$with = str_replace($lookup,'<a href="' . $prf . '">' . $newname . '</a>', $with);
if(strpos($with,'@') === 0)
@@ -418,7 +418,7 @@ class Profiles extends \Zotlabs\Web\Controller {
else
$with = $orig[0]['partner'];
}
$profile_fields_basic = get_profile_fields_basic();
$profile_fields_advanced = get_profile_fields_advanced();
$advanced = ((feature_enabled(local_channel(),'advanced_profiles')) ? true : false);
@@ -426,7 +426,7 @@ class Profiles extends \Zotlabs\Web\Controller {
$fields = $profile_fields_advanced;
else
$fields = $profile_fields_basic;
$z = q("select * from profdef where true");
if($z) {
foreach($z as $zz) {
@@ -453,7 +453,7 @@ class Profiles extends \Zotlabs\Web\Controller {
}
}
}
$changes = array();
$value = '';
if($is_default) {
@@ -513,12 +513,12 @@ class Profiles extends \Zotlabs\Web\Controller {
$comma2 = (($region && $country_name) ? ', ' : '');
$value = $locality . $comma1 . $region . $comma2 . $country_name;
}
profile_activity($changes,$value);
}
$r = q("UPDATE profile
}
$r = q("UPDATE profile
SET profile_name = '%s',
fullname = '%s',
pdesc = '%s',
@@ -591,10 +591,10 @@ class Profiles extends \Zotlabs\Web\Controller {
intval(argv(1)),
intval(local_channel())
);
if($r)
info( t('Profile updated.') . EOL);
$r = q("select * from profile where id = %d and uid = %d limit 1",
intval(argv(1)),
intval(local_channel())
@@ -603,9 +603,9 @@ class Profiles extends \Zotlabs\Web\Controller {
require_once('include/zot.php');
Libsync::build_sync_packet(local_channel(),array('profile' => $r));
}
$channel = \App::get_channel();
if($namechanged && $is_default) {
$r = q("UPDATE xchan SET xchan_name = '%s', xchan_name_date = '%s' WHERE xchan_url = '%s'",
dbesc($name),
@@ -617,7 +617,7 @@ class Profiles extends \Zotlabs\Web\Controller {
dbesc($channel['xchan_hash'])
);
}
if($is_default) {
// reload the info for the sidebar widget - why does this not work?
profile_load($channel['channel_address']);
@@ -625,24 +625,24 @@ class Profiles extends \Zotlabs\Web\Controller {
}
}
}
function get() {
$o = '';
$channel = \App::get_channel();
if(! local_channel()) {
notice( t('Permission denied.') . EOL);
return;
}
require_once('include/channel.php');
$profile_fields_basic = get_profile_fields_basic();
$profile_fields_advanced = get_profile_fields_advanced();
if(((argc() > 1) && (intval(argv(1)))) || !feature_enabled(local_channel(),'multi_profiles')) {
if(feature_enabled(local_channel(),'multi_profiles'))
$id = \App::$argv[1];
@@ -652,7 +652,7 @@ class Profiles extends \Zotlabs\Web\Controller {
);
if($x)
$id = $x[0]['id'];
}
}
$r = q("SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1",
intval($id),
intval(local_channel())
@@ -661,20 +661,20 @@ class Profiles extends \Zotlabs\Web\Controller {
notice( t('Profile not found.') . EOL);
return;
}
$editselect = 'none';
\App::$page['htmlhead'] .= replace_macros(get_markup_template('profed_head.tpl'), array(
'$baseurl' => z_root(),
'$editselect' => $editselect,
));
$advanced = ((feature_enabled(local_channel(),'advanced_profiles')) ? true : false);
if($advanced)
$fields = $profile_fields_advanced;
else
$fields = $profile_fields_basic;
$hide_friends = array(
'hide_friends',
t('Hide your connections list from viewers of this profile'),
@@ -682,36 +682,36 @@ class Profiles extends \Zotlabs\Web\Controller {
'',
array(t('No'),t('Yes'))
);
$q = q("select * from profdef where true");
if($q) {
$extra_fields = array();
foreach($q as $qq) {
$mine = q("select v from profext where k = '%s' and hash = '%s' and channel_id = %d limit 1",
dbesc($qq['field_name']),
dbesc($qq['field_name']),
dbesc($r[0]['profile_guid']),
intval(local_channel())
);
if(array_key_exists($qq['field_name'],$fields)) {
$extra_fields[] = array($qq['field_name'],$qq['field_desc'],(($mine) ? $mine[0]['v'] : ''), $qq['field_help']);
}
}
}
//logger('extra_fields: ' . print_r($extra_fields,true));
$vc = $r[0]['profile_vcard'];
$vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null);
$vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null);
$vcard = (($vctmp) ? get_vcard_array($vctmp,$r[0]['id']) : [] );
$f = get_config('system','birthday_input_format');
if(! $f)
$f = 'ymd';
$is_default = (($r[0]['is_default']) ? 1 : 0);
$tpl = get_markup_template("profile_edit.tpl");
$o .= replace_macros($tpl,array(
'$multi_profiles' => ((feature_enabled(local_channel(),'multi_profiles')) ? true : false),
@@ -749,7 +749,7 @@ class Profiles extends \Zotlabs\Web\Controller {
'$default' => t('This is your default profile.') . EOL . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))),
'$advanced' => $advanced,
'$name' => array('name', t('Your full name'), $r[0]['fullname'], t('Required'), '*'),
'$pdesc' => array('pdesc', t('Title/Description'), $r[0]['pdesc']),
'$pdesc' => array('pdesc', t('Short title/tescription'), $r[0]['pdesc'], t('Maximal 190 characters'), '', 'maxlength="190"'),
'$dob' => dob($r[0]['dob']),
'$hide_friends' => $hide_friends,
'$address' => array('address', t('Street address'), $r[0]['address']),
@@ -802,18 +802,18 @@ class Profiles extends \Zotlabs\Web\Controller {
'$delete' => t('Delete'),
'$cancel' => t('Cancel'),
));
$arr = array('profile' => $r[0], 'entry' => $o);
call_hooks('profile_edit', $arr);
return $o;
}
else {
$r = q("SELECT * FROM profile WHERE uid = %d",
local_channel());
if($r) {
$tpl = get_markup_template('profile_entry.tpl');
foreach($r as $rr) {
$profiles .= replace_macros($tpl, array(
@@ -821,24 +821,24 @@ class Profiles extends \Zotlabs\Web\Controller {
'$id' => $rr['id'],
'$alt' => t('Profile Image'),
'$profile_name' => $rr['profile_name'],
'$visible' => (($rr['is_default'])
? '<strong>' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))) . '</strong>'
'$visible' => (($rr['is_default'])
? '<strong>' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))) . '</strong>'
: '<a href="' . z_root() . '/profperm/' . $rr['id'] . '" />' . t('Edit visibility') . '</a>')
));
}
$tpl_header = get_markup_template('profile_listing_header.tpl');
$o .= replace_macros($tpl_header,array(
'$header' => t('Edit Profiles'),
'$cr_new' => t('Create New'),
'$cr_new_link' => 'profiles/new?t=' . get_form_security_token("profile_new"),
'$profiles' => $profiles
));
));
}
return $o;
}
}
}

View File

@@ -1,18 +1,19 @@
<?php
namespace Zotlabs\Module;
use Zotlabs\Lib\Libzotdir;
class Pubsites extends \Zotlabs\Web\Controller {
function get() {
require_once('include/dir_fns.php');
require_once('include/dir_fns.php');
$dirmode = intval(get_config('system','directory_mode'));
if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) {
$url = z_root() . '/dirsearch';
}
if(! $url) {
$directory = find_upstream_directory($dirmode);
$directory = Libzotdir::find_upstream_directory($dirmode);
$url = $directory['url'] . '/dirsearch';
}
$url .= '/sites';
@@ -20,12 +21,12 @@ class Pubsites extends \Zotlabs\Web\Controller {
$rating_enabled = get_config('system','rating_enabled');
$o .= '<div class="generic-content-wrapper">';
$o .= '<div class="section-title-wrapper"><h2>' . t('Public Hubs') . '</h2></div>';
$o .= '<div class="section-content-tools-wrapper"><div class="descriptive-text">' .
$o .= '<div class="section-content-tools-wrapper"><div class="descriptive-text">' .
t('The listed hubs allow public registration for the $Projectname network. All hubs in the network are interlinked so membership on any of them conveys membership in the network as a whole. Some hubs may require subscription or provide tiered service plans. The hub itself <strong>may</strong> provide additional details.') . '</div>' . EOL;
$ret = z_fetch_url($url);
if($ret['success']) {
$j = json_decode($ret['body'],true);
@@ -48,8 +49,8 @@ class Pubsites extends \Zotlabs\Web\Controller {
$host = strtolower(substr($jj['url'],strpos($jj['url'],'://')+3));
$rate_links = ((local_channel()) ? '<td><a href="rate?f=&target=' . $host . '" class="btn-btn-default"><i class="fa fa-check-square-o"></i> ' . t('Rate') . '</a></td>' : '');
$location = '';
if(!empty($jj['location'])) {
$location = '<p title="' . t('Location') . '" style="margin: 5px 5px 0 0; text-align: right"><i class="fa fa-globe"></i> ' . $jj['location'] . '</p>';
if(!empty($jj['location'])) {
$location = '<p title="' . t('Location') . '" style="margin: 5px 5px 0 0; text-align: right"><i class="fa fa-globe"></i> ' . $jj['location'] . '</p>';
}
else {
$location = '<br />&nbsp;';
@@ -61,14 +62,14 @@ class Pubsites extends \Zotlabs\Web\Controller {
$o .= '</tr>';
}
}
$o .= '</table>';
$o .= '</div></div>';
}
}
return $o;
}
}

View File

@@ -10,7 +10,7 @@ require_once('include/zot.php');
/**
* remote post
*
*
* https://yoursite/rpost?f=&title=&body=&remote_return=
*
* This can be called via either GET or POST, use POST for long body content as suhosin often limits GET parameter length
@@ -20,7 +20,7 @@ require_once('include/zot.php');
* body= Body of post
* url= URL which will be parsed and the results appended to the body
* source= Source application
* post_id= post_id of post to 'share' (local use only)
* post_id= post_id of post to 'share' (local use only)
* remote_return= absolute URL to return after posting is finished
* type= choices are 'html' or 'bbcode', default is 'bbcode'
*
@@ -32,16 +32,16 @@ require_once('include/zot.php');
class Rpost extends \Zotlabs\Web\Controller {
function get() {
$o = '';
if(! local_channel()) {
if(remote_channel()) {
// redirect to your own site.
// We can only do this with a GET request so you'll need to keep the text short or risk getting truncated
// by the wretched beast called 'suhosin'. All the browsers now allow long GET requests, but suhosin
// blocks them.
$url = get_rpost_path(\App::get_observer());
// make sure we're not looping to our own hub
if(($url) && (! stristr($url, \App::get_hostname()))) {
@@ -53,10 +53,10 @@ class Rpost extends \Zotlabs\Web\Controller {
goaway($url);
}
}
// The login procedure is going to bugger our $_REQUEST variables
// so save them in the session.
if(array_key_exists('body',$_REQUEST)) {
$_SESSION['rpost'] = $_REQUEST;
}
@@ -64,14 +64,14 @@ class Rpost extends \Zotlabs\Web\Controller {
}
nav_set_selected('Post');
// If we have saved rpost session variables, but nothing in the current $_REQUEST, recover the saved variables
if((! array_key_exists('body',$_REQUEST)) && (array_key_exists('rpost',$_SESSION))) {
$_REQUEST = $_SESSION['rpost'];
unset($_SESSION['rpost']);
}
if(array_key_exists('channel',$_REQUEST)) {
$r = q("select channel_id from channel where channel_account_id = %d and channel_address = '%s' limit 1",
intval(get_account_id()),
@@ -82,7 +82,7 @@ class Rpost extends \Zotlabs\Web\Controller {
$change = change_channel($r[0]['channel_id']);
}
}
if($_REQUEST['remote_return']) {
$_SESSION['remote_return'] = $_REQUEST['remote_return'];
}
@@ -91,21 +91,27 @@ class Rpost extends \Zotlabs\Web\Controller {
goaway($_SESSION['remote_return']);
goaway(z_root() . '/network');
}
$plaintext = true;
if(array_key_exists('type', $_REQUEST) && $_REQUEST['type'] === 'html') {
require_once('include/html2bbcode.php');
$_REQUEST['body'] = html2bbcode($_REQUEST['body']);
$_REQUEST['body'] = html2bbcode($_REQUEST['body']);
}
$channel = \App::get_channel();
$acl = new \Zotlabs\Access\AccessList($channel);
$channel_acl = $acl->get();
if($_REQUEST['acl']) {
$acl = new \Zotlabs\Access\AccessList([]);
$acl->set($_REQUEST['acl']);
$channel_acl = $acl->get();
}
else {
$acl = new \Zotlabs\Access\AccessList($channel);
$channel_acl = $acl->get();
}
if($_REQUEST['url']) {
$x = z_fetch_url(z_root() . '/linkinfo?f=&url=' . urlencode($_REQUEST['url']));
if($x['success'])
@@ -115,7 +121,7 @@ class Rpost extends \Zotlabs\Web\Controller {
if($_REQUEST['post_id']) {
$_REQUEST['body'] .= '[share=' . intval($_REQUEST['post_id']) . '][/share]';
}
$x = array(
'is_owner' => true,
'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''),
@@ -137,19 +143,19 @@ class Rpost extends \Zotlabs\Web\Controller {
'bbcode' => true,
'jotnets' => true
);
$editor = status_editor($a,$x,false,'Rpost');
$o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
'$title' => t('Edit post'),
'$cancel' => '',
'$editor' => $editor
));
return $o;
}
}

View File

@@ -123,7 +123,7 @@ class Sse_bs extends Controller {
$mids[] = '\'' . dbesc(@base64url_decode(substr($a,4))) . '\'';
}
$str = implode($mids, ',');
$str = implode(',', $mids);
$x = [ 'channel_id' => self::$uid, 'update' => 'unset' ];
call_hooks('update_unseen',$x);
@@ -162,7 +162,7 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
if ($notifications) {
$items = q("SELECT * FROM item
$items = q("SELECT * FROM item
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)
@@ -190,7 +190,7 @@ class Sse_bs extends Controller {
}
$r = q("SELECT count(id) as total FROM item
$r = q("SELECT count(id) as total FROM item
WHERE uid = %d and item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
@@ -231,10 +231,10 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
if ($notifications) {
$items = q("SELECT * FROM item
$items = q("SELECT * FROM item
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1 AND item_wall = 0 AND item_private = 2
AND item_unseen = 1 AND item_private = 2
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
$item_normal
@@ -259,8 +259,8 @@ class Sse_bs extends Controller {
}
$r = q("SELECT count(id) as total FROM item
WHERE uid = %d and item_unseen = 1 AND item_wall = 0 AND item_private = 2
$r = q("SELECT count(id) as total FROM item
WHERE uid = %d and item_unseen = 1 AND item_private = 2
$item_normal
$sql_extra
AND author_xchan != '%s'",
@@ -300,10 +300,10 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
if ($notifications) {
$items = q("SELECT * FROM item
$items = q("SELECT * FROM item
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1 AND item_wall = 1
AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
$item_normal
@@ -328,8 +328,8 @@ class Sse_bs extends Controller {
}
$r = q("SELECT count(id) as total FROM item
WHERE uid = %d and item_unseen = 1 AND item_wall = 1
$r = q("SELECT count(id) as total FROM item
WHERE uid = %d and item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)
$item_normal
$sql_extra
AND author_xchan != '%s'",
@@ -379,7 +379,7 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
if ($notifications) {
$items = q("SELECT * FROM item
$items = q("SELECT * FROM item
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1
@@ -410,7 +410,7 @@ class Sse_bs extends Controller {
}
$r = q("SELECT count(id) as total FROM item
$r = q("SELECT count(id) as total FROM item
WHERE uid = %d AND item_unseen = 1
AND created > '%s'
$item_normal
@@ -516,7 +516,7 @@ class Sse_bs extends Controller {
$p_str = ids_to_querystr($p, 'parent');
$p_sql = (($p_str) ? "OR parent IN ( $p_str )" : '');
$r = q("select mid from item
$r = q("select mid from item
where uid = %d and ( owner_xchan = '%s' OR author_xchan = '%s' $p_sql ) and item_unseen = 1 $sql_extra $item_normal",
intval(self::$uid),
dbesc($forums[$x]['xchan_hash']),
@@ -577,7 +577,7 @@ class Sse_bs extends Controller {
$item_normal = item_normal();
$r = q("SELECT * FROM item
$r = q("SELECT * FROM item
WHERE verb = '%s'
AND obj_type IN ('Document', 'Video', 'Audio', 'Image')
AND uid = %d

View File

@@ -7,7 +7,7 @@ use Zotlabs\Lib\Libzot;
class Zfinger extends \Zotlabs\Web\Controller {
function init() {
require_once('include/zot.php');
require_once('include/crypto.php');
@@ -26,7 +26,7 @@ class Zfinger extends \Zotlabs\Web\Controller {
if($chan) {
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],'acct:' . channel_reddress($chan));
$h = HTTPSig::create_sig($headers,$chan['channel_prvkey'], channel_url($chan));
HTTPSig::set_headers($h);
}
else {
@@ -37,7 +37,7 @@ class Zfinger extends \Zotlabs\Web\Controller {
echo $ret;
killme();
}
}

View File

@@ -3,6 +3,7 @@
namespace Zotlabs\Storage;
use Sabre\DAV;
use App;
/**
* @brief Provides a DAV frontend for the webbrowser.
@@ -76,49 +77,82 @@ class Browser extends DAV\Browser\Plugin {
* @param string $path which should be displayed
*/
public function generateDirectoryIndex($path) {
// (owner_id = channel_id) is visitor owner of this directory?
$is_owner = ((local_channel() && $this->auth->owner_id == local_channel()) ? true : false);
if ($this->auth->getTimezone())
date_default_timezone_set($this->auth->getTimezone());
require_once('include/conversation.php');
require_once('include/text.php');
if ($this->auth->owner_nick) {
$html = '';
$nick = $this->auth->owner_nick;
$channel_id = $this->auth->owner_id;
// Is visitor owner of this directory?
$is_owner = ((local_channel() && $channel_id == local_channel()) ? true : false);
$cat = ((x($_REQUEST,'cat')) ? $_REQUEST['cat'] : '');
if ($this->auth->getTimezone()) {
date_default_timezone_set($this->auth->getTimezone());
}
$files = $this->server->getPropertiesForPath($path, array(
'{DAV:}displayname',
'{DAV:}resourcetype',
'{DAV:}getcontenttype',
'{DAV:}getcontentlength',
'{DAV:}getlastmodified',
), 1);
$files = $this->server->getPropertiesForPath($path, [], 1);
$parent = $this->server->tree->getNodeForPath($path);
$parentpath = array();
// only show parent if not leaving /cloud/; TODO how to improve this?
if ($path && $path != "cloud") {
list($parentUri) = \Sabre\Uri\split($path);
$fullPath = \Sabre\HTTP\encodePath($this->server->getBaseUri() . $parentUri);
$arr = explode('/', $parent->os_path);
end($arr);
$folder_parent = ((isset($arr[1])) ? prev($arr) : '');
$parentpath['icon'] = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="' . t('parent') . '"></a>' : '';
$parentpath['path'] = $fullPath;
$folder_list = attach_folder_select_list($channel_id);
$siteroot_disabled = get_config('system', 'cloud_disable_siteroot');
$is_root_folder = (($path === 'cloud/' . $nick) ? true : false);
$parent_path = '';
if ($channel_id && ! $cat && !($siteroot_disabled && $is_root_folder)) {
list($parent_uri) = \Sabre\Uri\split($path);
$parent_path = \Sabre\HTTP\encodePath($this->server->getBaseUri() . $parent_uri);
}
$f = array();
$embedable_video_types = [
'video/mp4',
'video/ogg',
'video/webm'
];
$embedable_audio_types = [
'audio/mpeg',
'audio/wav',
'audio/ogg',
'audio/webm'
];
$f = [];
foreach ($files as $file) {
$ft = array();
$ft = [];
$type = null;
// This is the current directory, we can skip it
if (rtrim($file['href'], '/') == $path) continue;
$href = rtrim($file['href'], '/');
list(, $name) = \Sabre\Uri\split($file['href']);
// This is the current directory - skip it
if ($href === $path)
continue;
$node = $this->server->tree->getNodeForPath($href);
$data = $node->data;
$attach_hash = $data['hash'];
$folder_hash = $node->folder_hash;
list(, $filename) = \Sabre\Uri\split($href);
$name = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $filename;
$name = $this->escapeHTML($name);
$size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : '';
$lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : '');
if (isset($file[200]['{DAV:}resourcetype'])) {
$type = $file[200]['{DAV:}resourcetype']->getValue();
// resourcetype can have multiple values
@@ -128,22 +162,22 @@ class Browser extends DAV\Browser\Plugin {
// Some name mapping is preferred
switch ($v) {
case '{DAV:}collection' :
$type[$k] = t('Collection');
$type[$k] = 'Collection';
break;
case '{DAV:}principal' :
$type[$k] = t('Principal');
$type[$k] = 'Principal';
break;
case '{urn:ietf:params:xml:ns:carddav}addressbook' :
$type[$k] = t('Addressbook');
$type[$k] = 'Addressbook';
break;
case '{urn:ietf:params:xml:ns:caldav}calendar' :
$type[$k] = t('Calendar');
$type[$k] = 'Calendar';
break;
case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' :
$type[$k] = t('Schedule Inbox');
$type[$k] = 'Schedule Inbox';
break;
case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' :
$type[$k] = t('Schedule Outbox');
$type[$k] = 'Schedule Outbox';
break;
case '{http://calendarserver.org/ns/}calendar-proxy-read' :
$type[$k] = 'Proxy-Read';
@@ -158,124 +192,173 @@ class Browser extends DAV\Browser\Plugin {
// If no resourcetype was found, we attempt to use
// the contenttype property
if (!$type && isset($file[200]['{DAV:}getcontenttype'])) {
if (! $type && isset($file[200]['{DAV:}getcontenttype'])) {
$type = $file[200]['{DAV:}getcontenttype'];
}
if (!$type) $type = t('Unknown');
$size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : '';
$lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : '');
if (! $type) {
$type = $data['filetype'];
}
$fullPath = \Sabre\HTTP\encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/'));
$displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name;
$displayName = $this->escapeHTML($displayName);
$type = $this->escapeHTML($type);
$icon = '';
if ($this->enableAssets) {
$node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name);
foreach (array_reverse($this->iconMap) as $class=>$iconName) {
if ($node instanceof $class) {
$icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24"></a>';
break;
}
}
}
$parentHash = '';
$owner = $this->auth->owner_id;
$splitPath = explode('/', $fullPath);
if (count($splitPath) > 3) {
for ($i = 3; $i < count($splitPath); $i++) {
$attachName = urldecode($splitPath[$i]);
$attachHash = $this->findAttachHash($owner, $parentHash, $attachName);
$parentHash = $attachHash;
}
}
// generate preview icons for tile view.
// generate preview icons for tile view.
// Currently we only handle images, but this could potentially be extended with plugins
// to provide document and video thumbnails. SVG, PDF and office documents have some
// to provide document and video thumbnails. SVG, PDF and office documents have some
// security concerns and should only be allowed on single-user sites with tightly controlled
// upload access. system.thumbnail_security should be set to 1 if you want to include these
// types
// upload access. system.thumbnail_security should be set to 1 if you want to include these
// types
$is_creator = false;
$photo_icon = '';
$preview_style = intval(get_config('system','thumbnail_security',0));
$r = q("select content, creator from attach where hash = '%s' and uid = %d limit 1",
dbesc($attachHash),
intval($owner)
);
$is_creator = (($data['creator'] === get_observer_hash()) ? true : false);
if($r) {
$is_creator = (($r[0]['creator'] === get_observer_hash()) ? true : false);
if(file_exists(dbunescbin($r[0]['content']) . '.thumb')) {
$photo_icon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents(dbunescbin($r[0]['content']) . '.thumb'));
// logger('found thumb: ' . $photo_icon);
}
}
if(strpos($type,'image/') === 0 && $attachHash) {
$r = q("select resource_id, imgscale from photo where resource_id = '%s' and imgscale in ( %d, %d ) order by imgscale asc limit 1",
dbesc($attachHash),
if(strpos($type,'image/') === 0 && $attach_hash) {
$p = q("select resource_id, imgscale from photo where resource_id = '%s' and imgscale in ( %d, %d ) order by imgscale asc limit 1",
dbesc($attach_hash),
intval(PHOTO_RES_320),
intval(PHOTO_RES_PROFILE_80)
);
if($r) {
$photo_icon = 'photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale'];
if($p) {
$photo_icon = 'photo/' . $p[0]['resource_id'] . '-' . $p[0]['imgscale'];
}
if($type === 'image/svg+xml' && $preview_style > 0) {
$photo_icon = $fullPath;
$photo_icon = $href;
}
}
$g = [ 'resource_id' => $attachHash, 'thumbnail' => $photo_icon, 'security' => $preview_style ];
$g = [ 'resource_id' => $attach_hash, 'thumbnail' => $photo_icon, 'security' => $preview_style ];
call_hooks('file_thumbnail', $g);
$photo_icon = $g['thumbnail'];
$lockstate = (($data['allow_cid'] || $data['allow_gid'] || $data['deny_cid'] || $data['deny_gid']) ? 'lock' : 'unlock');
$id = $data['id'];
$attachIcon = ""; // "<a href=\"attach/".$attachHash."\" title=\"".$displayName."\"><i class=\"fa fa-arrow-circle-o-down\"></i></a>";
if($id) {
$terms = q("select * from term where oid = %d AND otype = %d",
intval($id),
intval(TERM_OBJ_FILE)
);
$categories = [];
$terms_str = '';
if($terms) {
foreach($terms as $t) {
$term = htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8',false) ;
if(! trim($term))
continue;
$categories[] = array('term' => $term, 'url' => $t['url']);
if ($terms_str)
$terms_str .= ',';
$terms_str .= $term;
}
$ft['terms'] = replace_macros(get_markup_template('item_categories.tpl'),array(
'$categories' => $categories
));
}
}
// put the array for this file together
$ft['attachId'] = $this->findAttachIdByHash($attachHash);
$ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->owner_nick;
$ft['attach_id'] = $id;
$ft['icon'] = $icon;
$ft['photo_icon'] = $photo_icon;
$ft['attachIcon'] = (($size) ? $attachIcon : '');
// @todo Should this be an item value, not a global one?
$ft['is_owner'] = $is_owner;
$ft['is_creator'] = $is_creator;
$ft['fullPath'] = $fullPath;
$ft['displayName'] = $displayName;
$ft['rel_path'] = (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href);
$ft['full_path'] = z_root() . (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href);
$ft['name'] = $name;
$ft['type'] = $type;
$ft['size'] = $size;
$ft['sizeFormatted'] = userReadableSize($size);
$ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : '');
$ft['iconFromType'] = getIconFromType($type);
$ft['collection'] = (($type === 'Collection') ? true : false);
$ft['size_formatted'] = userReadableSize($size);
$ft['last_modified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : '');
$ft['icon_from_type'] = getIconFromType($type);
$ft['allow_cid'] = acl2json($data['allow_cid']);
$ft['allow_gid'] = acl2json($data['allow_gid']);
$ft['deny_cid'] = acl2json($data['deny_cid']);
$ft['deny_gid'] = acl2json($data['deny_gid']);
$ft['raw_allow_cid'] = $data['allow_cid'];
$ft['raw_allow_gid'] = $data['allow_gid'];
$ft['raw_deny_cid'] = $data['deny_cid'];
$ft['raw_deny_gid'] = $data['deny_gid'];
$ft['lockstate'] = $lockstate;
$ft['resource'] = $data['hash'];
$ft['folder'] = $data['folder'];
$ft['revision'] = $data['revision'];
$ft['newfilename'] = ['newfilename_' . $id, t('Change filename to'), $name];
$ft['categories'] = ['categories_' . $id, t('Categories'), $terms_str];
// create a copy of the list which we can alter for the current resource
$folders = $folder_list;
if($data['is_dir']) {
$rm_path = $folders[$folder_hash];
// can not copy a folder into itself or own child folders
foreach($folders as $k => $v) {
if(strpos($v, $rm_path) === 0)
unset($folders[$k]);
}
}
$ft['newfolder'] = ['newfolder_' . $id, t('Select a target location'), $data['folder'], '', $folders];
$ft['copy'] = ['copy_' . $id, t('Copy to target location'), 0, '', [t('No'), t('Yes')]];
$ft['recurse'] = ['recurse_' . $id, t('Set permissions for all files and sub folders'), 0, '', [t('No'), t('Yes')]];
$ft['notify'] = ['notify_edit_' . $id, t('Notify your contacts about this file'), 0, '', [t('No'), t('Yes')]];
$embed_bbcode = '';
$link_bbcode = '';
$attach_bbcode = '';
if($data['is_photo']) {
$embed_bbcode = '[zmg]' . $ft['full_path'] . '[/zmg]';
}
elseif(strpos($type, 'video') === 0 && in_array($type, $embedable_video_types)) {
$embed_bbcode = '[zvideo]' . $ft['full_path'] . '[/zvideo]';
}
elseif(strpos($type, 'audio') === 0 && in_array($type, $embedable_audio_types)) {
$embed_bbcode = '[zaudio]' . $ft['full_path'] . '[/zaudio]';
}
$ft['embed_bbcode'] = $embed_bbcode;
if(! $data['is_dir']) {
$attach_bbcode = '[attachment]' . $data['hash'] . ',' . $data['revision'] . '[/attachment]';
}
$ft['attach_bbcode'] = $attach_bbcode;
$link_bbcode = '[zrl=' . $ft['full_path'] . ']' . $ft['name'] . '[/zrl]';
$ft['link_bbcode'] = $link_bbcode;
$f[] = $ft;
}
$output = '';
if ($this->enablePost) {
$this->server->emit('onHTMLActionsPanel', array($parent, &$output, $path));
$this->server->emit('onHTMLActionsPanel', [$parent, &$output, $path]);
}
$deftiles = (($is_owner) ? 0 : 1);
$tiles = ((array_key_exists('cloud_tiles',$_SESSION)) ? intval($_SESSION['cloud_tiles']) : $deftiles);
$_SESSION['cloud_tiles'] = $tiles;
$html .= replace_macros(get_markup_template('cloud.tpl'), array(
'$header' => t('Files') . ": " . $this->escapeHTML($path) . "/",
$header = (($cat) ? t('File category') . ": " . $this->escapeHTML($cat) : t('Files'));
$channel = channelx_by_n($channel_id);
if($channel) {
$acl = new \Zotlabs\Access\AccessList($channel);
$channel_acl = $acl->get();
$lockstate = (($acl->is_private()) ? 'lock' : 'unlock');
}
$html = replace_macros(get_markup_template('cloud.tpl'), array(
'$header' => $header,
'$total' => t('Total'),
'$actionspanel' => $output,
'$shared' => t('Shared'),
@@ -283,9 +366,12 @@ class Browser extends DAV\Browser\Plugin {
'$upload' => t('Add Files'),
'$is_owner' => $is_owner,
'$is_admin' => is_site_admin(),
'$admin_delete' => t('Admin Delete'),
'$parentpath' => $parentpath,
'$cpath' => bin2hex(\App::$query_string),
'$admin_delete_label' => t('Admin Delete'),
'$parentpath' => $parent_path,
'$folder_parent' => $folder_parent,
'$folder' => $parent->folder_hash,
'$is_root_folder' => $is_root_folder,
'$cpath' => bin2hex(App::$query_string),
'$tiles' => intval($_SESSION['cloud_tiles']),
'$entries' => $f,
'$name' => t('Name'),
@@ -293,17 +379,43 @@ class Browser extends DAV\Browser\Plugin {
'$size' => t('Size'),
'$lastmod' => t('Last Modified'),
'$parent' => t('parent'),
'$edit' => t('Edit'),
'$delete' => t('Delete'),
'$nick' => $this->auth->getCurrentUser()
'$submit_label' => t('Submit'),
'$cancel_label' => t('Cancel'),
'$delete_label' => t('Delete'),
'$channel_id' => $channel_id,
'$cpdesc' => t('Copy/paste this code to attach file to a post'),
'$cpldesc' => t('Copy/paste this URL to link file from a web page'),
'$categories' => ['categories', t('Categories')],
'$recurse' => ['recurse', t('Set permissions for all files and sub folders'), 0, '', [t('No'), t('Yes')]],
'$newfolder' => ['newfolder', t('Select a target location'), $parent->folder_hash, '', $folder_list],
'$copy' => ['copy', t('Copy to target location'), 0, '', [t('No'), t('Yes')]],
'$return_path' => $path,
'$lockstate' => $lockstate,
'$allow_cid' => acl2json($channel_acl['allow_cid']),
'$allow_gid' => acl2json($channel_acl['allow_gid']),
'$deny_cid' => acl2json($channel_acl['deny_cid']),
'$deny_gid' => acl2json($channel_acl['deny_gid']),
'$is_owner' => $is_owner,
'$select_all_label' => t('Select All'),
'$bulk_actions_label' => t('Bulk Actions'),
'$adjust_permissions_label' => t('Adjust Permissions'),
'$move_copy_label' => t('Move or Copy'),
'$categories_label' => t('Categories'),
'$download_label' => t('Download'),
'$info_label' => t('Info'),
'$rename_label' => t('Rename'),
'$post_label' => t('Post'),
'$attach_bbcode_label' => t('Attachment BBcode'),
'$embed_bbcode_label' => t('Embed BBcode'),
'$link_bbcode_label' => t('Link BBcode'),
'$close_label' => t('Close')
));
$a = false;
nav_set_selected('Files');
\App::$page['content'] = $html;
App::$page['content'] = $html;
load_pdl();
$current_theme = \Zotlabs\Render\Theme::current();
@@ -335,6 +447,7 @@ class Browser extends DAV\Browser\Plugin {
// SimpleCollection, we won't need to show the panel either.
if (get_class($node) === 'Sabre\\DAV\\SimpleCollection')
return;
require_once('include/acl_selectors.php');
$aclselect = null;
@@ -387,10 +500,39 @@ class Browser extends DAV\Browser\Plugin {
$special = 'cloud/' . $this->auth->owner_nick;
$count = strlen($special);
if(strpos($path,$special) === 0)
$path = trim(substr($path,$count),'/');
if(strpos($path,$special) === 0)
$display_path = trim(substr($path,$count),'/');
$breadcrumbs_html = '';
if($display_path && ! $_REQUEST['cat'] && ! $_SESSION['cloud_tiles']){
$breadcrumbs = [];
$folders = explode('/', $display_path);
$folder_hashes = explode('/', $node->os_path);
$breadcrumb_path = z_root() . '/cloud/' . $this->auth->owner_nick;
$breadcrumbs[] = [
'name' => $this->auth->owner_nick,
'hash' => '',
'path' => $breadcrumb_path
];
foreach($folders as $i => $name) {
$breadcrumb_path .= '/' . $name;
$breadcrumbs[] = [
'name' => $name,
'hash' => $folder_hashes[$i],
'path' => $breadcrumb_path
];
}
$breadcrumbs_html = replace_macros(get_markup_template('breadcrumb.tpl'), array(
'$breadcrumbs' => $breadcrumbs
));
}
$output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array(
'$folder_header' => t('Create new folder'),
'$folder_submit' => t('Create'),
@@ -404,11 +546,12 @@ class Browser extends DAV\Browser\Plugin {
'$deny_cid' => acl2json($channel_acl['deny_cid']),
'$deny_gid' => acl2json($channel_acl['deny_gid']),
'$lockstate' => $lockstate,
'$return_url' => \App::$cmd,
'$path' => $path,
'$folder' => find_folder_hash_by_path($this->auth->owner_id, $path),
'$return_url' => $path,
'$folder' => $node->folder_hash,
'$dragdroptext' => t('Drop files here to immediately upload'),
'$notify' => ['notify', t('Show in your contacts shared folder'), 0, '', [t('No'), t('Yes')]]
'$notify' => ['notify', t('Show in your contacts shared folder'), 0, '', [t('No'), t('Yes')]],
'$breadcrumbs_html' => $breadcrumbs_html,
'$drop_area_label' => t('You can select files via the upload button or drop them right here or into an existing folder.')
));
}
@@ -453,6 +596,21 @@ class Browser extends DAV\Browser\Plugin {
return $hash;
}
protected function findAttachHashFlat($owner, $attachName) {
$r = q("SELECT hash FROM attach WHERE uid = %d AND filename = '%s' ORDER BY edited DESC LIMIT 1",
intval($owner),
dbesc($attachName)
);
$hash = '';
if ($r) {
foreach ($r as $rr) {
$hash = $rr['hash'];
}
}
return $hash;
}
/**
* @brief Returns an attachment's id for a given hash.
*

View File

@@ -25,7 +25,10 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
* @var string $red_path
*/
private $red_path;
private $folder_hash;
public $folder_hash;
public $data;
/**
* @brief The full path as seen in the browser.
* /cloud + $red_path
@@ -41,7 +44,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
*
* @var string $os_path
*/
private $os_path = '';
public $os_path = '';
/**
* @brief Sets up the directory node, expects a full path.
@@ -49,7 +52,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
* @param string $ext_path a full path
* @param BasicAuth &$auth_plugin
*/
public function __construct($ext_path, &$auth_plugin) {
public function __construct($ext_path, $data, &$auth_plugin) {
// $ext_path = urldecode($ext_path);
logger('directory ' . $ext_path, LOGGER_DATA);
$this->ext_path = $ext_path;
@@ -61,6 +64,8 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
}
$this->auth = $auth_plugin;
$this->folder_hash = '';
$this->data = $data;
$this->getDir();
if($this->auth->browser) {
@@ -116,7 +121,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
$modulename = \App::$module;
if ($this->red_path === '/' && $name === $modulename) {
return new Directory('/' . $modulename, $this->auth);
return new Directory('/' . $modulename, [], $this->auth);
}
$x = $this->FileData($this->ext_path . '/' . $name, $this->auth);
@@ -269,8 +274,8 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
dbesc($f),
dbesc(datetime_convert()),
dbesc(datetime_convert()),
'',
'',
'',
'',
dbesc($allow_cid),
dbesc($allow_gid),
dbesc($deny_cid),
@@ -293,7 +298,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
else {
$size = file_put_contents($f, $data);
}
// delete attach entry if file_put_contents() failed
if ($size === false) {
logger('file_put_contents() failed to ' . $f);
@@ -374,7 +379,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
$args = array( 'resource_id' => $hash, 'album' => $album, 'os_syspath' => $f, 'os_path' => $xpath['os_path'], 'display_path' => $xpath['path'], 'filename' => $name, 'getimagesize' => $gis, 'directory' => $direct);
$p = photo_upload($c[0], \App::get_observer(), $args);
}
\Zotlabs\Daemon\Master::Summon([ 'Thumbnail' , $hash ]);
$sync = attach_export_data($c[0], $hash);
@@ -402,13 +407,14 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
if ($r) {
// When initiated from DAV, set the 'force' flag on attach_mkdir(). This will cause the operation to report success even if the
// folder already exists.
// When initiated from DAV, set the 'force' flag on attach_mkdir(). This will cause the operation to report success even if the
// folder already exists.
require_once('include/attach.php');
$result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash, 'force' => true));
if($result['success']) {
$sync = attach_export_data($r[0],$result['data']['hash']);
logger('createDirectory: attach_export_data returns $sync:' . print_r($sync, true), LOGGER_DEBUG);
@@ -476,15 +482,16 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
public function moveInto($targetName,$sourcePath, DAV\INode $sourceNode) {
if(! $this->auth->owner_id) {
return false;
}
$channel_id = $this->auth->owner_id;
// Files have $sourceNode->data['hash'] set. For directories rely on $sourceNode->folder_hash.
$resource_id = ((isset($sourceNode->data['hash'])) ? $sourceNode->data['hash'] : $sourceNode->folder_hash);
$new_folder_hash = $this->folder_hash;
if(! ($sourceNode->data && $sourceNode->data->hash)) {
if(!$channel_id && !$resource_id)
return false;
}
return attach_move($this->auth->owner_id, $sourceNode->data->hash, $this->folder_hash);
$ret = attach_move($channel_id, $resource_id, $new_folder_hash);
return $ret['success'];
}
@@ -515,6 +522,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
$file = trim($file, '/');
$path_arr = explode('/', $file);
if (! $path_arr)
return;
@@ -609,6 +617,9 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
$file = trim($file, '/');
$path_arr = explode('/', $file);
$cat = $_REQUEST['cat'];
if (! $path_arr)
return null;
@@ -679,7 +690,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
$_SESSION['cloud_sort'] = 'name';
switch($_SESSION['cloud_sort']) {
case 'size':
case 'size':
$suffix = ' order by is_dir desc, filesize asc ';
break;
// The following provides inconsistent results for directories because we re-calculate the date for directories based on the most recent change
@@ -692,17 +703,34 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
break;
}
$r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix",
dbesc($folder),
intval($channel_id)
);
if ($cat) {
$r = q("select $prefix attach.id, attach.uid, attach.hash, attach.filename, attach.is_photo,
attach.filetype, attach.filesize, attach.revision, attach.folder, attach.creator,
attach.flags, attach.is_dir, attach.created, attach.edited, attach.display_path,
attach.allow_cid, attach.allow_gid, attach.deny_cid, attach.deny_gid from attach
left join term on attach.id = term.oid
where term.term = '%s' and attach.uid = %d $perms $suffix",
dbesc($cat),
intval($channel_id)
);
}
else {
$r = q("select $prefix attach.id, attach.uid, attach.hash, attach.filename, attach.is_photo,
attach.filetype, attach.filesize, attach.revision, attach.folder, attach.creator,
attach.flags, attach.is_dir, attach.created, attach.edited, attach.display_path,
attach.allow_cid, attach.allow_gid, attach.deny_cid, attach.deny_gid from attach
where folder = '%s' and uid = %d $perms $suffix",
dbesc($folder),
intval($channel_id)
);
}
foreach ($r as $rr) {
if(\App::$module === 'cloud' && (strpos($rr['filename'],'.') === 0) && (! get_pconfig($channel_id,'system','show_dot_files')) )
continue;
// @FIXME I don't think we use revisions currently in attach structures.
// In case we see any in the wild provide a unique filename. This
// In case we see any in the wild provide a unique filename. This
// name may or may not be accessible
if($rr['revision'])
@@ -710,13 +738,12 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
//logger('filename: ' . $rr['filename'], LOGGER_DEBUG);
if (intval($rr['is_dir'])) {
$ret[] = new Directory($path . '/' . $rr['filename'], $auth);
$ret[] = new Directory($path . '/' . $rr['filename'], $rr, $auth);
}
else {
$ret[] = new File($path . '/' . $rr['filename'], $rr, $auth);
}
}
return $ret;
}
@@ -738,15 +765,14 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
return $ret;
}
$r = q("SELECT channel_id, channel_address, profile.publish FROM channel left join profile on profile.uid = channel.channel_id WHERE channel_removed = 0 AND channel_system = 0 AND (channel_pageflags & %d) = 0",
$r = q("SELECT channel_id, channel_address, profile.publish FROM channel left join profile on profile.uid = channel.channel_id WHERE channel_removed = 0 AND channel_system = 0 AND (channel_pageflags & %d) = 0 and profile.is_default = 1",
intval(PAGE_HIDDEN)
);
if ($r) {
foreach ($r as $rr) {
if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage') && $rr['publish']) {
if ((perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage') && $rr['publish'])|| $rr['channel_id'] == $this->auth->channel_id) {
logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA);
$ret[] = new Directory($rr['channel_address'], $auth);
$ret[] = new Directory($rr['channel_address'], [], $auth);
}
}
}
@@ -778,7 +804,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
}
if ((! $file) || ($file === '/')) {
return new Directory('/', $auth);
return new Directory('/', [], $auth);
}
$file = trim($file, '/');
@@ -848,7 +874,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
if ($test)
return true;
// final component was a directory.
return new Directory($file, $auth);
return new Directory($file, [], $auth);
}
if ($errors) {
@@ -867,7 +893,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
return true;
if (intval($r[0]['is_dir'])) {
return new Directory($path . '/' . $r[0]['filename'], $auth);
return new Directory($path . '/' . $r[0]['filename'], [], $auth);
}
else {
return new File($path . '/' . $r[0]['filename'], $r[0], $auth);
@@ -888,7 +914,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
$used = 0;
$limit = 0;
$free = 0;
if ($this->auth->owner_id) {
$channel = channelx_by_n($this->auth->owner_id);
if($channel) {
@@ -919,5 +945,4 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
return [ (int) $used, (int) $free ];
}
}

View File

@@ -151,13 +151,13 @@ class HTTPSig {
$result['signer'] = $sig_block['keyId'];
$key = self::get_key($key,$keytype,$result['signer']);
$cached_key = self::get_key($key,$keytype,$result['signer']);
if(! ($key && $key['public_key'])) {
if(! ($cached_key && $cached_key['public_key'])) {
return $result;
}
$x = rsa_verify($signed_data,$sig_block['signature'],$key['public_key'],$algorithm);
$x = rsa_verify($signed_data,$sig_block['signature'],$cached_key['public_key'],$algorithm);
logger('verified: ' . $x, LOGGER_DEBUG);
@@ -166,15 +166,15 @@ class HTTPSig {
// try again, ignoring the local actor (xchan) cache and refetching the key
// from its source
$fkey = self::get_key($key,$keytype,$result['signer'],true);
$fetched_key = self::get_key($key,$keytype,$result['signer'],true);
if ($fkey && $fkey['public_key']) {
$y = rsa_verify($signed_data,$sig_block['signature'],$fkey['public_key'],$algorithm);
if ($fetched_key && $fetched_key['public_key']) {
$y = rsa_verify($signed_data,$sig_block['signature'],$fetched_key['public_key'],$algorithm);
logger('verified: (cache reload) ' . $x, LOGGER_DEBUG);
}
if (! $y) {
logger('verify failed for ' . $result['signer'] . ' alg=' . $algorithm . (($fkey['public_key']) ? '' : ' no key'));
logger('verify failed for ' . $result['signer'] . ' alg=' . $algorithm . (($fetched_key['public_key']) ? '' : ' no key'));
$sig_block['signature'] = base64_encode($sig_block['signature']);
logger('affected sigblock: ' . print_r($sig_block,true));
logger('headers: ' . print_r($headers,true));
@@ -184,6 +184,8 @@ class HTTPSig {
}
$key = (($fetched_key) ? $fetched_key : $cached_key);
$result['portable_id'] = $key['portable_id'];
$result['header_valid'] = true;
@@ -244,7 +246,7 @@ class HTTPSig {
function convertKey($key) {
if(strstr($key,'RSA ')) {
if(strstr($key,'RSA ')) {
return rsatopem($key);
}
elseif(substr($key,0,5) === 'data:') {
@@ -453,7 +455,7 @@ class HTTPSig {
foreach($headers as $h) {
header($h);
}
}
}
}

View File

@@ -21,7 +21,9 @@ class Categories {
if(($articles) && (! Apps::system_app_installed(App::$profile['profile_uid'],'Articles')))
return '';
if((! App::$profile['profile_uid'])
$files = ((array_key_exists('files',$arr) && $arr['files']) ? true : false);
if((! App::$profile['profile_uid'])
|| (! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),(($cards || $articles) ? 'view_pages' : 'view_stream')))) {
return '';
}
@@ -29,12 +31,14 @@ class Categories {
$cat = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : '');
$srchurl = (($cards) ? App::$argv[0] . '/' . App::$argv[1] : App::$query_string);
$srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&');
$srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl);
$srchurl = str_replace(array('?f=','&f=', '/?'),array('', '', ''),$srchurl);
if($cards)
return cardcategories_widget($srchurl, $cat);
elseif($articles)
return articlecategories_widget($srchurl, $cat);
elseif($files)
return filecategories_widget($srchurl, $cat);
else
return categories_widget($srchurl, $cat);

View File

@@ -2,10 +2,10 @@
namespace Zotlabs\Widget;
require_once('include/dir_fns.php');
use Zotlabs\Lib\Libzotdir;
class Dirsort {
function widget($arr) {
return dir_sort_links();
return Libzotdir::dir_sort_links();
}
}

View File

@@ -88,7 +88,7 @@ class Finger {
$headers = [];
$headers['X-Zot-Channel'] = $channel['channel_address'] . '@' . \App::get_hostname();
$headers['X-Zot-Nonce'] = random_string();
$xhead = HTTPSig::create_sig($headers,$channel['channel_prvkey'],'acct:' . channel_reddress($channel));
$xhead = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel));
$retries = 0;
@@ -100,7 +100,7 @@ class Finger {
$result = z_post_url('http://' . $host . $rhs,$postvars, $retries, [ 'headers' => $xhead ]);
}
}
}
}
else {
$rhs .= '?f=&address=' . urlencode($address) . '&token=' . self::$token;

View File

@@ -226,18 +226,18 @@ class Zot6Handler implements IHandler {
if ($recipients) {
// basically this means "unfriend"
foreach ($recipients as $recip) {
$r = q("select channel.*,xchan.* from channel
$channel = q("select channel.*,xchan.* from channel
left join xchan on channel_hash = xchan_hash
where channel_hash = '%s' limit 1",
dbesc($recip)
);
if ($r) {
$r = q("select abook_id from abook where uid = %d and abook_xchan = '%s' limit 1",
intval($r[0]['channel_id']),
if ($channel) {
$abook = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s' limit 1",
intval($channel[0]['channel_id']),
dbesc($sender)
);
if ($r) {
contact_remove($r[0]['channel_id'],$r[0]['abook_id']);
if ($abook) {
contact_remove($channel[0]['channel_id'],$abook[0]['abook_id']);
}
}
}

View File

@@ -1,6 +0,0 @@
version: 2
url: $baseurl/mail/combined
requires: local_channel
name: Mail
photo: icon:envelope
categories: nav_featured_app, Personal

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -50,7 +50,7 @@ require_once('include/attach.php');
require_once('include/bbcode.php');
define ( 'PLATFORM_NAME', 'hubzilla' );
define ( 'STD_VERSION', '5.0.7' );
define ( 'STD_VERSION', '5.2' );
define ( 'ZOT_REVISION', '6.0' );
define ( 'DB_UPDATE_VERSION', 1240 );
@@ -82,11 +82,16 @@ define ( 'DIRECTORY_MODE_STANDALONE', 0x0100); // A detached (off the grid) hub
define ( 'DIRECTORY_REALM', 'RED_GLOBAL');
define ( 'DIRECTORY_FALLBACK_MASTER', 'https://hub.netzgemeinde.eu');
$DIRECTORY_FALLBACK_SERVERS = array(
'https://hub.netzgemeinde.eu',
'https://zotsite.net',
'https://hub.libranet.de'
);
function get_directory_fallback_servers() {
$ret = [
'https://hub.netzgemeinde.eu',
'https://zotsite.net',
'https://hub.libranet.de'
];
return $ret;
}
/**
@@ -355,6 +360,7 @@ define ( 'UPDATE_FLAGS_UPDATED', 0x0001);
define ( 'UPDATE_FLAGS_FORCED', 0x0002);
define ( 'UPDATE_FLAGS_DELETED', 0x1000);
define ( 'HUBLOC_OFFLINE', 0x0001);
define ( 'DROPITEM_NORMAL', 0);
define ( 'DROPITEM_PHASE1', 1);
@@ -433,7 +439,7 @@ define ( 'TERM_FORUM', 11 );
define ( 'TERM_EMOJI', 12 );
define ( 'TERM_OBJ_POST', 1 );
define ( 'TERM_OBJ_PHOTO', 2 );
define ( 'TERM_OBJ_FILE', 2 );
define ( 'TERM_OBJ_PROFILE', 3 );
define ( 'TERM_OBJ_CHANNEL', 4 );
define ( 'TERM_OBJ_OBJECT', 5 );
@@ -2017,7 +2023,7 @@ function proc_run(){
}
$args = array_map('escapeshellarg',$args);
$cmdline = implode($args," ");
$cmdline = implode(' ', $args);
if(is_windows()) {
$cwd = getcwd();

View File

@@ -39,7 +39,7 @@
"commerceguys/intl": "~1.0.5",
"lukasreschke/id3parser": "^0.0.3",
"smarty/smarty": "~3.1",
"ramsey/uuid": "^3.8",
"ramsey/uuid": "^4.1",
"twbs/bootstrap": "^4.3.1",
"blueimp/jquery-file-upload": "^10.3",
"desandro/imagesloaded": "^4.1"

2417
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -82,7 +82,6 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti
$r = q("SELECT id, hash, gname FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
intval(local_channel())
);
if($r) {
$groups .= '<optgroup label = "' . t('Privacy Groups').'">';
foreach($r as $rr) {
@@ -110,13 +109,23 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti
if($dialog_description) {
$forums = get_forum_channels(local_channel(),1);
if($forums) {
$groups .= '<optgroup label = "' . t('Forums').'">';
$forums_count = 0;
$forum_otions = '';
foreach($forums as $f) {
if($f['no_post_perms'])
continue;
$private = (($f['private_forum']) ? ' (' . t('Private Forum') . ')' : '');
$selected = (($single_group && $f['hash'] === $allow_cid[0]) ? ' selected = "selected" ' : '');
$groups .= '<option id="^' . $f['abook_id'] . '" value="^' . $f['xchan_hash'] . '"' . $selected . '>' . $f['xchan_name'] . $private . '</option>' . "\r\n";
$forum_otions .= '<option id="^' . $f['abook_id'] . '" value="^' . $f['xchan_hash'] . '"' . $selected . '>' . $f['xchan_name'] . $private . '</option>' . "\r\n";
$forums_count++;
}
$groups .= '</optgroup>';
if($forums_count) {
$groups .= '<optgroup label = "' . t('Forums').'">';
$groups .= $forum_otions;
$groups .= '</optgroup>';
}
}
}

View File

@@ -300,6 +300,44 @@ function attach_by_hash($hash, $observer_hash, $rev = 0) {
}
/**
* @brief Find an attachment by id.
*
* Returns the entire attach structure including data.
*
* This could exhaust memory so most useful only when immediately sending the data.
*
* @param string $hash
* @param string $observer_hash
* @return array
*/
function attach_by_id($id, $observer_hash) {
$ret = array('success' => false);
// Check for existence, which will also provide us the owner uid
$r = q("SELECT * FROM attach WHERE id = %d",
intval($id)
);
if(! $r) {
$ret['message'] = t('Item was not found.');
return $ret;
}
if(! attach_can_view($r[0]['uid'], $observer_hash, $r[0]['hash'])) {
$ret['message'] = t('Permission denied.');
return $ret;
}
$r[0]['content'] = dbunescbin($r[0]['content']);
$ret['success'] = true;
$ret['data'] = $r[0];
return $ret;
}
function attach_can_view($uid,$ob_hash,$resource) {
$sql_extra = permissions_sql($uid,$ob_hash);
@@ -647,17 +685,15 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
$pathname = filepath_macro($newalbum);
}
elseif(array_key_exists('folder',$arr)) {
$x = q("select filename from attach where hash = '%s' and uid = %d limit 1",
dbesc($arr['folder']),
intval($channel['channel_id'])
);
if($x)
$pathname = $x[0]['filename'];
$pathname = find_path_by_hash($channel['channel_id'], $arr['folder']);
}
else {
$pathname = filepath_macro($album);
}
}
elseif(array_key_exists('folder',$arr)) {
$pathname = find_path_by_hash($channel['channel_id'], $arr['folder']);
}
if(! $pathname) {
$pathname = filepath_macro($upload_path);
}
@@ -807,6 +843,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
$os_relpath = ltrim($os_relpath,'/');
$os_path = $os_relpath;
$display_path = ltrim($pathname . '/' . $filename,'/');
if($src) {
@@ -890,7 +927,6 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
);
}
else {
$r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, os_path, display_path, allow_cid, allow_gid,deny_cid, deny_gid )
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
intval($channel['channel_account_id']),
@@ -1122,7 +1158,6 @@ function attach_mkdir($channel, $observer_hash, $arr = null) {
if(! is_dir($os_basepath))
os_mkdir($os_basepath,STORAGE_DEFAULT_PERMISSIONS, true);
$os_basepath .= '/';
if(! perm_is_allowed($channel_id, $observer_hash, 'write_storage')) {
@@ -1432,7 +1467,7 @@ function attach_delete($channel_id, $resource, $is_photo = 0) {
$channel_address = (($c) ? $c[0]['channel_address'] : 'notfound');
$photo_sql = (($is_photo) ? " and is_photo = 1 " : '');
$r = q("SELECT hash, os_storage, flags, is_dir, is_photo, folder FROM attach WHERE hash = '%s' AND uid = %d $photo_sql limit 1",
$r = q("SELECT id, hash, os_storage, flags, is_dir, is_photo, folder FROM attach WHERE hash = '%s' AND uid = %d $photo_sql limit 1",
dbesc($resource),
intval($channel_id)
);
@@ -1453,6 +1488,12 @@ function attach_delete($channel_id, $resource, $is_photo = 0) {
return;
}
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel_id),
intval($r[0]['id']),
intval(TERM_OBJ_FILE)
);
// If resource is a directory delete everything in the directory recursive
if(intval($r[0]['is_dir'])) {
$x = q("SELECT hash, os_storage, is_dir, flags FROM attach WHERE folder = '%s' AND uid = %d",
@@ -1534,7 +1575,7 @@ function attach_drop_photo($channel_id,$resource) {
$interactive = (($x[0]['item_hidden']) ? false : true);
drop_item($x[0]['id'], $interactive, $stage);
}
$r = q("SELECT content FROM photo WHERE resource_id = '%s' AND uid = %d AND os_storage = 1",
dbesc($resource),
intval($channel_id)
@@ -1544,7 +1585,7 @@ function attach_drop_photo($channel_id,$resource) {
@unlink(dbunescbin($i['content']));
}
}
q("DELETE FROM photo WHERE uid = %d AND resource_id = '%s'",
intval($channel_id),
dbesc($resource)
@@ -1740,6 +1781,29 @@ function find_filename_by_hash($channel_id, $attachHash) {
return $filename;
}
/**
* @brief Returns the display_path of an attachment in a given channel.
*
* @param int $channel_id
* The id of the channel
* @param string $attachHash
* The hash of the attachment
* @return string
* The filename of the attachment
*/
function find_path_by_hash($channel_id, $attachHash) {
$r = q("SELECT display_path FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1",
intval($channel_id),
dbesc($attachHash)
);
$display_path = '';
if ($r) {
$display_path = $r[0]['display_path'];
}
return $display_path;
}
/**
* @brief Pipes $in to $out in 16KB chunks.
*
@@ -2306,6 +2370,18 @@ function attach_export_data($channel, $resource_id, $deleted = false) {
$r[0]['content'] = dbunescbin($r[0]['content']);
$hash_ptr = $r[0]['folder'];
$r[0]['term'] = [];
$term = q("SELECT * FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel['channel_id']),
intval($r[0]['id']),
intval(TERM_OBJ_FILE)
);
if ($term)
$r[0]['term'] = array_reverse($term);
$paths[] = $r[0];
} while($hash_ptr);
@@ -2314,7 +2390,6 @@ function attach_export_data($channel, $resource_id, $deleted = false) {
$ret['attach'] = $paths;
if($attach_ptr['is_photo']) {
// This query could potentially result in a few megabytes of data use.
@@ -2495,7 +2570,7 @@ function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpat
return true;
}
/**
* This function performs an in place directory-to-directory move of a stored attachment or photo.
* This function performs an in place directory-to-directory move of a stored resource.
* The data is physically moved in the store/nickname storage location and the paths adjusted
* in the attach structure (and if applicable the photo table). The new 'album name' is recorded
* for photos and will show up immediately there.
@@ -2507,60 +2582,64 @@ function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpat
* @param int $channel_id
* @param int $resource_id
* @param string $new_folder_hash
* @return void|boolean
* @param (optional) string $newname
* @param (optional) boolean $recurse
* @return array Associative array with:
* * \e boolean \b success
* * \e string \b resource_id
*/
function attach_move($channel_id, $resource_id, $new_folder_hash) {
function attach_move($channel_id, $resource_id, $new_folder_hash, $newname = '', $recurse = true) {
$ret = [
'success' => false,
'resource_id' => $resource_id
];
$c = channelx_by_n($channel_id);
if(! ($c && $resource_id))
return false;
return $ret;
// find the resource to be moved
$r = q("select * from attach where hash = '%s' and uid = %d limit 1",
dbesc($resource_id),
intval($channel_id)
);
if(! $r) {
logger('resource_id not found');
return false;
return $ret;
}
$oldstorepath = dbunescbin($r[0]['content']);
// find the resource we are moving to
if($new_folder_hash) {
$n = q("select * from attach where hash = '%s' and uid = %d and is_dir = 1 limit 1",
dbesc($new_folder_hash),
intval($channel_id)
);
if(! $n)
return false;
return $ret;
$newdirname = $n[0]['filename'];
$newalbumname = $n[0]['display_path'];
$newstorepath = dbunescbin($n[0]['content']) . '/' . $resource_id;
}
else {
// root directory
$newdirname = EMPTY_STR;
$newalbumname = EMPTY_STR;
$newstorepath = 'store/' . $c['channel_address'] . '/' . $resource_id;
}
rename($oldstorepath,$newstorepath);
if ($recurse) {
rename($oldstorepath,$newstorepath);
}
// duplicate detection. If 'overwrite' is specified, return false because we can't yet do that.
$filename = $r[0]['filename'];
// don't do duplicate check unless our parent folder has changed.
if($r[0]['folder'] !== $new_folder_hash) {
$oldfilename = $r[0]['filename'];
$filename = (($newname) ? basename($newname) : $oldfilename);
// duplicate detection.
if($recurse) {
$s = q("select filename, id, hash, filesize from attach where filename = '%s' and folder = '%s' ",
dbesc($filename),
dbesc($new_folder_hash)
@@ -2568,9 +2647,10 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) {
if($s) {
$overwrite = get_pconfig($channel_id,'system','overwrite_dup_files');
// If 'overwrite' is specified, return false because we can't yet do that.
if($overwrite) {
/// @fixme
return;
return $ret;
}
else {
if(strpos($filename,'.') !== false) {
@@ -2586,7 +2666,8 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) {
if(preg_match('/(.*?)\([0-9]{1,}\)$/',$basename,$matches))
$basename = $matches[1];
$v = q("select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' ",
$v = q("select filename from attach where uid = %d and ( filename = '%s' OR filename like '%s' ) and folder = '%s' ",
intval($channel_id),
dbesc($basename . $ext),
dbesc($basename . '(%)' . $ext),
dbesc($new_folder_hash)
@@ -2609,12 +2690,14 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) {
while($found);
$filename = $basename . '(' . $x . ')' . $ext;
}
else
else {
$filename = $basename . $ext;
}
}
}
}
q("update attach set content = '%s', folder = '%s', filename = '%s' where id = %d",
dbescbin($newstorepath),
dbesc($new_folder_hash),
@@ -2631,7 +2714,6 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) {
intval($r[0]['id'])
);
if($r[0]['is_photo']) {
q("update photo set album = '%s', filename = '%s', os_path = '%s', display_path = '%s'
where resource_id = '%s' and uid = %d",
@@ -2643,11 +2725,24 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) {
intval($channel_id)
);
q("update photo set content = '%s' where resource_id = '%s' and uid = %d and imgscale = 0",
q("update photo set content = CASE imgscale WHEN 0 THEN '%s' ELSE CONCAT('%s', '-', imgscale) END where resource_id = '%s' and uid = %d and os_storage = 1",
dbescbin($newstorepath),
dbescbin($newstorepath),
dbesc($resource_id),
intval($channel_id)
);
// now rename the thumbnails in os_storage - the original should have been copied before already
$ps = q("SELECT content, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' and imgscale > 0 and os_storage = 1",
intval($channel_id),
dbesc($resource_id)
);
if ($recurse) {
foreach($ps as $p) {
rename($oldstorepath . '-' . $p['imgscale'], $p['content']);
}
}
}
if($r[0]['is_dir']) {
@@ -2658,19 +2753,223 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) {
);
if($x) {
foreach($x as $xv) {
$rs = attach_move($channel_id,$xv['hash'],$r[0]['hash']);
if(! $rs) {
$rs = attach_move($channel_id, $xv['hash'], $r[0]['hash'], '', false);
if(! $rs['success']) {
$move_success = false;
break;
}
}
}
return $move_success;
$ret['success'] = $move_success;
return $ret;
}
return true;
$ret['success'] = true;
return $ret;
}
/**
* This function performs an in place directory-to-directory copy of a stored resource.
* The data is physically copyed in the store/nickname storage location and the paths adjusted
* in the attach structure (and if applicable the photo table). The new 'album name' is recorded
* for photos and will show up immediately there.
* This takes a channel_id, attach.hash of the file to copy (this is the same as a photo resource_id), and
* the attach.hash of the new parent folder, which must already exist. If $new_folder_hash is blank or empty,
* the new file is copyed to the root of the channel's storage area.
*
*
* @param int $channel_id
* @param int $resource_id
* @param string $new_folder_hash
* @param (optional) string $newname
* @param (optional) boolean $recurse
* @return array Associative array with:
* * \e boolean \b success
* * \e string \b resource_id of the new resource
*/
function attach_copy($channel_id, $resource_id, $new_folder_hash, $newname = '', $recurse = true) {
$ret = [
'success' => false,
'resource_id' => ''
];
$c = channelx_by_n($channel_id);
if(! ($c && $resource_id))
return $ret;
// find the resource to be copied
$r = q("select * from attach where hash = '%s' and uid = %d limit 1",
dbesc($resource_id),
intval($channel_id)
);
if(! $r) {
logger('resource_id not found');
return $ret;
}
$a = $r[0];
$new_resource_id = new_uuid();
$ret['resource_id'] = $new_resource_id;
$oldstorepath = dbunescbin($r[0]['content']);
// find the resource we are copying to
if($new_folder_hash) {
$n = q("select * from attach where hash = '%s' and uid = %d and is_dir = 1 limit 1",
dbesc($new_folder_hash),
intval($channel_id)
);
if(! $n) {
logger('new_folder_hash not found');
return $ret;
}
$newdirname = $n[0]['filename'];
$newalbumname = $n[0]['display_path'];
$newstorepath = dbunescbin($n[0]['content']) . '/' . $new_resource_id;
}
else {
// root directory
$newdirname = EMPTY_STR;
$newalbumname = EMPTY_STR;
$newstorepath = 'store/' . $c['channel_address'] . '/' . $new_resource_id;
}
if(is_dir($oldstorepath)) {
os_mkdir($newstorepath,STORAGE_DEFAULT_PERMISSIONS,true);
}
else {
copy($oldstorepath,$newstorepath);
}
$oldfilename = $r[0]['filename'];
$filename = (($newname) ? basename($newname) : $oldfilename);
// duplicate detection. If 'overwrite' is specified, return false because we can't yet do that.
if($recurse) {
$s = q("select filename, id, hash, filesize from attach where filename = '%s' and folder = '%s' ",
dbesc($filename),
dbesc($new_folder_hash)
);
if($s) {
$overwrite = get_pconfig($channel_id,'system','overwrite_dup_files');
if($overwrite) {
/// @fixme
return $ret;
}
else {
if(strpos($filename,'.') !== false) {
$basename = substr($filename,0,strrpos($filename,'.'));
$ext = substr($filename,strrpos($filename,'.'));
}
else {
$basename = $filename;
$ext = '';
}
$matches = false;
if(preg_match('/(.*?)\([0-9]{1,}\)$/',$basename,$matches))
$basename = $matches[1];
$v = q("select filename from attach where uid = %d and ( filename = '%s' OR filename like '%s' ) and folder = '%s' ",
intval($channel_id),
dbesc($basename . $ext),
dbesc($basename . '(%)' . $ext),
dbesc($new_folder_hash)
);
if($v) {
$x = 1;
do {
$found = false;
foreach($v as $vv) {
if($vv['filename'] === $basename . '(' . $x . ')' . $ext) {
$found = true;
break;
}
}
if($found)
$x++;
}
while($found);
$filename = $basename . '(' . $x . ')' . $ext;
}
else {
$filename = $basename . $ext;
}
}
}
}
unset($a['id']);
$a['hash'] = $new_resource_id;
$a['content'] = $newstorepath;
$a['folder'] = $new_folder_hash;
$a['filename'] = $filename;
create_table_from_array('attach', $a, ['content']);
$x = attach_syspaths($channel_id, $new_resource_id);
q("update attach set os_path = '%s', display_path = '%s' where hash = '%s'",
dbesc($x['os_path']),
dbesc($x['path']),
dbesc($new_resource_id)
);
if($a['is_photo']) {
$ps = q("SELECT * FROM photo WHERE uid = %d AND resource_id = '%s'",
intval($channel_id),
dbesc($resource_id)
);
foreach($ps as $p) {
unset($p['id']);
$p['resource_id'] = $new_resource_id;
$p['album'] = $newalbumname;
$p['filename'] = $filename;
$p['os_path'] = $x['os_path'];
$p['display_path'] = $x['path'];
if($p['os_storage']) {
$p['content'] = (($p['imgscale'] == 0) ? $newstorepath : $newstorepath . '-' . $p['imgscale']);
//the original should have been copied before already
if($p['imgscale'] > 0)
copy($oldstorepath, $p['content']);
}
create_table_from_array('photo', $p, ['content']);
}
}
if($r[0]['is_dir']) {
$copy_success = true;
$x = q("select hash from attach where folder = '%s' and uid = %d",
dbesc($r[0]['hash']),
intval($channel_id)
);
if($x) {
foreach($x as $xv) {
$rs = attach_copy($channel_id,$xv['hash'],$new_resource_id, '', false);
if(! $rs['success']) {
$copy_success = false;
break;
}
}
}
$ret['success'] = $copy_success;
return $ret;
}
$ret['success'] = true;
return $ret;
}
/**
* Used to generate a select input box of all your folders
@@ -2688,10 +2987,7 @@ function attach_folder_select_list($channel_id) {
if($r) {
foreach($r as $rv) {
$x = attach_folder_rpaths($r,$rv);
if($x) {
$out[$x[0]] = $x[1];
}
$out[$rv['hash']] = $rv['display_path'];
}
}
@@ -2721,7 +3017,7 @@ function attach_folder_rpaths($all_folders,$that_folder) {
if(! $found)
$error = true;
}
while((! $found) && (! $error) && ($parent_hash != ''));
while((! $error) && ($parent_hash != ''));
}
return (($error) ? false : [ $current_hash , $path ]);

View File

@@ -111,7 +111,7 @@ function process_cdav_card($f, &$vcard, $edit = false) {
function import_cdav_card($id, $ext, $table, $column, $objects, $profile, $backend, &$ids, $notice = false) {
$i = 0;
$newid = (count($ids) ? false : true);
$newid = (count($ids) ? false : true);
while ($object = $objects->getNext()) {
@@ -161,7 +161,7 @@ function import_cdav_card($id, $ext, $table, $column, $objects, $profile, $backe
);
}
if($notice && $exp == 'vcf') {
if($notice && $ext == 'vcf') {
notice(
'<strong>' . t('INVALID CARD DISMISSED!') . '</strong>' . EOL .
'<strong>' . t('Name: ') . '</strong>' . (($object->FN) ? $object->FN : t('Unknown')) . EOL .

View File

@@ -651,7 +651,7 @@ function change_channel_keys($channel) {
foreach($h as $hv) {
$hv['hubloc_guid_sig'] = $sig;
$hv['hubloc_hash'] = $hash;
$hv['hubloc_url_sig'] = base64url_encode(rsa_sign(z_root(),$modifed['channel_prvkey']));
$hv['hubloc_url_sig'] = base64url_encode(rsa_sign(z_root(),$modified['channel_prvkey']));
hubloc_store_lowlevel($hv);
}
}

View File

@@ -71,7 +71,7 @@ function abook_connections($channel_id, $sql_conditions = '') {
intval($channel_id)
);
return(($r) ? $r : array());
}
}
function abook_self($channel_id) {
$r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d
@@ -79,7 +79,7 @@ function abook_self($channel_id) {
intval($channel_id)
);
return(($r) ? $r[0] : array());
}
}
function vcard_from_xchan($xchan, $observer = null, $mode = '') {
@@ -119,14 +119,15 @@ function vcard_from_xchan($xchan, $observer = null, $mode = '') {
if(array_key_exists('channel_id',$xchan))
App::$profile_uid = $xchan['channel_id'];
$url = (($observer)
? z_root() . '/magic?f=&owa=1&bdest=' . bin2hex($xchan['xchan_url']) . '&addr=' . $xchan['xchan_addr']
$url = (($observer)
? z_root() . '/magic?f=&owa=1&bdest=' . bin2hex($xchan['xchan_url']) . '&addr=' . $xchan['xchan_addr']
: $xchan['xchan_url']
);
return replace_macros(get_markup_template('xchan_vcard.tpl'),array(
'$name' => $xchan['xchan_name'],
'$photo' => ((is_array(App::$profile) && array_key_exists('photo',App::$profile)) ? App::$profile['photo'] : $xchan['xchan_photo_l']),
'$addr' => (($xchan['xchan_addr']) ? $xchan['xchan_addr'] : $xchan['xchan_url']),
'$photo' => $xchan['xchan_photo_m'],
'$follow' => (($xchan['xchan_addr']) ? $xchan['xchan_addr'] : $xchan['xchan_url']),
'$link' => zid($xchan['xchan_url']),
'$connect' => $connect,
@@ -177,10 +178,10 @@ function abook_toggle_flag($abook,$flag) {
);
// if unsetting the archive bit, update the timestamps so we'll try to connect for an additional 30 days.
// if unsetting the archive bit, update the timestamps so we'll try to connect for an additional 30 days.
if(($flag === ABOOK_FLAG_ARCHIVED) && (intval($abook['abook_archived']))) {
$r = q("update abook set abook_connected = '%s', abook_updated = '%s'
$r = q("update abook set abook_connected = '%s', abook_updated = '%s'
where abook_id = %d and abook_channel = %d",
dbesc(datetime_convert()),
dbesc(datetime_convert()),
@@ -210,7 +211,7 @@ function mark_orphan_hubsxchans() {
if($dirmode == DIRECTORY_MODE_NORMAL)
return;
$r = q("update hubloc set hubloc_error = 1 where hubloc_error = 0
$r = q("update hubloc set hubloc_error = 1 where hubloc_error = 0
and hubloc_network = 'zot' and hubloc_connected < %s - interval %s",
db_utcnow(), db_quoteinterval('36 day')
);
@@ -301,9 +302,9 @@ function remove_all_xchan_resources($xchan, $channel_id = 0) {
);
// Cannot delete just one side of the conversation since we do not allow
// you to block private mail replies. This would leave open a gateway for abuse.
// you to block private mail replies. This would leave open a gateway for abuse.
// Both participants are owners of the conversation and both can remove it.
$r = q("delete from mail where ( from_xchan = '%s' or to_xchan = '%s' )",
dbesc($xchan),
dbesc($xchan)
@@ -356,7 +357,7 @@ function contact_remove($channel_id, $abook_id) {
call_hooks('connection_remove',$x);
$archive = get_pconfig($channel_id, 'system','archive_removed_contacts');
$archive = get_pconfig($channel_id, 'system', 'archive_removed_contacts');
if($archive) {
q("update abook set abook_archived = 1 where abook_id = %d and abook_channel = %d",
intval($abook_id),
@@ -387,7 +388,7 @@ function contact_remove($channel_id, $abook_id) {
$already_saved = [];
foreach($r as $rr) {
$w = $x = $y = null;
// optimise so we only process newly seen parent items
if (in_array($rr['parent'],$already_saved)) {
continue;
@@ -423,7 +424,7 @@ function contact_remove($channel_id, $abook_id) {
drop_item($rr['id'],false);
}
}
q("delete from abook where abook_id = %d and abook_channel = %d",
intval($abook['abook_id']),
intval($channel_id)
@@ -471,7 +472,7 @@ function random_profile() {
$r = q("select xchan_url, xchan_hash from xchan left join hubloc on hubloc_hash = xchan_hash where
xchan_hidden = 0 and xchan_system = 0 and
xchan_network = 'zot' and xchan_deleted = 0 and
xchan_network = 'zot6' and xchan_deleted = 0 and
hubloc_connected > %s - interval %s order by $randfunc limit 1",
db_utcnow(),
db_quoteinterval('30 day')
@@ -501,17 +502,17 @@ function update_vcard($arr,$vcard = null) {
$fn = $arr['fn'];
// This isn't strictly correct and could be a cause for concern.
// 'N' => array_reverse(explode(' ', $fn))
// What we really want is
// What we really want is
// 'N' => Adams;John;Quincy;Reverend,Dr.;III
// which is a very difficult parsing problem especially if you allow
// the surname to contain spaces. The only way to be sure to get it
// right is to provide a form to input all the various fields and not
// try to extract it from the FN.
// right is to provide a form to input all the various fields and not
// try to extract it from the FN.
if(! $vcard) {
$vcard = new \Sabre\VObject\Component\VCard([
@@ -689,12 +690,12 @@ function get_vcard_array($vc,$id) {
if(is_array($entry['address'])) {
array_walk($entry['address'],'array_escape_tags');
}
else {
else {
$entry['address'] = (string) escape_tags($entry['address']);
}
$adrs[] = $entry;
}
}
@@ -768,7 +769,7 @@ function vcard_query(&$r) {
if($a) {
foreach($a as $av) {
for($x = 0; $x < count($r); $x ++) {
if($r[$x]['abook_xchan'] == $av['xchan']) {
if($r[$x]['abook_xchan'] == $av['xchan']) {
$vctmp = \Sabre\VObject\Reader::read($av['v']);
$r[$x]['vcard'] = (($vctmp) ? get_vcard_array($vctmp,$r[$x]['abook_id']) : [] );
}

View File

@@ -93,7 +93,7 @@ function categories_widget($baseurl,$selected = '') {
dbesc(ACTIVITY_UPDATE)
);
}
else
else
$r = unserialize($content);
$terms = array();
@@ -206,6 +206,40 @@ function articlecategories_widget($baseurl,$selected = '') {
return '';
}
function filecategories_widget($baseurl,$selected = '') {
$perms = permissions_sql(App::$profile['profile_uid']);
$terms = array();
$r = q("select distinct(term.term)
from term join attach on term.oid = attach.id
where attach.uid = %d
and term.uid = attach.uid
and term.ttype = %d
and term.otype = %d
$perms
order by term.term asc",
intval(App::$profile['profile_uid']),
intval(TERM_CATEGORY),
intval(TERM_OBJ_FILE)
);
if($r && count($r)) {
foreach($r as $rr)
$terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : ''));
return replace_macros(get_markup_template('categories_widget.tpl'),array(
'$title' => t('Categories'),
'$desc' => '',
'$sel_all' => (($selected == '') ? 'selected' : ''),
'$all' => t('Everything'),
'$terms' => $terms,
'$base' => $baseurl,
));
}
return '';
}
function common_friends_visitor_widget($profile_uid,$cnt = 25) {

View File

@@ -16,7 +16,6 @@ require_once('include/permissions.php');
* @return array
*/
function find_upstream_directory($dirmode) {
global $DIRECTORY_FALLBACK_SERVERS;
$preferred = get_config('system','directory_server');
@@ -28,7 +27,7 @@ function find_upstream_directory($dirmode) {
);
if(($r) && ($r[0]['site_flags'] & DIRECTORY_MODE_STANDALONE)) {
$preferred = '';
}
}
}
@@ -39,14 +38,16 @@ function find_upstream_directory($dirmode) {
* from our list of directory servers. However, if we're a directory
* server ourself, point at the local instance
* We will then set this value so this should only ever happen once.
* Ideally there will be an admin setting to change to a different
* Ideally there will be an admin setting to change to a different
* directory server if you don't like our choice or if circumstances change.
*/
$directory_fallback_servers = get_directory_fallback_servers();
$dirmode = intval(get_config('system','directory_mode'));
if ($dirmode == DIRECTORY_MODE_NORMAL) {
$toss = mt_rand(0,count($DIRECTORY_FALLBACK_SERVERS));
$preferred = $DIRECTORY_FALLBACK_SERVERS[$toss];
$toss = mt_rand(0,count($directory_fallback_servers));
$preferred = $directory_fallback_servers[$toss];
set_config('system','directory_server',$preferred);
} else{
set_config('system','directory_server',z_root());
@@ -94,7 +95,7 @@ function get_directory_setting($observer, $setting) {
$ret = get_config('directory', $setting);
// 'safemode' is the default if there is no observer or no established preference.
// 'safemode' is the default if there is no observer or no established preference.
if($setting == 'safemode' && $ret === false)
$ret = 1;
@@ -152,8 +153,8 @@ function dir_sort_links() {
*
* Checks the directory mode of this hub to see if it is some form of directory server. If it is,
* get the directory realm of this hub. Fetch a list of all other directory servers in this realm and request
* a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB.
* In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored
* a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB.
* In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored
* directly if the rater's signature matches.
*
* @param int $dirmode;
@@ -189,7 +190,7 @@ function sync_directories($dirmode) {
[
'site_url' => DIRECTORY_FALLBACK_MASTER,
'site_flags' => DIRECTORY_MODE_PRIMARY,
'site_update' => NULL_DATE,
'site_update' => NULL_DATE,
'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch',
'site_realm' => DIRECTORY_REALM,
'site_valid' => 1,
@@ -335,11 +336,11 @@ function update_directory_entry($ud) {
// modify the directory search to only return zot6 entries, and also modify this function
// to *only* fetch the zot6 entries.
// Otherwise we'll be showing duplicates or have a mostly empty directory for a good chunk of
// the transition period. Directory server load will likely increase "moderately" during this transition.
// The one month counter begins when the primary directory has upgraded to a release which uses this code.
// the transition period. Directory server load will likely increase "moderately" during this transition.
// The one month counter begins when the primary directory has upgraded to a release which uses this code.
// Hubzilla channels running traditional zot which have not upgraded can or will be dropped from the directory or
// "not found" at the end of the transition period as the directory will only serve zot6 entries at that time.
$uri = Webfinger::zot_url($ud['ud_addr']);
if($uri) {
$record = Zotfinger::exec($uri);
@@ -347,8 +348,8 @@ function update_directory_entry($ud) {
// Check the HTTP signature
$hsig = $record['signature'];
if($hsig && $hsig['signer'] === $url && $hsig['header_valid'] === true && $hsig['content_valid'] === true) {
$x = \Zotlabs\Lib\Libzot::import_xchan($record['data'], 0, $ud);
if($hsig && $hsig['signer'] === $uri && $hsig['header_valid'] === true && $hsig['content_valid'] === true) {
$x = Libzot::import_xchan($record['data'], 0, $ud);
if($x['success']) {
$success = true;
}
@@ -394,7 +395,7 @@ function local_dir_update($uid, $force) {
$profile['description'] = $p[0]['pdesc'];
$profile['birthday'] = $p[0]['dob'];
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
$profile['age'] = $age;
$profile['gender'] = $p[0]['gender'];

View File

@@ -14,9 +14,6 @@
* @return string with an atom feed
*/
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\Exception\UnsatisfiedDependencyException;
function get_public_feed($channel, $params) {
if(! $params)
@@ -435,13 +432,7 @@ function get_atom_elements($feed, $item) {
$res['plink'] = unxmlify($item->get_link(0));
$res['item_rss'] = 1;
try {
$uuid = Uuid::uuid5(Uuid::NAMESPACE_URL, $res['plink'])->toString();
} catch (UnsatisfiedDependencyException $e) {
$uuid = md5($res['plink']);
}
$res['uuid'] = $uuid;
$res['uuid'] = uuid_from_url($res['plink']);
$summary = unxmlify($item->get_description(true));

View File

@@ -299,7 +299,7 @@ function load_context_help() {
break;
array_pop($args);
$path = implode($args,'/');
$path = implode('/', $args);
}
return $context_help;

View File

@@ -40,7 +40,7 @@ function breaklines($line, $level, $wraplength = 75)
$newlines[] = $line;
return(implode($newlines, "\n"));
return(implode("\n", $newlines));
}
function quotelevel($message, $wraplength = 75)
@@ -73,7 +73,7 @@ function quotelevel($message, $wraplength = 75)
if (!$startquote or ($line != ''))
$newlines[] = breaklines($line, $currlevel, $wraplength);
}
return(implode($newlines, "\n"));
return(implode("\n", $newlines));
}
function collecturls($message) {

View File

@@ -1208,6 +1208,9 @@ function sync_files($channel, $files) {
continue;
}
$term = $att['term'];
unset($att['term']);
$attach_exists = false;
$x = attach_by_hash($att['hash'],$channel['channel_hash']);
logger('sync_files duplicate check: attach_exists=' . $attach_exists, LOGGER_DEBUG);
@@ -1352,17 +1355,35 @@ function sync_files($channel, $files) {
}
$redirects = 0;
$headers = [];
$headers['Accept'] = 'application/x-zot+json' ;
$headers['Sigtoken'] = random_string();
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], 'acct:' . channel_reddress($channel),true,'sha512');
$headers = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), true, 'sha512');
$x = z_post_url($fetch_url,$parr,$redirects,[ 'filep' => $fp, 'headers' => $headers]);
fclose($fp);
if($x['success']) {
$attachment_stored = true;
$a = q("SELECT id FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($att['hash']),
intval($channel['channel_id'])
);
if($a) {
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel['channel_id']),
intval($a[0]['id']),
intval(TERM_OBJ_FILE)
);
if($term) {
foreach($term as $t) {
if(array_key_exists('type',$t))
$t['ttype'] = $t['type'];
store_item_tag($channel['channel_id'], $a[0]['id'], TERM_OBJ_FILE, $t['ttype'], escape_tags($t['term']), escape_tags($t['url']));
}
}
}
}
continue;
}
@@ -1442,7 +1463,7 @@ function sync_files($channel, $files) {
$headers = [];
$headers['Accept'] = 'application/x-zot+json' ;
$headers['Sigtoken'] = random_string();
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'],'acct:' . channel_reddress($channel),true,'sha512');
$headers = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), true, 'sha512');
$x = z_post_url($fetch_url,$parr,$redirects,[ 'filep' => $fp, 'headers' => $headers]);
fclose($fp);

View File

@@ -1551,7 +1551,7 @@ function get_mail_elements($x) {
$arr['expires'] = datetime_convert('UTC','UTC',$x['expires']);
$arr['mail_flags'] = 0;
if(array_key_exists('sig',$x))
$arr['sig'] = $x['sig'];
@@ -2206,6 +2206,7 @@ function item_store_update($arr, $allow_exec = false, $deliver = true) {
return $ret;
}
// override the unseen flag with the original
$arr['item_unseen'] = $orig[0]['item_unseen'];
@@ -2672,6 +2673,11 @@ function tag_deliver($uid, $item_id) {
}
if ($is_group && intval($item['item_private']) === 2 && intval($item['item_thread_top'])) {
// do not turn the groups own direkt messages into group items
if($item['item_wall'] && $item['author_xchan'] === $u[0]['channel_hash'])
return;
// group delivery via DM
if(perm_is_allowed($uid,$item['owner_xchan'],'post_wall') || perm_is_allowed($uid,$item['owner_xchan'],'tag_deliver')) {
logger('group DM delivery for ' . $u[0]['channel_address']);
@@ -2682,6 +2688,12 @@ function tag_deliver($uid, $item_id) {
if ($is_group && intval($item['item_thread_top']) && intval($item['item_wall']) && $item['author_xchan'] !== $item['owner_xchan']) {
if($item['resource_type'] === 'group_item') {
logger('resource_type group_item: already shared');
return;
}
if (strpos($item['body'],'[/share]')) {
logger('W2W post already shared');
return;
@@ -2813,7 +2825,7 @@ function tag_deliver($uid, $item_id) {
// standard forum tagging sequence !forumname
/*
$forumpattern = '/\!\!?\[[uz]rl\=([^\]]*?)\]((?:.(?!\[[uz]rl\=))*?)\[\/[uz]rl\]/';
$forumpattern2 = '/\[[uz]rl\=([^\]]*?)\]\!((?:.(?!\[[uz]rl\=))*?)\[\/[uz]rl\]/';
@@ -2847,6 +2859,8 @@ function tag_deliver($uid, $item_id) {
}
}
*/
if(! ($tagged || $plustagged)) {
logger('Mention was in a reshare or exceeded max_tagged_forums - ignoring');
continue;
@@ -3022,7 +3036,7 @@ function tgroup_check($uid, $item) {
}
// post to group via DM
if ($is_group) {
if (intval($item['item_private']) === 2 && $item['mid'] === $item['parent_mid']) {
return true;
@@ -3076,7 +3090,7 @@ function tgroup_check($uid, $item) {
$body = preg_replace('/\[share(.*?)\[\/share\]/','',$item['body']);
/*
$forumpattern = '/\!\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\[\/zrl\]/';
$forumpattern2 = '/\[zrl\=([^\]]*?)\]\!((?:.(?!\[zrl\=))*?)\[\/zrl\]/';
@@ -3116,6 +3130,7 @@ function tgroup_check($uid, $item) {
logger('tgroup_check: mention was in a reshare or exceeded max_tagged_forums - ignoring');
continue;
}
*/
return true;
}
@@ -3199,18 +3214,20 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false
if ($group && (! $parent)) {
$arr = [];
if ($edit) {
// process edit or delete action
$r = q("select * from item where source_xchan = '%s' and body like '%s' and uid = %d limit 1",
dbesc($item['owner_xchan']),
dbesc("%message_id='" . $item['mid'] . "'%"),
intval($channel['channel_id'])
$r = q("select * from item where uid = %d and resource_id = '%s' and source_xchan = '%s' and resource_type = 'group_item' limit 1",
intval($channel['channel_id']),
dbesc($item['mid']),
dbesc($item['author_xchan'])
);
if ($r) {
if (intval($item['item_deleted'])) {
drop_item($r[0]['id'],false,DROPITEM_PHASE1);
Master::Summon([ 'Notifier','drop',$r[0]['id'] ]);
drop_item($r[0]['id'], false, DROPITEM_PHASE1);
Master::Summon([ 'Notifier', 'drop', $r[0]['id'] ]);
return;
}
$arr['id'] = intval($r[0]['id']);
@@ -3231,29 +3248,32 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false
$arr['mid'] = z_root() . '/activity/' . $arr['uuid'];
$arr['parent_mid'] = $arr['mid'];
}
$arr['aid'] = $channel['channel_account_id'];
$arr['uid'] = $channel['channel_id'];
// WARNING: the presence of both source_xchan and non-zero item_uplink here will cause a delivery loop
$arr['item_uplink'] = 0;
$arr['source_xchan'] = $item['owner_xchan'];
$arr['resource_id'] = $item['mid'];
$arr['resource_type'] = 'group_item';
$arr['item_private'] = (($channel['channel_allow_cid'] || $channel['channel_allow_gid']
|| $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 1 : 0);
$arr['item_origin'] = 1;
$arr['item_wall'] = 1;
$arr['item_thread_top'] = 1;
if (strpos($item['body'], "[/share]") !== false) {
$pos = strpos($item['body'], "[share");
$bb = substr($item['body'], $pos);
} else {
$bb = "[share author='" . urlencode($item['author']['xchan_name']).
"' profile='" . $item['author']['xchan_url'] .
"' portable_id='" . $item['author']['xchan_hash'] .
"' portable_id='" . $item['author']['xchan_hash'] .
"' avatar='" . $item['author']['xchan_photo_s'] .
"' link='" . $item['plink'] .
"' auth='" . ((in_array($item['author']['xchan_network'], ['zot6','zot'])) ? 'true' : 'false') .
@@ -3261,12 +3281,13 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false
"' message_id='" . $item['mid'] .
"']";
if($item['title'])
$bb .= '[b]'.$item['title'].'[/b]'."\r\n";
$bb .= '[h3][b]'.$item['title'].'[/b][/h3]'."\r\n";
$bb .= $item['body'];
$bb .= "[/share]";
}
$arr['body'] = $bb;
$arr['term'] = $item['term'];
$arr['author_xchan'] = $channel['channel_hash'];
$arr['owner_xchan'] = $channel['channel_hash'];
@@ -4800,7 +4821,7 @@ function set_linkified_perms($linkified, &$str_contact_allow, &$str_group_allow,
elseif(strpos($access_tag,'gid:') === 0) {
$str_group_allow .= '<' . substr($access_tag,4) . '>';
$access_tag = '';
$private = 2;
$private = 1;
}
}
}

View File

@@ -47,10 +47,10 @@ function js_strings() {
'days' => tt('%d days', '%d days', '%d'),
'months' => tt('%d months', '%d months', '%d'),
'years' => tt('%d years', '%d years', '%d'),
// get plural function code
'plural_func' => tf(),
'$t01' => ((t('timeago.prefixAgo') == 'timeago.prefixAgo') ? '' : ((t('timeago.prefixAgo') == 'NONE') ? '' : t('timeago.prefixAgo'))),
'$t02' => ((t('timeago.prefixFromNow') == 'timeago.prefixFromNow') ? '' : ((t('timeago.prefixFromNow') == 'NONE') ? '' : t('timeago.prefixFromNow'))),
'$t03' => ((t('timeago.suffixAgo') == 'timeago.suffixAgo') ? 'ago' : ((t('timeago.suffixAgo') == 'NONE') ? '' : t('timeago.suffixAgo'))),
@@ -113,6 +113,9 @@ function js_strings() {
'$month' => t('month','calendar'),
'$week' => t('week','calendar'),
'$day' => t('day','calendar'),
'$allday' => t('All day','calendar')
'$allday' => t('All day','calendar'),
// mod cloud
'$download_info' => t('Please stand by while your download is being prepared.')
));
}

View File

@@ -714,7 +714,7 @@ function sxml2array ( $xmlObject, $out = array () )
* @brief xml2array() will convert the given XML text to an array in the XML structure.
*
* Link: http://www.bin-co.com/php/scripts/xml2array/
* Portions significantly re-written by mike@macgirvin.com
* Portions significantly re-written by mike@macgirvin.com
* (namespaces, lowercase tags, get_attribute default changed, more...)
*
* Examples: $array = xml2array(file_get_contents('feed.xml'));
@@ -1113,8 +1113,8 @@ function discover_by_webbie($webbie, $protocol = '') {
// Check the HTTP signature
$hsig = $record['signature'];
if($hsig && ($hsig['signer'] === $url || $hsig['signer'] === $link['href']) && $hsig['header_valid'] === true && $hsig['content_valid'] === true)
$hsig_valid = true;
if($hsig && $hsig['signer'] === $link['href'] && $hsig['header_valid'] === true && $hsig['content_valid'] === true)
$hsig_valid = true;
if(! $hsig_valid) {
logger('http signature not valid: ' . print_r($hsig,true));
@@ -1431,7 +1431,7 @@ function scrape_feed($url) {
function do_delivery($deliveries, $force = false) {
// $force is set if a site that wasn't responding suddenly returns to life.
// Try and shove through everything going to that site while it's responding.
// Try and shove through everything going to that site while it's responding.
if(! (is_array($deliveries) && count($deliveries)))
return;
@@ -2069,7 +2069,7 @@ function get_request_string($url) {
* Takes the output of parse_url and builds a URL from it
*
*/
function unparse_url($parsed_url) {
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
@@ -2081,4 +2081,4 @@ function unparse_url($parsed_url) {
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
}

View File

@@ -38,6 +38,10 @@ function photo_factory($data, $type = null) {
$v = Imagick::getVersion();
preg_match('/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v['versionString'], $m);
if(version_compare($m[1], '6.6.7') >= 0) {
$limits = get_config('system', 'imagick_limits', false);
if ($limits)
foreach ($limits as $k => $v)
IMagick::setResourceLimit($k, $v);
$ph = new PhotoImagick($data, $type);
} else {
// earlier imagick versions have issues with scaling png's

View File

@@ -1801,7 +1801,7 @@ function prepare_body(&$item,$attach = false,$opts = false) {
$tags = format_hashtags($item);
if($item['resource_type'])
if($item['resource_type'] == 'photo')
$mentions = format_mentions($item);
$categories = format_categories($item,$writeable);
@@ -2795,7 +2795,7 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true)
$termtype = ((strpos($tag,'#') === 0) ? TERM_HASHTAG : TERM_UNKNOWN);
$termtype = ((strpos($tag,'@') === 0) ? TERM_MENTION : $termtype);
$termtype = ((strpos($tag,'!') === 0) ? TERM_FORUM : $termtype);
// $termtype = ((strpos($tag,'!') === 0) ? TERM_FORUM : $termtype);
$termtype = ((strpos($tag,'#^[') === 0) ? TERM_BOOKMARK : $termtype);
// Is it a hashtag of some kind?
@@ -2864,7 +2864,7 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true)
if ( in_array($termtype, [ TERM_MENTION, TERM_FORUM ] )) {
// The @! tag and !! tag will alter permissions
// The @! tag will alter permissions
// $in_network is set to false to avoid false positives on posts originating
// on a network which does not implement privacy tags or implements them differently.
@@ -2966,12 +2966,13 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true)
//create profile link
$profile = str_replace(',','%2c',$profile);
$url = $profile;
/*
if($termtype === TERM_FORUM) {
$newtag = '!' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]';
$body = str_replace('!' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
}
else {
// ( $termtype === TERM_MENTION )
*/
if ($termtype === TERM_MENTION) {
$newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]';
$body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
}
@@ -2983,7 +2984,7 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true)
$str_tags .= $newtag;
}
}
$fn_results[] = [
'replaced' => $replaced,
@@ -3032,12 +3033,13 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true)
//create profile link
$profile = str_replace(',','%2c',$profile);
$url = $profile;
/*
if($termtype === TERM_FORUM) {
$newtag = '!' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]';
$body = str_replace('!' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
}
else {
// ( $termtype === TERM_MENTION )
*/
if ($termtype === TERM_MENTION) {
$newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]';
$body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
}
@@ -3060,7 +3062,7 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true)
];
}
}
return $fn_results;
}
@@ -3098,7 +3100,7 @@ function linkify_tags(&$body, $uid, $in_network = true) {
function getIconFromType($type) {
$iconMap = array(
//Folder
t('Collection') => 'fa-folder-o',
'Collection' => 'fa-folder-o',
'multipart/mixed' => 'fa-folder-o', //dirs in attach use this mime type
//Common file
'application/octet-stream' => 'fa-file-o',
@@ -3242,7 +3244,7 @@ function item_url_replace($channel,&$item,$old,$new,$oldnick = '') {
if($oldnick)
json_url_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['target']);
}
$item['body'] = preg_replace("/(\[zrl=".preg_quote($old,'/')."\/(photo|photos|gallery)\/".$channel['channel_address'].".+\]\[zmg=\d+x\d+\])".preg_quote($old,'/')."\/(.+\[\/zmg\])/", '${1}'.$new.'/${3}', $item['body']);
$item['body'] = preg_replace("/".preg_quote($old,'/')."\/(search|\w+\/".$channel['channel_address'].")/", $new.'/${1}', $item['body']);
@@ -3575,7 +3577,7 @@ function cleanup_bbcode($body) {
$body = preg_replace_callback('/\[\$b64url(.*?)\[\/(url)\]/ism','\red_unescape_codeblock',$body);
$body = preg_replace_callback('/\[\$b64code(.*?)\[\/(code)\]/ism','\red_unescape_codeblock',$body);
$body = preg_replace_callback('/\[\$b64svg(.*?)\[\/(svg)\]/ism','\red_unescape_codeblock',$body);
// fix any img tags that should be zmg
$body = preg_replace_callback('/\[img(.*?)\](.*?)\[\/img\]/ism','\red_zrlify_img_callback',$body);
@@ -3690,6 +3692,11 @@ function get_forum_channels($uid) {
);
if($x1) {
$x2 = [];
$x3 = [];
$x4 = [];
$xc = ids_to_querystr($x1,'xchan',true);
$x2 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'tag_deliver' and v = '1' and xchan in (" . $xc . ") ",
@@ -3706,6 +3713,15 @@ function get_forum_channels($uid) {
if($x3) {
$xf = ids_to_querystr(array_merge($x2,$x3),'xchan',true);
}
// public forums with no permission to post
$x4 = q("select xchan from abconfig left join xchan on xchan = xchan_hash where chan = %d and cat = 'their_perms' and k in ('post_wall', 'tag_deliver') and v = '0' and xchan in (" . $xc . ") and xchan_pubforum = 1 $sql_extra ",
intval(local_channel())
);
if($x4) {
$xf = ids_to_querystr(array_merge($x2,$x3,$x4),'xchan',true);
}
}
$sql_extra_1 = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 ");
@@ -3722,6 +3738,15 @@ function get_forum_channels($uid) {
}
}
}
if($x4) {
foreach($x4 as $xx) {
if($r[$x]['xchan_hash'] == $xx['xchan']) {
$r[$x]['no_post_perms'] = 1;
}
}
}
}
App::$data['forum_channels'] = $r;
@@ -3791,7 +3816,7 @@ function array_path_exists($str,$arr) {
/**
* @brief Generate a unique ID.
* @brief Generate a random v4 UUID.
*
* @return string
*/
@@ -3807,6 +3832,22 @@ function new_uuid() {
}
/**
* @brief Generate a name-based v5 UUID in the URL namespace
*
* @param string $url
* @return string
*/
function uuid_from_url($url) {
try {
$hash = Uuid::uuid5(Uuid::NAMESPACE_URL, $url)->toString();
} catch (UnsatisfiedDependencyException $e) {
$hash = md5($url);
}
return $hash;
}
function svg2bb($s) {
$s = preg_replace("/\<text (.*?)\>(.*?)\<(.*?)\<\/text\>/", '<text $1>$2&lt;$3</text>', $s);

View File

@@ -110,7 +110,7 @@ class ASNValue
function SetIntBuffer($Value)
{
if (strlen($Value) > 1) {
$firstByte = ord($Value{0});
$firstByte = ord($Value[0]);
if ($firstByte & 0x80) { //first bit set
$Value = chr(0x00) . $Value;
}
@@ -122,7 +122,7 @@ class ASNValue
function GetIntBuffer()
{
$result = $this->Value;
if (ord($result{0}) == 0x00) {
if (ord($result[0]) == 0x00) {
$result = substr($result, 1);
}

File diff suppressed because it is too large Load Diff

20
vendor/brick/math/LICENSE vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013-present Benjamin Morel
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

16
vendor/brick/math/SECURITY.md vendored Normal file
View File

@@ -0,0 +1,16 @@
# Security Policy
## Supported Versions
Only the latest release stream is supported.
| Version | Supported |
| ------- | ------------------ |
| 0.8.x | :white_check_mark: |
| < 0.8 | :x: |
## Reporting a Vulnerability
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.

35
vendor/brick/math/composer.json vendored Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "brick/math",
"description": "Arbitrary-precision arithmetic library",
"type": "library",
"keywords": [
"Brick",
"Math",
"Arbitrary-precision",
"Arithmetic",
"BigInteger",
"BigDecimal",
"BigRational",
"Bignum"
],
"license": "MIT",
"require": {
"php": "^7.1|^8.0",
"ext-json": "*"
},
"require-dev": {
"phpunit/phpunit": "^7.5.15|^8.5",
"php-coveralls/php-coveralls": "^2.2",
"vimeo/psalm": "^3.5"
},
"autoload": {
"psr-4": {
"Brick\\Math\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Brick\\Math\\Tests\\": "tests/"
}
}
}

40
vendor/brick/math/psalm-baseline.xml vendored Normal file
View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="3.8.5@e6ec5fa22a7b9e61670a24d07b3119aff80dcd89">
<file src="src/Internal/Calculator/BcMathCalculator.php">
<InvalidNullableReturnType occurrences="3">
<code>string</code>
<code>string</code>
<code>string</code>
</InvalidNullableReturnType>
<InvalidReturnStatement occurrences="1">
<code>[$q, $r]</code>
</InvalidReturnStatement>
<InvalidReturnType occurrences="1">
<code>array</code>
</InvalidReturnType>
<NullableReturnStatement occurrences="3">
<code>\bcdiv($a, $b, 0)</code>
<code>\bcmod($a, $b)</code>
<code>\bcpowmod($base, $exp, $mod, 0)</code>
</NullableReturnStatement>
</file>
<file src="src/Internal/Calculator/NativeCalculator.php">
<InvalidOperand occurrences="6">
<code>$a</code>
<code>$a</code>
<code>$a</code>
<code>$b</code>
<code>$blockA</code>
<code>$blockA</code>
</InvalidOperand>
<LoopInvalidation occurrences="4">
<code>$i</code>
<code>$i</code>
<code>$i</code>
<code>$j</code>
</LoopInvalidation>
<PossiblyInvalidArgument occurrences="1">
<code>$e / 2</code>
</PossiblyInvalidArgument>
</file>
</files>

56
vendor/brick/math/psalm.xml vendored Normal file
View File

@@ -0,0 +1,56 @@
<?xml version="1.0"?>
<psalm
totallyTyped="false"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<issueHandlers>
<LessSpecificReturnType errorLevel="info" />
<!-- level 3 issues - slightly lazy code writing, but provably low false-negatives -->
<DeprecatedMethod errorLevel="info" />
<DeprecatedProperty errorLevel="info" />
<DeprecatedClass errorLevel="info" />
<DeprecatedConstant errorLevel="info" />
<DeprecatedFunction errorLevel="info" />
<DeprecatedInterface errorLevel="info" />
<DeprecatedTrait errorLevel="info" />
<InternalMethod errorLevel="info" />
<InternalProperty errorLevel="info" />
<InternalClass errorLevel="info" />
<MissingClosureReturnType errorLevel="info" />
<MissingReturnType errorLevel="info" />
<MissingPropertyType errorLevel="info" />
<InvalidDocblock errorLevel="info" />
<MisplacedRequiredParam errorLevel="info" />
<PropertyNotSetInConstructor errorLevel="info" />
<MissingConstructor errorLevel="info" />
<MissingClosureParamType errorLevel="info" />
<MissingParamType errorLevel="info" />
<RedundantCondition errorLevel="info" />
<DocblockTypeContradiction errorLevel="info" />
<RedundantConditionGivenDocblockType errorLevel="info" />
<UnresolvableInclude errorLevel="info" />
<RawObjectIteration errorLevel="info" />
<InvalidStringClass errorLevel="info" />
</issueHandlers>
</psalm>

184
vendor/brick/math/random-tests.php vendored Normal file
View File

@@ -0,0 +1,184 @@
<?php
/**
* This script stress tests calculators with random large numbers and ensures that all implementations return the same
* results. It is designed to run in an infinite loop unless a bug is found.
*/
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use Brick\Math\Internal\Calculator;
(new class(30) { // max digits
private $gmp;
private $bcmath;
private $native;
private $maxDigits;
public function __construct(int $maxDigits)
{
$this->gmp = new Calculator\GmpCalculator();
$this->bcmath = new Calculator\BcMathCalculator();
$this->native = new Calculator\NativeCalculator();
$this->maxDigits = $maxDigits;
}
public function __invoke() : void
{
for (;;) {
$a = $this->generateRandomNumber();
$b = $this->generateRandomNumber();
$c = $this->generateRandomNumber();
$this->runTests($a, $b);
$this->runTests($b, $a);
if ($a !== '0') {
$this->runTests("-$a", $b);
$this->runTests($b, "-$a");
}
if ($b !== '0') {
$this->runTests($a, "-$b");
$this->runTests("-$b", $a);
}
if ($a !== '0' && $b !== '0') {
$this->runTests("-$a", "-$b");
$this->runTests("-$b", "-$a");
}
if ($c !== '0') {
$this->test("$a POW $b MOD $c", function(Calculator $calc) use($a, $b, $c) {
return $calc->modPow($a, $b, $c);
});
}
}
}
/**
* @param string $a The left operand.
* @param string $b The right operand.
*/
function runTests(string $a, string $b) : void
{
$this->test("$a + $b", function(Calculator $c) use($a, $b) {
return $c->add($a, $b);
});
$this->test("$a - $b", function(Calculator $c) use($a, $b) {
return $c->sub($a, $b);
});
$this->test("$a * $b", function(Calculator $c) use($a, $b) {
return $c->mul($a, $b);
});
if ($b !== '0') {
$this->test("$a / $b", function(Calculator $c) use($a, $b) {
return $c->divQR($a, $b);
});
$this->test("$a MOD $b", function(Calculator $c) use($a, $b) {
return $c->mod($a, $b);
});
}
if ($b !== '0' && $b[0] !== '-') {
$this->test("INV $a MOD $b", function(Calculator $c) use($a, $b) {
return $c->modInverse($a, $b);
});
}
$this->test("GCD $a, $b", function(Calculator $c) use($a, $b) {
return $c->gcd($a, $b);
});
if ($a[0] !== '-') {
$this->test("SQRT $a", function(Calculator $c) use($a, $b) {
return $c->sqrt($a);
});
}
$this->test("$a AND $b", function(Calculator $c) use($a, $b) {
return $c->and($a, $b);
});
$this->test("$a OR $b", function(Calculator $c) use($a, $b) {
return $c->or($a, $b);
});
$this->test("$a XOR $b", function(Calculator $c) use($a, $b) {
return $c->xor($a, $b);
});
}
/**
* @param string $test A string representing the test being executed.
* @param Closure $callback A callback function accepting a Calculator instance and returning a calculation result.
*/
private function test(string $test, Closure $callback) : void
{
static $counter = 0;
static $lastOutputTime = null;
$gmpResult = $callback($this->gmp);
$bcmathResult = $callback($this->bcmath);
$nativeResult = $callback($this->native);
if ($gmpResult !== $bcmathResult) {
self::failure('GMP', 'BCMath', $test);
}
if ($gmpResult !== $nativeResult) {
self::failure('GMP', 'Native', $test);
}
$counter++;
$time = microtime(true);
if ($lastOutputTime === null) {
$lastOutputTime = $time;
} elseif ($time - $lastOutputTime >= 0.1) {
echo "\r", number_format($counter);
$lastOutputTime = $time;
}
}
/**
* @param string $c1 The name of the first calculator.
* @param string $c2 The name of the second calculator.
* @param string $test A string representing the test being executed.
*/
private static function failure(string $c1, string $c2, string $test) : void
{
echo PHP_EOL;
echo 'FAILURE!', PHP_EOL;
echo $c1, ' vs ', $c2, PHP_EOL;
echo $test, PHP_EOL;
die;
}
private function generateRandomNumber() : string
{
$length = random_int(1, $this->maxDigits);
$number = '';
for ($i = 0; $i < $length; $i++) {
$number .= random_int(0, 9);
}
$number = ltrim($number, '0');
if ($number === '') {
return '0';
}
return $number;
}
})();

855
vendor/brick/math/src/BigDecimal.php vendored Normal file
View File

@@ -0,0 +1,855 @@
<?php
declare(strict_types=1);
namespace Brick\Math;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NegativeNumberException;
use Brick\Math\Internal\Calculator;
/**
* Immutable, arbitrary-precision signed decimal numbers.
*
* @psalm-immutable
*/
final class BigDecimal extends BigNumber
{
/**
* The unscaled value of this decimal number.
*
* This is a string of digits with an optional leading minus sign.
* No leading zero must be present.
* No leading minus sign must be present if the value is 0.
*
* @var string
*/
private $value;
/**
* The scale (number of digits after the decimal point) of this decimal number.
*
* This must be zero or more.
*
* @var int
*/
private $scale;
/**
* Protected constructor. Use a factory method to obtain an instance.
*
* @param string $value The unscaled value, validated.
* @param int $scale The scale, validated.
*/
protected function __construct(string $value, int $scale = 0)
{
$this->value = $value;
$this->scale = $scale;
}
/**
* Creates a BigDecimal of the given value.
*
* @param BigNumber|int|float|string $value
*
* @return BigDecimal
*
* @throws MathException If the value cannot be converted to a BigDecimal.
*
* @psalm-pure
*/
public static function of($value) : BigNumber
{
return parent::of($value)->toBigDecimal();
}
/**
* Creates a BigDecimal from an unscaled value and a scale.
*
* Example: `(12345, 3)` will result in the BigDecimal `12.345`.
*
* @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
* @param int $scale The scale of the number, positive or zero.
*
* @return BigDecimal
*
* @throws \InvalidArgumentException If the scale is negative.
*
* @psalm-pure
*/
public static function ofUnscaledValue($value, int $scale = 0) : BigDecimal
{
if ($scale < 0) {
throw new \InvalidArgumentException('The scale cannot be negative.');
}
return new BigDecimal((string) BigInteger::of($value), $scale);
}
/**
* Returns a BigDecimal representing zero, with a scale of zero.
*
* @return BigDecimal
*
* @psalm-pure
*/
public static function zero() : BigDecimal
{
/** @psalm-suppress ImpureStaticVariable */
static $zero;
if ($zero === null) {
$zero = new BigDecimal('0');
}
return $zero;
}
/**
* Returns a BigDecimal representing one, with a scale of zero.
*
* @return BigDecimal
*
* @psalm-pure
*/
public static function one() : BigDecimal
{
/** @psalm-suppress ImpureStaticVariable */
static $one;
if ($one === null) {
$one = new BigDecimal('1');
}
return $one;
}
/**
* Returns a BigDecimal representing ten, with a scale of zero.
*
* @return BigDecimal
*
* @psalm-pure
*/
public static function ten() : BigDecimal
{
/** @psalm-suppress ImpureStaticVariable */
static $ten;
if ($ten === null) {
$ten = new BigDecimal('10');
}
return $ten;
}
/**
* Returns the sum of this number and the given one.
*
* The result has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
*
* @return BigDecimal The result.
*
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
*/
public function plus($that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->value === '0' && $that->scale <= $this->scale) {
return $this;
}
if ($this->value === '0' && $this->scale <= $that->scale) {
return $that;
}
[$a, $b] = $this->scaleValues($this, $that);
$value = Calculator::get()->add($a, $b);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
return new BigDecimal($value, $scale);
}
/**
* Returns the difference of this number and the given one.
*
* The result has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
*
* @return BigDecimal The result.
*
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
*/
public function minus($that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->value === '0' && $that->scale <= $this->scale) {
return $this;
}
[$a, $b] = $this->scaleValues($this, $that);
$value = Calculator::get()->sub($a, $b);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
return new BigDecimal($value, $scale);
}
/**
* Returns the product of this number and the given one.
*
* The result has a scale of `$this->scale + $that->scale`.
*
* @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
*
* @return BigDecimal The result.
*
* @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
*/
public function multipliedBy($that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->value === '1' && $that->scale === 0) {
return $this;
}
if ($this->value === '1' && $this->scale === 0) {
return $that;
}
$value = Calculator::get()->mul($this->value, $that->value);
$scale = $this->scale + $that->scale;
return new BigDecimal($value, $scale);
}
/**
* Returns the result of the division of this number by the given one, at the given scale.
*
* @param BigNumber|int|float|string $that The divisor.
* @param int|null $scale The desired scale, or null to use the scale of this number.
* @param int $roundingMode An optional rounding mode.
*
* @return BigDecimal
*
* @throws \InvalidArgumentException If the scale or rounding mode is invalid.
* @throws MathException If the number is invalid, is zero, or rounding was necessary.
*/
public function dividedBy($that, ?int $scale = null, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}
if ($scale === null) {
$scale = $this->scale;
} elseif ($scale < 0) {
throw new \InvalidArgumentException('Scale cannot be negative.');
}
if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
return $this;
}
$p = $this->valueWithMinScale($that->scale + $scale);
$q = $that->valueWithMinScale($this->scale - $scale);
$result = Calculator::get()->divRound($p, $q, $roundingMode);
return new BigDecimal($result, $scale);
}
/**
* Returns the exact result of the division of this number by the given one.
*
* The scale of the result is automatically calculated to fit all the fraction digits.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @return BigDecimal The result.
*
* @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
* or the result yields an infinite number of digits.
*/
public function exactlyDividedBy($that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->value === '0') {
throw DivisionByZeroException::divisionByZero();
}
[, $b] = $this->scaleValues($this, $that);
$d = \rtrim($b, '0');
$scale = \strlen($b) - \strlen($d);
$calculator = Calculator::get();
foreach ([5, 2] as $prime) {
for (;;) {
$lastDigit = (int) $d[-1];
if ($lastDigit % $prime !== 0) {
break;
}
$d = $calculator->divQ($d, (string) $prime);
$scale++;
}
}
return $this->dividedBy($that, $scale)->stripTrailingZeros();
}
/**
* Returns this number exponentiated to the given value.
*
* The result has a scale of `$this->scale * $exponent`.
*
* @param int $exponent The exponent.
*
* @return BigDecimal The result.
*
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
*/
public function power(int $exponent) : BigDecimal
{
if ($exponent === 0) {
return BigDecimal::one();
}
if ($exponent === 1) {
return $this;
}
if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
throw new \InvalidArgumentException(\sprintf(
'The exponent %d is not in the range 0 to %d.',
$exponent,
Calculator::MAX_POWER
));
}
return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
}
/**
* Returns the quotient of the division of this number by this given one.
*
* The quotient has a scale of `0`.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @return BigDecimal The quotient.
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function quotient($that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}
$p = $this->valueWithMinScale($that->scale);
$q = $that->valueWithMinScale($this->scale);
$quotient = Calculator::get()->divQ($p, $q);
return new BigDecimal($quotient, 0);
}
/**
* Returns the remainder of the division of this number by this given one.
*
* The remainder has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @return BigDecimal The remainder.
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function remainder($that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}
$p = $this->valueWithMinScale($that->scale);
$q = $that->valueWithMinScale($this->scale);
$remainder = Calculator::get()->divR($p, $q);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
return new BigDecimal($remainder, $scale);
}
/**
* Returns the quotient and remainder of the division of this number by the given one.
*
* The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @return BigDecimal[] An array containing the quotient and the remainder.
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function quotientAndRemainder($that) : array
{
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}
$p = $this->valueWithMinScale($that->scale);
$q = $that->valueWithMinScale($this->scale);
[$quotient, $remainder] = Calculator::get()->divQR($p, $q);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
$quotient = new BigDecimal($quotient, 0);
$remainder = new BigDecimal($remainder, $scale);
return [$quotient, $remainder];
}
/**
* Returns the square root of this number, rounded down to the given number of decimals.
*
* @param int $scale
*
* @return BigDecimal
*
* @throws \InvalidArgumentException If the scale is negative.
* @throws NegativeNumberException If this number is negative.
*/
public function sqrt(int $scale) : BigDecimal
{
if ($scale < 0) {
throw new \InvalidArgumentException('Scale cannot be negative.');
}
if ($this->value === '0') {
return new BigDecimal('0', $scale);
}
if ($this->value[0] === '-') {
throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
}
$value = $this->value;
$addDigits = 2 * $scale - $this->scale;
if ($addDigits > 0) {
// add zeros
$value .= \str_repeat('0', $addDigits);
} elseif ($addDigits < 0) {
// trim digits
if (-$addDigits >= \strlen($this->value)) {
// requesting a scale too low, will always yield a zero result
return new BigDecimal('0', $scale);
}
$value = \substr($value, 0, $addDigits);
}
$value = Calculator::get()->sqrt($value);
return new BigDecimal($value, $scale);
}
/**
* Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
*
* @param int $n
*
* @return BigDecimal
*/
public function withPointMovedLeft(int $n) : BigDecimal
{
if ($n === 0) {
return $this;
}
if ($n < 0) {
return $this->withPointMovedRight(-$n);
}
return new BigDecimal($this->value, $this->scale + $n);
}
/**
* Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
*
* @param int $n
*
* @return BigDecimal
*/
public function withPointMovedRight(int $n) : BigDecimal
{
if ($n === 0) {
return $this;
}
if ($n < 0) {
return $this->withPointMovedLeft(-$n);
}
$value = $this->value;
$scale = $this->scale - $n;
if ($scale < 0) {
if ($value !== '0') {
$value .= \str_repeat('0', -$scale);
}
$scale = 0;
}
return new BigDecimal($value, $scale);
}
/**
* Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
*
* @return BigDecimal
*/
public function stripTrailingZeros() : BigDecimal
{
if ($this->scale === 0) {
return $this;
}
$trimmedValue = \rtrim($this->value, '0');
if ($trimmedValue === '') {
return BigDecimal::zero();
}
$trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);
if ($trimmableZeros === 0) {
return $this;
}
if ($trimmableZeros > $this->scale) {
$trimmableZeros = $this->scale;
}
$value = \substr($this->value, 0, -$trimmableZeros);
$scale = $this->scale - $trimmableZeros;
return new BigDecimal($value, $scale);
}
/**
* Returns the absolute value of this number.
*
* @return BigDecimal
*/
public function abs() : BigDecimal
{
return $this->isNegative() ? $this->negated() : $this;
}
/**
* Returns the negated value of this number.
*
* @return BigDecimal
*/
public function negated() : BigDecimal
{
return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
}
/**
* {@inheritdoc}
*/
public function compareTo($that) : int
{
$that = BigNumber::of($that);
if ($that instanceof BigInteger) {
$that = $that->toBigDecimal();
}
if ($that instanceof BigDecimal) {
[$a, $b] = $this->scaleValues($this, $that);
return Calculator::get()->cmp($a, $b);
}
return - $that->compareTo($this);
}
/**
* {@inheritdoc}
*/
public function getSign() : int
{
return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
}
/**
* @return BigInteger
*/
public function getUnscaledValue() : BigInteger
{
return BigInteger::create($this->value);
}
/**
* @return int
*/
public function getScale() : int
{
return $this->scale;
}
/**
* Returns a string representing the integral part of this decimal number.
*
* Example: `-123.456` => `-123`.
*
* @return string
*/
public function getIntegralPart() : string
{
if ($this->scale === 0) {
return $this->value;
}
$value = $this->getUnscaledValueWithLeadingZeros();
return \substr($value, 0, -$this->scale);
}
/**
* Returns a string representing the fractional part of this decimal number.
*
* If the scale is zero, an empty string is returned.
*
* Examples: `-123.456` => '456', `123` => ''.
*
* @return string
*/
public function getFractionalPart() : string
{
if ($this->scale === 0) {
return '';
}
$value = $this->getUnscaledValueWithLeadingZeros();
return \substr($value, -$this->scale);
}
/**
* Returns whether this decimal number has a non-zero fractional part.
*
* @return bool
*/
public function hasNonZeroFractionalPart() : bool
{
return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
}
/**
* {@inheritdoc}
*/
public function toBigInteger() : BigInteger
{
if ($this->scale === 0) {
$zeroScaleDecimal = $this;
} else {
$zeroScaleDecimal = $this->dividedBy(1, 0);
}
return BigInteger::create($zeroScaleDecimal->value);
}
/**
* {@inheritdoc}
*/
public function toBigDecimal() : BigDecimal
{
return $this;
}
/**
* {@inheritdoc}
*/
public function toBigRational() : BigRational
{
$numerator = BigInteger::create($this->value);
$denominator = BigInteger::create('1' . \str_repeat('0', $this->scale));
return BigRational::create($numerator, $denominator, false);
}
/**
* {@inheritdoc}
*/
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
if ($scale === $this->scale) {
return $this;
}
return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
}
/**
* {@inheritdoc}
*/
public function toInt() : int
{
return $this->toBigInteger()->toInt();
}
/**
* {@inheritdoc}
*/
public function toFloat() : float
{
return (float) (string) $this;
}
/**
* {@inheritdoc}
*/
public function __toString() : string
{
if ($this->scale === 0) {
return $this->value;
}
$value = $this->getUnscaledValueWithLeadingZeros();
return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
}
/**
* This method is required by interface Serializable and SHOULD NOT be accessed directly.
*
* @internal
*
* @return string
*/
public function serialize() : string
{
return $this->value . ':' . $this->scale;
}
/**
* This method is only here to implement interface Serializable and cannot be accessed directly.
*
* @internal
*
* @param string $value
*
* @return void
*
* @throws \LogicException
*/
public function unserialize($value) : void
{
if (isset($this->value)) {
throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
}
[$value, $scale] = \explode(':', $value);
$this->value = $value;
$this->scale = (int) $scale;
}
/**
* Puts the internal values of the given decimal numbers on the same scale.
*
* @param BigDecimal $x The first decimal number.
* @param BigDecimal $y The second decimal number.
*
* @return array{0: string, 1: string} The scaled integer values of $x and $y.
*/
private function scaleValues(BigDecimal $x, BigDecimal $y) : array
{
$a = $x->value;
$b = $y->value;
if ($b !== '0' && $x->scale > $y->scale) {
$b .= \str_repeat('0', $x->scale - $y->scale);
} elseif ($a !== '0' && $x->scale < $y->scale) {
$a .= \str_repeat('0', $y->scale - $x->scale);
}
return [$a, $b];
}
/**
* @param int $scale
*
* @return string
*/
private function valueWithMinScale(int $scale) : string
{
$value = $this->value;
if ($this->value !== '0' && $scale > $this->scale) {
$value .= \str_repeat('0', $scale - $this->scale);
}
return $value;
}
/**
* Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
*
* @return string
*/
private function getUnscaledValueWithLeadingZeros() : string
{
$value = $this->value;
$targetLength = $this->scale + 1;
$negative = ($value[0] === '-');
$length = \strlen($value);
if ($negative) {
$length--;
}
if ($length >= $targetLength) {
return $this->value;
}
if ($negative) {
$value = \substr($value, 1);
}
$value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);
if ($negative) {
$value = '-' . $value;
}
return $value;
}
}

1134
vendor/brick/math/src/BigInteger.php vendored Normal file

File diff suppressed because it is too large Load Diff

566
vendor/brick/math/src/BigNumber.php vendored Normal file
View File

@@ -0,0 +1,566 @@
<?php
declare(strict_types=1);
namespace Brick\Math;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\RoundingNecessaryException;
/**
* Common interface for arbitrary-precision rational numbers.
*
* @psalm-immutable
*/
abstract class BigNumber implements \Serializable, \JsonSerializable
{
/**
* The regular expression used to parse integer, decimal and rational numbers.
*/
private const PARSE_REGEXP =
'/^' .
'(?<sign>[\-\+])?' .
'(?:' .
'(?:' .
'(?<integral>[0-9]+)?' .
'(?<point>\.)?' .
'(?<fractional>[0-9]+)?' .
'(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
')|(?:' .
'(?<numerator>[0-9]+)' .
'\/?' .
'(?<denominator>[0-9]+)' .
')' .
')' .
'$/';
/**
* Creates a BigNumber of the given value.
*
* The concrete return type is dependent on the given value, with the following rules:
*
* - BigNumber instances are returned as is
* - integer numbers are returned as BigInteger
* - floating point numbers are converted to a string then parsed as such
* - strings containing a `/` character are returned as BigRational
* - strings containing a `.` character or using an exponential notation are returned as BigDecimal
* - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
*
* @param BigNumber|int|float|string $value
*
* @return BigNumber
*
* @throws NumberFormatException If the format of the number is not valid.
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
*
* @psalm-pure
*/
public static function of($value) : BigNumber
{
if ($value instanceof BigNumber) {
return $value;
}
if (\is_int($value)) {
return new BigInteger((string) $value);
}
if (\is_float($value)) {
$value = self::floatToString($value);
} else {
$value = (string) $value;
}
$throw = function() use ($value) : void {
throw new NumberFormatException(\sprintf(
'The given value "%s" does not represent a valid number.',
$value
));
};
if (\preg_match(self::PARSE_REGEXP, $value, $matches) !== 1) {
$throw();
}
$getMatch = function(string $value) use ($matches) : ?string {
return isset($matches[$value]) && $matches[$value] !== '' ? $matches[$value] : null;
};
$sign = $getMatch('sign');
$numerator = $getMatch('numerator');
$denominator = $getMatch('denominator');
if ($numerator !== null) {
$numerator = self::cleanUp($sign . $numerator);
$denominator = self::cleanUp($denominator);
if ($denominator === '0') {
throw DivisionByZeroException::denominatorMustNotBeZero();
}
return new BigRational(
new BigInteger($numerator),
new BigInteger($denominator),
false
);
}
$point = $getMatch('point');
$integral = $getMatch('integral');
$fractional = $getMatch('fractional');
$exponent = $getMatch('exponent');
if ($integral === null && $fractional === null) {
$throw();
}
if ($integral === null) {
$integral = '0';
}
if ($point !== null || $exponent !== null) {
$fractional = $fractional ?? '';
$exponent = $exponent !== null ? (int) $exponent : 0;
if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
throw new NumberFormatException('Exponent too large.');
}
$unscaledValue = self::cleanUp($sign . $integral . $fractional);
$scale = \strlen($fractional) - $exponent;
if ($scale < 0) {
if ($unscaledValue !== '0') {
$unscaledValue .= \str_repeat('0', - $scale);
}
$scale = 0;
}
return new BigDecimal($unscaledValue, $scale);
}
$integral = self::cleanUp($sign . $integral);
return new BigInteger($integral);
}
/**
* Safely converts float to string, avoiding locale-dependent issues.
*
* @see https://github.com/brick/math/pull/20
*
* @param float $float
*
* @return string
*
* @psalm-pure
* @psalm-suppress ImpureFunctionCall
*/
private static function floatToString(float $float) : string
{
$currentLocale = \setlocale(LC_NUMERIC, '0');
\setlocale(LC_NUMERIC, 'C');
$result = (string) $float;
\setlocale(LC_NUMERIC, $currentLocale);
return $result;
}
/**
* Proxy method to access protected constructors from sibling classes.
*
* @internal
*
* @param mixed ...$args The arguments to the constructor.
*
* @return static
*
* @psalm-pure
*/
protected static function create(... $args) : BigNumber
{
/** @psalm-suppress TooManyArguments */
return new static(... $args);
}
/**
* Returns the minimum of the given values.
*
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
* to an instance of the class this method is called on.
*
* @return static The minimum value.
*
* @throws \InvalidArgumentException If no values are given.
* @throws MathException If an argument is not valid.
*
* @psalm-pure
*/
public static function min(...$values) : BigNumber
{
$min = null;
foreach ($values as $value) {
$value = static::of($value);
if ($min === null || $value->isLessThan($min)) {
$min = $value;
}
}
if ($min === null) {
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
}
return $min;
}
/**
* Returns the maximum of the given values.
*
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
* to an instance of the class this method is called on.
*
* @return static The maximum value.
*
* @throws \InvalidArgumentException If no values are given.
* @throws MathException If an argument is not valid.
*
* @psalm-pure
*/
public static function max(...$values) : BigNumber
{
$max = null;
foreach ($values as $value) {
$value = static::of($value);
if ($max === null || $value->isGreaterThan($max)) {
$max = $value;
}
}
if ($max === null) {
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
}
return $max;
}
/**
* Returns the sum of the given values.
*
* @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
* to an instance of the class this method is called on.
*
* @return static The sum.
*
* @throws \InvalidArgumentException If no values are given.
* @throws MathException If an argument is not valid.
*
* @psalm-pure
*/
public static function sum(...$values) : BigNumber
{
/** @var BigNumber|null $sum */
$sum = null;
foreach ($values as $value) {
$value = static::of($value);
if ($sum === null) {
$sum = $value;
} else {
$sum = self::add($sum, $value);
}
}
if ($sum === null) {
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
}
return $sum;
}
/**
* Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
*
* @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
* concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
* depending on their ability to perform the operation. This will also require a version bump because we're
* potentially breaking custom BigNumber implementations (if any...)
*
* @param BigNumber $a
* @param BigNumber $b
*
* @return BigNumber
*
* @psalm-pure
*/
private static function add(BigNumber $a, BigNumber $b) : BigNumber
{
if ($a instanceof BigRational) {
return $a->plus($b);
}
if ($b instanceof BigRational) {
return $b->plus($a);
}
if ($a instanceof BigDecimal) {
return $a->plus($b);
}
if ($b instanceof BigDecimal) {
return $b->plus($a);
}
/** @var BigInteger $a */
return $a->plus($b);
}
/**
* Removes optional leading zeros and + sign from the given number.
*
* @param string $number The number, validated as a non-empty string of digits with optional leading sign.
*
* @return string
*
* @psalm-pure
*/
private static function cleanUp(string $number) : string
{
$firstChar = $number[0];
if ($firstChar === '+' || $firstChar === '-') {
$number = \substr($number, 1);
}
$number = \ltrim($number, '0');
if ($number === '') {
return '0';
}
if ($firstChar === '-') {
return '-' . $number;
}
return $number;
}
/**
* Checks if this number is equal to the given one.
*
* @param BigNumber|int|float|string $that
*
* @return bool
*/
public function isEqualTo($that) : bool
{
return $this->compareTo($that) === 0;
}
/**
* Checks if this number is strictly lower than the given one.
*
* @param BigNumber|int|float|string $that
*
* @return bool
*/
public function isLessThan($that) : bool
{
return $this->compareTo($that) < 0;
}
/**
* Checks if this number is lower than or equal to the given one.
*
* @param BigNumber|int|float|string $that
*
* @return bool
*/
public function isLessThanOrEqualTo($that) : bool
{
return $this->compareTo($that) <= 0;
}
/**
* Checks if this number is strictly greater than the given one.
*
* @param BigNumber|int|float|string $that
*
* @return bool
*/
public function isGreaterThan($that) : bool
{
return $this->compareTo($that) > 0;
}
/**
* Checks if this number is greater than or equal to the given one.
*
* @param BigNumber|int|float|string $that
*
* @return bool
*/
public function isGreaterThanOrEqualTo($that) : bool
{
return $this->compareTo($that) >= 0;
}
/**
* Checks if this number equals zero.
*
* @return bool
*/
public function isZero() : bool
{
return $this->getSign() === 0;
}
/**
* Checks if this number is strictly negative.
*
* @return bool
*/
public function isNegative() : bool
{
return $this->getSign() < 0;
}
/**
* Checks if this number is negative or zero.
*
* @return bool
*/
public function isNegativeOrZero() : bool
{
return $this->getSign() <= 0;
}
/**
* Checks if this number is strictly positive.
*
* @return bool
*/
public function isPositive() : bool
{
return $this->getSign() > 0;
}
/**
* Checks if this number is positive or zero.
*
* @return bool
*/
public function isPositiveOrZero() : bool
{
return $this->getSign() >= 0;
}
/**
* Returns the sign of this number.
*
* @return int -1 if the number is negative, 0 if zero, 1 if positive.
*/
abstract public function getSign() : int;
/**
* Compares this number to the given one.
*
* @param BigNumber|int|float|string $that
*
* @return int [-1,0,1] If `$this` is lower than, equal to, or greater than `$that`.
*
* @throws MathException If the number is not valid.
*/
abstract public function compareTo($that) : int;
/**
* Converts this number to a BigInteger.
*
* @return BigInteger The converted number.
*
* @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
*/
abstract public function toBigInteger() : BigInteger;
/**
* Converts this number to a BigDecimal.
*
* @return BigDecimal The converted number.
*
* @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
*/
abstract public function toBigDecimal() : BigDecimal;
/**
* Converts this number to a BigRational.
*
* @return BigRational The converted number.
*/
abstract public function toBigRational() : BigRational;
/**
* Converts this number to a BigDecimal with the given scale, using rounding if necessary.
*
* @param int $scale The scale of the resulting `BigDecimal`.
* @param int $roundingMode A `RoundingMode` constant.
*
* @return BigDecimal
*
* @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
* This only applies when RoundingMode::UNNECESSARY is used.
*/
abstract public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;
/**
* Returns the exact value of this number as a native integer.
*
* If this number cannot be converted to a native integer without losing precision, an exception is thrown.
* Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
*
* @return int The converted value.
*
* @throws MathException If this number cannot be exactly converted to a native integer.
*/
abstract public function toInt() : int;
/**
* Returns an approximation of this number as a floating-point value.
*
* Note that this method can discard information as the precision of a floating-point value
* is inherently limited.
*
* If the number is greater than the largest representable floating point number, positive infinity is returned.
* If the number is less than the smallest representable floating point number, negative infinity is returned.
*
* @return float The converted value.
*/
abstract public function toFloat() : float;
/**
* Returns a string representation of this number.
*
* The output of this method can be parsed by the `of()` factory method;
* this will yield an object equal to this one, without any information loss.
*
* @return string
*/
abstract public function __toString() : string;
/**
* {@inheritdoc}
*/
public function jsonSerialize() : string
{
return $this->__toString();
}
}

479
vendor/brick/math/src/BigRational.php vendored Normal file
View File

@@ -0,0 +1,479 @@
<?php
declare(strict_types=1);
namespace Brick\Math;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\RoundingNecessaryException;
/**
* An arbitrarily large rational number.
*
* This class is immutable.
*
* @psalm-immutable
*/
final class BigRational extends BigNumber
{
/**
* The numerator.
*
* @var BigInteger
*/
private $numerator;
/**
* The denominator. Always strictly positive.
*
* @var BigInteger
*/
private $denominator;
/**
* Protected constructor. Use a factory method to obtain an instance.
*
* @param BigInteger $numerator The numerator.
* @param BigInteger $denominator The denominator.
* @param bool $checkDenominator Whether to check the denominator for negative and zero.
*
* @throws DivisionByZeroException If the denominator is zero.
*/
protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator)
{
if ($checkDenominator) {
if ($denominator->isZero()) {
throw DivisionByZeroException::denominatorMustNotBeZero();
}
if ($denominator->isNegative()) {
$numerator = $numerator->negated();
$denominator = $denominator->negated();
}
}
$this->numerator = $numerator;
$this->denominator = $denominator;
}
/**
* Creates a BigRational of the given value.
*
* @param BigNumber|int|float|string $value
*
* @return BigRational
*
* @throws MathException If the value cannot be converted to a BigRational.
*
* @psalm-pure
*/
public static function of($value) : BigNumber
{
return parent::of($value)->toBigRational();
}
/**
* Creates a BigRational out of a numerator and a denominator.
*
* If the denominator is negative, the signs of both the numerator and the denominator
* will be inverted to ensure that the denominator is always positive.
*
* @param BigNumber|int|float|string $numerator The numerator. Must be convertible to a BigInteger.
* @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger.
*
* @return BigRational
*
* @throws NumberFormatException If an argument does not represent a valid number.
* @throws RoundingNecessaryException If an argument represents a non-integer number.
* @throws DivisionByZeroException If the denominator is zero.
*
* @psalm-pure
*/
public static function nd($numerator, $denominator) : BigRational
{
$numerator = BigInteger::of($numerator);
$denominator = BigInteger::of($denominator);
return new BigRational($numerator, $denominator, true);
}
/**
* Returns a BigRational representing zero.
*
* @return BigRational
*
* @psalm-pure
*/
public static function zero() : BigRational
{
/** @psalm-suppress ImpureStaticVariable */
static $zero;
if ($zero === null) {
$zero = new BigRational(BigInteger::zero(), BigInteger::one(), false);
}
return $zero;
}
/**
* Returns a BigRational representing one.
*
* @return BigRational
*
* @psalm-pure
*/
public static function one() : BigRational
{
/** @psalm-suppress ImpureStaticVariable */
static $one;
if ($one === null) {
$one = new BigRational(BigInteger::one(), BigInteger::one(), false);
}
return $one;
}
/**
* Returns a BigRational representing ten.
*
* @return BigRational
*
* @psalm-pure
*/
public static function ten() : BigRational
{
/** @psalm-suppress ImpureStaticVariable */
static $ten;
if ($ten === null) {
$ten = new BigRational(BigInteger::ten(), BigInteger::one(), false);
}
return $ten;
}
/**
* @return BigInteger
*/
public function getNumerator() : BigInteger
{
return $this->numerator;
}
/**
* @return BigInteger
*/
public function getDenominator() : BigInteger
{
return $this->denominator;
}
/**
* Returns the quotient of the division of the numerator by the denominator.
*
* @return BigInteger
*/
public function quotient() : BigInteger
{
return $this->numerator->quotient($this->denominator);
}
/**
* Returns the remainder of the division of the numerator by the denominator.
*
* @return BigInteger
*/
public function remainder() : BigInteger
{
return $this->numerator->remainder($this->denominator);
}
/**
* Returns the quotient and remainder of the division of the numerator by the denominator.
*
* @return BigInteger[]
*/
public function quotientAndRemainder() : array
{
return $this->numerator->quotientAndRemainder($this->denominator);
}
/**
* Returns the sum of this number and the given one.
*
* @param BigNumber|int|float|string $that The number to add.
*
* @return BigRational The result.
*
* @throws MathException If the number is not valid.
*/
public function plus($that) : BigRational
{
$that = BigRational::of($that);
$numerator = $this->numerator->multipliedBy($that->denominator);
$numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator));
$denominator = $this->denominator->multipliedBy($that->denominator);
return new BigRational($numerator, $denominator, false);
}
/**
* Returns the difference of this number and the given one.
*
* @param BigNumber|int|float|string $that The number to subtract.
*
* @return BigRational The result.
*
* @throws MathException If the number is not valid.
*/
public function minus($that) : BigRational
{
$that = BigRational::of($that);
$numerator = $this->numerator->multipliedBy($that->denominator);
$numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator));
$denominator = $this->denominator->multipliedBy($that->denominator);
return new BigRational($numerator, $denominator, false);
}
/**
* Returns the product of this number and the given one.
*
* @param BigNumber|int|float|string $that The multiplier.
*
* @return BigRational The result.
*
* @throws MathException If the multiplier is not a valid number.
*/
public function multipliedBy($that) : BigRational
{
$that = BigRational::of($that);
$numerator = $this->numerator->multipliedBy($that->numerator);
$denominator = $this->denominator->multipliedBy($that->denominator);
return new BigRational($numerator, $denominator, false);
}
/**
* Returns the result of the division of this number by the given one.
*
* @param BigNumber|int|float|string $that The divisor.
*
* @return BigRational The result.
*
* @throws MathException If the divisor is not a valid number, or is zero.
*/
public function dividedBy($that) : BigRational
{
$that = BigRational::of($that);
$numerator = $this->numerator->multipliedBy($that->denominator);
$denominator = $this->denominator->multipliedBy($that->numerator);
return new BigRational($numerator, $denominator, true);
}
/**
* Returns this number exponentiated to the given value.
*
* @param int $exponent The exponent.
*
* @return BigRational The result.
*
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
*/
public function power(int $exponent) : BigRational
{
if ($exponent === 0) {
$one = BigInteger::one();
return new BigRational($one, $one, false);
}
if ($exponent === 1) {
return $this;
}
return new BigRational(
$this->numerator->power($exponent),
$this->denominator->power($exponent),
false
);
}
/**
* Returns the reciprocal of this BigRational.
*
* The reciprocal has the numerator and denominator swapped.
*
* @return BigRational
*
* @throws DivisionByZeroException If the numerator is zero.
*/
public function reciprocal() : BigRational
{
return new BigRational($this->denominator, $this->numerator, true);
}
/**
* Returns the absolute value of this BigRational.
*
* @return BigRational
*/
public function abs() : BigRational
{
return new BigRational($this->numerator->abs(), $this->denominator, false);
}
/**
* Returns the negated value of this BigRational.
*
* @return BigRational
*/
public function negated() : BigRational
{
return new BigRational($this->numerator->negated(), $this->denominator, false);
}
/**
* Returns the simplified value of this BigRational.
*
* @return BigRational
*/
public function simplified() : BigRational
{
$gcd = $this->numerator->gcd($this->denominator);
$numerator = $this->numerator->quotient($gcd);
$denominator = $this->denominator->quotient($gcd);
return new BigRational($numerator, $denominator, false);
}
/**
* {@inheritdoc}
*/
public function compareTo($that) : int
{
return $this->minus($that)->getSign();
}
/**
* {@inheritdoc}
*/
public function getSign() : int
{
return $this->numerator->getSign();
}
/**
* {@inheritdoc}
*/
public function toBigInteger() : BigInteger
{
$simplified = $this->simplified();
if (! $simplified->denominator->isEqualTo(1)) {
throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.');
}
return $simplified->numerator;
}
/**
* {@inheritdoc}
*/
public function toBigDecimal() : BigDecimal
{
return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
}
/**
* {@inheritdoc}
*/
public function toBigRational() : BigRational
{
return $this;
}
/**
* {@inheritdoc}
*/
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
}
/**
* {@inheritdoc}
*/
public function toInt() : int
{
return $this->toBigInteger()->toInt();
}
/**
* {@inheritdoc}
*/
public function toFloat() : float
{
return $this->numerator->toFloat() / $this->denominator->toFloat();
}
/**
* {@inheritdoc}
*/
public function __toString() : string
{
$numerator = (string) $this->numerator;
$denominator = (string) $this->denominator;
if ($denominator === '1') {
return $numerator;
}
return $this->numerator . '/' . $this->denominator;
}
/**
* This method is required by interface Serializable and SHOULD NOT be accessed directly.
*
* @internal
*
* @return string
*/
public function serialize() : string
{
return $this->numerator . '/' . $this->denominator;
}
/**
* This method is only here to implement interface Serializable and cannot be accessed directly.
*
* @internal
*
* @param string $value
*
* @return void
*
* @throws \LogicException
*/
public function unserialize($value) : void
{
if (isset($this->numerator)) {
throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
}
[$numerator, $denominator] = \explode('/', $value);
$this->numerator = BigInteger::of($numerator);
$this->denominator = BigInteger::of($denominator);
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Exception thrown when a division by zero occurs.
*/
class DivisionByZeroException extends MathException
{
/**
* @return DivisionByZeroException
*
* @psalm-pure
*/
public static function divisionByZero() : DivisionByZeroException
{
return new self('Division by zero.');
}
/**
* @return DivisionByZeroException
*
* @psalm-pure
*/
public static function modulusMustNotBeZero() : DivisionByZeroException
{
return new self('The modulus must not be zero.');
}
/**
* @return DivisionByZeroException
*
* @psalm-pure
*/
public static function denominatorMustNotBeZero() : DivisionByZeroException
{
return new self('The denominator of a rational number cannot be zero.');
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
use Brick\Math\BigInteger;
/**
* Exception thrown when an integer overflow occurs.
*/
class IntegerOverflowException extends MathException
{
/**
* @param BigInteger $value
*
* @return IntegerOverflowException
*
* @psalm-pure
*/
public static function toIntOverflow(BigInteger $value) : IntegerOverflowException
{
$message = '%s is out of range %d to %d and cannot be represented as an integer.';
return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX));
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Base class for all math exceptions.
*
* This class is abstract to ensure that only fine-grained exceptions are thrown throughout the code.
*/
class MathException extends \RuntimeException
{
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number.
*/
class NegativeNumberException extends MathException
{
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Exception thrown when attempting to create a number from a string with an invalid format.
*/
class NumberFormatException extends MathException
{
/**
* @param string $char The failing character.
*
* @return NumberFormatException
*
* @psalm-pure
*/
public static function charNotInAlphabet(string $char) : self
{
$ord = \ord($char);
if ($ord < 32 || $ord > 126) {
$char = \strtoupper(\dechex($ord));
if ($ord < 10) {
$char = '0' . $char;
}
} else {
$char = '"' . $char . '"';
}
return new self(sprintf('Char %s is not a valid character in the given alphabet.', $char));
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Exception thrown when a number cannot be represented at the requested scale without rounding.
*/
class RoundingNecessaryException extends MathException
{
/**
* @return RoundingNecessaryException
*
* @psalm-pure
*/
public static function roundingNecessary() : RoundingNecessaryException
{
return new self('Rounding is necessary to represent the result of the operation at this scale.');
}
}

View File

@@ -0,0 +1,756 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Internal;
use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Math\RoundingMode;
/**
* Performs basic operations on arbitrary size integers.
*
* Unless otherwise specified, all parameters must be validated as non-empty strings of digits,
* without leading zero, and with an optional leading minus sign if the number is not zero.
*
* Any other parameter format will lead to undefined behaviour.
* All methods must return strings respecting this format, unless specified otherwise.
*
* @internal
*
* @psalm-immutable
*/
abstract class Calculator
{
/**
* The maximum exponent value allowed for the pow() method.
*/
public const MAX_POWER = 1000000;
/**
* The alphabet for converting from and to base 2 to 36, lowercase.
*/
public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
/**
* The Calculator instance in use.
*
* @var Calculator|null
*/
private static $instance;
/**
* Sets the Calculator instance to use.
*
* An instance is typically set only in unit tests: the autodetect is usually the best option.
*
* @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect.
*
* @return void
*/
final public static function set(?Calculator $calculator) : void
{
self::$instance = $calculator;
}
/**
* Returns the Calculator instance to use.
*
* If none has been explicitly set, the fastest available implementation will be returned.
*
* @return Calculator
*
* @psalm-pure
* @psalm-suppress ImpureStaticProperty
*/
final public static function get() : Calculator
{
if (self::$instance === null) {
/** @psalm-suppress ImpureMethodCall */
self::$instance = self::detect();
}
return self::$instance;
}
/**
* Returns the fastest available Calculator implementation.
*
* @codeCoverageIgnore
*
* @return Calculator
*/
private static function detect() : Calculator
{
if (\extension_loaded('gmp')) {
return new Calculator\GmpCalculator();
}
if (\extension_loaded('bcmath')) {
return new Calculator\BcMathCalculator();
}
return new Calculator\NativeCalculator();
}
/**
* Extracts the sign & digits of the operands.
*
* @param string $a The first operand.
* @param string $b The second operand.
*
* @return array{0: bool, 1: bool, 2: string, 3: string} Whether $a and $b are negative, followed by their digits.
*/
final protected function init(string $a, string $b) : array
{
return [
$aNeg = ($a[0] === '-'),
$bNeg = ($b[0] === '-'),
$aNeg ? \substr($a, 1) : $a,
$bNeg ? \substr($b, 1) : $b,
];
}
/**
* Returns the absolute value of a number.
*
* @param string $n The number.
*
* @return string The absolute value.
*/
final public function abs(string $n) : string
{
return ($n[0] === '-') ? \substr($n, 1) : $n;
}
/**
* Negates a number.
*
* @param string $n The number.
*
* @return string The negated value.
*/
final public function neg(string $n) : string
{
if ($n === '0') {
return '0';
}
if ($n[0] === '-') {
return \substr($n, 1);
}
return '-' . $n;
}
/**
* Compares two numbers.
*
* @param string $a The first number.
* @param string $b The second number.
*
* @return int [-1, 0, 1] If the first number is less than, equal to, or greater than the second number.
*/
final public function cmp(string $a, string $b) : int
{
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
if ($aNeg && ! $bNeg) {
return -1;
}
if ($bNeg && ! $aNeg) {
return 1;
}
$aLen = \strlen($aDig);
$bLen = \strlen($bDig);
if ($aLen < $bLen) {
$result = -1;
} elseif ($aLen > $bLen) {
$result = 1;
} else {
$result = $aDig <=> $bDig;
}
return $aNeg ? -$result : $result;
}
/**
* Adds two numbers.
*
* @param string $a The augend.
* @param string $b The addend.
*
* @return string The sum.
*/
abstract public function add(string $a, string $b) : string;
/**
* Subtracts two numbers.
*
* @param string $a The minuend.
* @param string $b The subtrahend.
*
* @return string The difference.
*/
abstract public function sub(string $a, string $b) : string;
/**
* Multiplies two numbers.
*
* @param string $a The multiplicand.
* @param string $b The multiplier.
*
* @return string The product.
*/
abstract public function mul(string $a, string $b) : string;
/**
* Returns the quotient of the division of two numbers.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
*
* @return string The quotient.
*/
abstract public function divQ(string $a, string $b) : string;
/**
* Returns the remainder of the division of two numbers.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
*
* @return string The remainder.
*/
abstract public function divR(string $a, string $b) : string;
/**
* Returns the quotient and remainder of the division of two numbers.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
*
* @return string[] An array containing the quotient and remainder.
*/
abstract public function divQR(string $a, string $b) : array;
/**
* Exponentiates a number.
*
* @param string $a The base number.
* @param int $e The exponent, validated as an integer between 0 and MAX_POWER.
*
* @return string The power.
*/
abstract public function pow(string $a, int $e) : string;
/**
* @param string $a
* @param string $b The modulus; must not be zero.
*
* @return string
*/
public function mod(string $a, string $b) : string
{
return $this->divR($this->add($this->divR($a, $b), $b), $b);
}
/**
* Returns the modular multiplicative inverse of $x modulo $m.
*
* If $x has no multiplicative inverse mod m, this method must return null.
*
* This method can be overridden by the concrete implementation if the underlying library has built-in support.
*
* @param string $x
* @param string $m The modulus; must not be negative or zero.
*
* @return string|null
*/
public function modInverse(string $x, string $m) : ?string
{
if ($m === '1') {
return '0';
}
$modVal = $x;
if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) {
$modVal = $this->mod($x, $m);
}
$x = '0';
$y = '0';
$g = $this->gcdExtended($modVal, $m, $x, $y);
if ($g !== '1') {
return null;
}
return $this->mod($this->add($this->mod($x, $m), $m), $m);
}
/**
* Raises a number into power with modulo.
*
* @param string $base The base number; must be positive or zero.
* @param string $exp The exponent; must be positive or zero.
* @param string $mod The modulus; must be strictly positive.
*
* @return string The power.
*/
abstract public function modPow(string $base, string $exp, string $mod) : string;
/**
* Returns the greatest common divisor of the two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for GCD calculations.
*
* @param string $a The first number.
* @param string $b The second number.
*
* @return string The GCD, always positive, or zero if both arguments are zero.
*/
public function gcd(string $a, string $b) : string
{
if ($a === '0') {
return $this->abs($b);
}
if ($b === '0') {
return $this->abs($a);
}
return $this->gcd($b, $this->divR($a, $b));
}
private function gcdExtended(string $a, string $b, string &$x, string &$y) : string
{
if ($a === '0') {
$x = '0';
$y = '1';
return $b;
}
$x1 = '0';
$y1 = '0';
$gcd = $this->gcdExtended($this->mod($b, $a), $a, $x1, $y1);
$x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1));
$y = $x1;
return $gcd;
}
/**
* Returns the square root of the given number, rounded down.
*
* The result is the largest x such that x² ≤ n.
* The input MUST NOT be negative.
*
* @param string $n The number.
*
* @return string The square root.
*/
abstract public function sqrt(string $n) : string;
/**
* Converts a number from an arbitrary base.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for base conversion.
*
* @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base.
* @param int $base The base of the number, validated from 2 to 36.
*
* @return string The converted number, following the Calculator conventions.
*/
public function fromBase(string $number, int $base) : string
{
return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base);
}
/**
* Converts a number to an arbitrary base.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for base conversion.
*
* @param string $number The number to convert, following the Calculator conventions.
* @param int $base The base to convert to, validated from 2 to 36.
*
* @return string The converted number, lowercase.
*/
public function toBase(string $number, int $base) : string
{
$negative = ($number[0] === '-');
if ($negative) {
$number = \substr($number, 1);
}
$number = $this->toArbitraryBase($number, self::ALPHABET, $base);
if ($negative) {
return '-' . $number;
}
return $number;
}
/**
* Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10.
*
* @param string $number The number to convert, validated as a non-empty string,
* containing only chars in the given alphabet/base.
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
* @param int $base The base of the number, validated from 2 to alphabet length.
*
* @return string The number in base 10, following the Calculator conventions.
*/
final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string
{
// remove leading "zeros"
$number = \ltrim($number, $alphabet[0]);
if ($number === '') {
return '0';
}
// optimize for "one"
if ($number === $alphabet[1]) {
return '1';
}
$result = '0';
$power = '1';
$base = (string) $base;
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
$index = \strpos($alphabet, $number[$i]);
if ($index !== 0) {
$result = $this->add($result, ($index === 1)
? $power
: $this->mul($power, (string) $index)
);
}
if ($i !== 0) {
$power = $this->mul($power, $base);
}
}
return $result;
}
/**
* Converts a non-negative number to an arbitrary base using a custom alphabet.
*
* @param string $number The number to convert, positive or zero, following the Calculator conventions.
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
* @param int $base The base to convert to, validated from 2 to alphabet length.
*
* @return string The converted number in the given alphabet.
*/
final public function toArbitraryBase(string $number, string $alphabet, int $base) : string
{
if ($number === '0') {
return $alphabet[0];
}
$base = (string) $base;
$result = '';
while ($number !== '0') {
[$number, $remainder] = $this->divQR($number, $base);
$remainder = (int) $remainder;
$result .= $alphabet[$remainder];
}
return \strrev($result);
}
/**
* Performs a rounded division.
*
* Rounding is performed when the remainder of the division is not zero.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
* @param int $roundingMode The rounding mode.
*
* @return string
*
* @throws \InvalidArgumentException If the rounding mode is invalid.
* @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary.
*/
final public function divRound(string $a, string $b, int $roundingMode) : string
{
[$quotient, $remainder] = $this->divQR($a, $b);
$hasDiscardedFraction = ($remainder !== '0');
$isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');
$discardedFractionSign = function() use ($remainder, $b) : int {
$r = $this->abs($this->mul($remainder, '2'));
$b = $this->abs($b);
return $this->cmp($r, $b);
};
$increment = false;
switch ($roundingMode) {
case RoundingMode::UNNECESSARY:
if ($hasDiscardedFraction) {
throw RoundingNecessaryException::roundingNecessary();
}
break;
case RoundingMode::UP:
$increment = $hasDiscardedFraction;
break;
case RoundingMode::DOWN:
break;
case RoundingMode::CEILING:
$increment = $hasDiscardedFraction && $isPositiveOrZero;
break;
case RoundingMode::FLOOR:
$increment = $hasDiscardedFraction && ! $isPositiveOrZero;
break;
case RoundingMode::HALF_UP:
$increment = $discardedFractionSign() >= 0;
break;
case RoundingMode::HALF_DOWN:
$increment = $discardedFractionSign() > 0;
break;
case RoundingMode::HALF_CEILING:
$increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0;
break;
case RoundingMode::HALF_FLOOR:
$increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
break;
case RoundingMode::HALF_EVEN:
$lastDigit = (int) $quotient[-1];
$lastDigitIsEven = ($lastDigit % 2 === 0);
$increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
break;
default:
throw new \InvalidArgumentException('Invalid rounding mode.');
}
if ($increment) {
return $this->add($quotient, $isPositiveOrZero ? '1' : '-1');
}
return $quotient;
}
/**
* Calculates bitwise AND of two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for bitwise operations.
*
* @param string $a
* @param string $b
*
* @return string
*/
public function and(string $a, string $b) : string
{
return $this->bitwise('and', $a, $b);
}
/**
* Calculates bitwise OR of two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for bitwise operations.
*
* @param string $a
* @param string $b
*
* @return string
*/
public function or(string $a, string $b) : string
{
return $this->bitwise('or', $a, $b);
}
/**
* Calculates bitwise XOR of two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for bitwise operations.
*
* @param string $a
* @param string $b
*
* @return string
*/
public function xor(string $a, string $b) : string
{
return $this->bitwise('xor', $a, $b);
}
/**
* Performs a bitwise operation on a decimal number.
*
* @param string $operator The operator to use, must be "and", "or" or "xor".
* @param string $a The left operand.
* @param string $b The right operand.
*
* @return string
*/
private function bitwise(string $operator, string $a, string $b) : string
{
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
$aBin = $this->toBinary($aDig);
$bBin = $this->toBinary($bDig);
$aLen = \strlen($aBin);
$bLen = \strlen($bBin);
if ($aLen > $bLen) {
$bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin;
} elseif ($bLen > $aLen) {
$aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin;
}
if ($aNeg) {
$aBin = $this->twosComplement($aBin);
}
if ($bNeg) {
$bBin = $this->twosComplement($bBin);
}
switch ($operator) {
case 'and':
$value = $aBin & $bBin;
$negative = ($aNeg and $bNeg);
break;
case 'or':
$value = $aBin | $bBin;
$negative = ($aNeg or $bNeg);
break;
case 'xor':
$value = $aBin ^ $bBin;
$negative = ($aNeg xor $bNeg);
break;
// @codeCoverageIgnoreStart
default:
throw new \InvalidArgumentException('Invalid bitwise operator.');
// @codeCoverageIgnoreEnd
}
if ($negative) {
$value = $this->twosComplement($value);
}
$result = $this->toDecimal($value);
return $negative ? $this->neg($result) : $result;
}
/**
* @param string $number A positive, binary number.
*
* @return string
*/
private function twosComplement(string $number) : string
{
$xor = \str_repeat("\xff", \strlen($number));
$number = $number ^ $xor;
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
$byte = \ord($number[$i]);
if (++$byte !== 256) {
$number[$i] = \chr($byte);
break;
}
$number[$i] = "\x00";
if ($i === 0) {
$number = "\x01" . $number;
}
}
return $number;
}
/**
* Converts a decimal number to a binary string.
*
* @param string $number The number to convert, positive or zero, only digits.
*
* @return string
*/
private function toBinary(string $number) : string
{
$result = '';
while ($number !== '0') {
[$number, $remainder] = $this->divQR($number, '256');
$result .= \chr((int) $remainder);
}
return \strrev($result);
}
/**
* Returns the positive decimal representation of a binary number.
*
* @param string $bytes The bytes representing the number.
*
* @return string
*/
private function toDecimal(string $bytes) : string
{
$result = '0';
$power = '1';
for ($i = \strlen($bytes) - 1; $i >= 0; $i--) {
$index = \ord($bytes[$i]);
if ($index !== 0) {
$result = $this->add($result, ($index === 1)
? $power
: $this->mul($power, (string) $index)
);
}
if ($i !== 0) {
$power = $this->mul($power, '256');
}
}
return $result;
}
}

View File

@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Internal\Calculator;
use Brick\Math\Internal\Calculator;
/**
* Calculator implementation built around the bcmath library.
*
* @internal
*
* @psalm-immutable
*/
class BcMathCalculator extends Calculator
{
/**
* {@inheritdoc}
*/
public function add(string $a, string $b) : string
{
return \bcadd($a, $b, 0);
}
/**
* {@inheritdoc}
*/
public function sub(string $a, string $b) : string
{
return \bcsub($a, $b, 0);
}
/**
* {@inheritdoc}
*/
public function mul(string $a, string $b) : string
{
return \bcmul($a, $b, 0);
}
/**
* {@inheritdoc}
*/
public function divQ(string $a, string $b) : string
{
return \bcdiv($a, $b, 0);
}
/**
* {@inheritdoc}
*/
public function divR(string $a, string $b) : string
{
return \bcmod($a, $b);
}
/**
* {@inheritdoc}
*/
public function divQR(string $a, string $b) : array
{
$q = \bcdiv($a, $b, 0);
$r = \bcmod($a, $b);
return [$q, $r];
}
/**
* {@inheritdoc}
*/
public function pow(string $a, int $e) : string
{
return \bcpow($a, (string) $e, 0);
}
/**
* {@inheritdoc}
*/
public function modPow(string $base, string $exp, string $mod) : string
{
return \bcpowmod($base, $exp, $mod, 0);
}
/**
* {@inheritDoc}
*/
public function sqrt(string $n) : string
{
return \bcsqrt($n, 0);
}
}

View File

@@ -0,0 +1,156 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Internal\Calculator;
use Brick\Math\Internal\Calculator;
/**
* Calculator implementation built around the GMP library.
*
* @internal
*
* @psalm-immutable
*/
class GmpCalculator extends Calculator
{
/**
* {@inheritdoc}
*/
public function add(string $a, string $b) : string
{
return \gmp_strval(\gmp_add($a, $b));
}
/**
* {@inheritdoc}
*/
public function sub(string $a, string $b) : string
{
return \gmp_strval(\gmp_sub($a, $b));
}
/**
* {@inheritdoc}
*/
public function mul(string $a, string $b) : string
{
return \gmp_strval(\gmp_mul($a, $b));
}
/**
* {@inheritdoc}
*/
public function divQ(string $a, string $b) : string
{
return \gmp_strval(\gmp_div_q($a, $b));
}
/**
* {@inheritdoc}
*/
public function divR(string $a, string $b) : string
{
return \gmp_strval(\gmp_div_r($a, $b));
}
/**
* {@inheritdoc}
*/
public function divQR(string $a, string $b) : array
{
[$q, $r] = \gmp_div_qr($a, $b);
return [
\gmp_strval($q),
\gmp_strval($r)
];
}
/**
* {@inheritdoc}
*/
public function pow(string $a, int $e) : string
{
return \gmp_strval(\gmp_pow($a, $e));
}
/**
* {@inheritdoc}
*/
public function modInverse(string $x, string $m) : ?string
{
$result = \gmp_invert($x, $m);
if ($result === false) {
return null;
}
return \gmp_strval($result);
}
/**
* {@inheritdoc}
*/
public function modPow(string $base, string $exp, string $mod) : string
{
return \gmp_strval(\gmp_powm($base, $exp, $mod));
}
/**
* {@inheritdoc}
*/
public function gcd(string $a, string $b) : string
{
return \gmp_strval(\gmp_gcd($a, $b));
}
/**
* {@inheritdoc}
*/
public function fromBase(string $number, int $base) : string
{
return \gmp_strval(\gmp_init($number, $base));
}
/**
* {@inheritdoc}
*/
public function toBase(string $number, int $base) : string
{
return \gmp_strval($number, $base);
}
/**
* {@inheritdoc}
*/
public function and(string $a, string $b) : string
{
return \gmp_strval(\gmp_and($a, $b));
}
/**
* {@inheritdoc}
*/
public function or(string $a, string $b) : string
{
return \gmp_strval(\gmp_or($a, $b));
}
/**
* {@inheritdoc}
*/
public function xor(string $a, string $b) : string
{
return \gmp_strval(\gmp_xor($a, $b));
}
/**
* {@inheritDoc}
*/
public function sqrt(string $n) : string
{
return \gmp_strval(\gmp_sqrt($n));
}
}

View File

@@ -0,0 +1,616 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Internal\Calculator;
use Brick\Math\Internal\Calculator;
/**
* Calculator implementation using only native PHP code.
*
* @internal
*
* @psalm-immutable
*/
class NativeCalculator extends Calculator
{
/**
* The max number of digits the platform can natively add, subtract, multiply or divide without overflow.
* For multiplication, this represents the max sum of the lengths of both operands.
*
* For addition, it is assumed that an extra digit can hold a carry (1) without overflowing.
* Example: 32-bit: max number 1,999,999,999 (9 digits + carry)
* 64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry)
*
* @var int
*/
private $maxDigits;
/**
* Class constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
switch (PHP_INT_SIZE) {
case 4:
$this->maxDigits = 9;
break;
case 8:
$this->maxDigits = 18;
break;
default:
throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.');
}
}
/**
* {@inheritdoc}
*/
public function add(string $a, string $b) : string
{
$result = $a + $b;
if (is_int($result)) {
return (string) $result;
}
if ($a === '0') {
return $b;
}
if ($b === '0') {
return $a;
}
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
if ($aNeg === $bNeg) {
$result = $this->doAdd($aDig, $bDig);
} else {
$result = $this->doSub($aDig, $bDig);
}
if ($aNeg) {
$result = $this->neg($result);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function sub(string $a, string $b) : string
{
return $this->add($a, $this->neg($b));
}
/**
* {@inheritdoc}
*/
public function mul(string $a, string $b) : string
{
$result = $a * $b;
if (is_int($result)) {
return (string) $result;
}
if ($a === '0' || $b === '0') {
return '0';
}
if ($a === '1') {
return $b;
}
if ($b === '1') {
return $a;
}
if ($a === '-1') {
return $this->neg($b);
}
if ($b === '-1') {
return $this->neg($a);
}
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
$result = $this->doMul($aDig, $bDig);
if ($aNeg !== $bNeg) {
$result = $this->neg($result);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function divQ(string $a, string $b) : string
{
return $this->divQR($a, $b)[0];
}
/**
* {@inheritdoc}
*/
public function divR(string $a, string $b): string
{
return $this->divQR($a, $b)[1];
}
/**
* {@inheritdoc}
*/
public function divQR(string $a, string $b) : array
{
if ($a === '0') {
return ['0', '0'];
}
if ($a === $b) {
return ['1', '0'];
}
if ($b === '1') {
return [$a, '0'];
}
if ($b === '-1') {
return [$this->neg($a), '0'];
}
$na = $a * 1; // cast to number
if (is_int($na)) {
$nb = $b * 1;
if (is_int($nb)) {
// the only division that may overflow is PHP_INT_MIN / -1,
// which cannot happen here as we've already handled a divisor of -1 above.
$r = $na % $nb;
$q = ($na - $r) / $nb;
assert(is_int($q));
return [
(string) $q,
(string) $r
];
}
}
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
[$q, $r] = $this->doDiv($aDig, $bDig);
if ($aNeg !== $bNeg) {
$q = $this->neg($q);
}
if ($aNeg) {
$r = $this->neg($r);
}
return [$q, $r];
}
/**
* {@inheritdoc}
*/
public function pow(string $a, int $e) : string
{
if ($e === 0) {
return '1';
}
if ($e === 1) {
return $a;
}
$odd = $e % 2;
$e -= $odd;
$aa = $this->mul($a, $a);
$result = $this->pow($aa, $e / 2);
if ($odd === 1) {
$result = $this->mul($result, $a);
}
return $result;
}
/**
* Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/
*
* {@inheritdoc}
*/
public function modPow(string $base, string $exp, string $mod) : string
{
// special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0)
if ($base === '0' && $exp === '0' && $mod === '1') {
return '0';
}
// special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0)
if ($exp === '0' && $mod === '1') {
return '0';
}
$x = $base;
$res = '1';
// numbers are positive, so we can use remainder instead of modulo
$x = $this->divR($x, $mod);
while ($exp !== '0') {
if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd
$res = $this->divR($this->mul($res, $x), $mod);
}
$exp = $this->divQ($exp, '2');
$x = $this->divR($this->mul($x, $x), $mod);
}
return $res;
}
/**
* Adapted from https://cp-algorithms.com/num_methods/roots_newton.html
*
* {@inheritDoc}
*/
public function sqrt(string $n) : string
{
if ($n === '0') {
return '0';
}
// initial approximation
$x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1);
$decreased = false;
for (;;) {
$nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2');
if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) {
break;
}
$decreased = $this->cmp($nx, $x) < 0;
$x = $nx;
}
return $x;
}
/**
* Performs the addition of two non-signed large integers.
*
* @param string $a The first operand.
* @param string $b The second operand.
*
* @return string
*/
private function doAdd(string $a, string $b) : string
{
[$a, $b, $length] = $this->pad($a, $b);
$carry = 0;
$result = '';
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
$blockLength = $this->maxDigits;
if ($i < 0) {
$blockLength += $i;
$i = 0;
}
$blockA = \substr($a, $i, $blockLength);
$blockB = \substr($b, $i, $blockLength);
$sum = (string) ($blockA + $blockB + $carry);
$sumLength = \strlen($sum);
if ($sumLength > $blockLength) {
$sum = \substr($sum, 1);
$carry = 1;
} else {
if ($sumLength < $blockLength) {
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
}
$carry = 0;
}
$result = $sum . $result;
if ($i === 0) {
break;
}
}
if ($carry === 1) {
$result = '1' . $result;
}
return $result;
}
/**
* Performs the subtraction of two non-signed large integers.
*
* @param string $a The first operand.
* @param string $b The second operand.
*
* @return string
*/
private function doSub(string $a, string $b) : string
{
if ($a === $b) {
return '0';
}
// Ensure that we always subtract to a positive result: biggest minus smallest.
$cmp = $this->doCmp($a, $b);
$invert = ($cmp === -1);
if ($invert) {
$c = $a;
$a = $b;
$b = $c;
}
[$a, $b, $length] = $this->pad($a, $b);
$carry = 0;
$result = '';
$complement = 10 ** $this->maxDigits;
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
$blockLength = $this->maxDigits;
if ($i < 0) {
$blockLength += $i;
$i = 0;
}
$blockA = \substr($a, $i, $blockLength);
$blockB = \substr($b, $i, $blockLength);
$sum = $blockA - $blockB - $carry;
if ($sum < 0) {
$sum += $complement;
$carry = 1;
} else {
$carry = 0;
}
$sum = (string) $sum;
$sumLength = \strlen($sum);
if ($sumLength < $blockLength) {
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
}
$result = $sum . $result;
if ($i === 0) {
break;
}
}
// Carry cannot be 1 when the loop ends, as a > b
assert($carry === 0);
$result = \ltrim($result, '0');
if ($invert) {
$result = $this->neg($result);
}
return $result;
}
/**
* Performs the multiplication of two non-signed large integers.
*
* @param string $a The first operand.
* @param string $b The second operand.
*
* @return string
*/
private function doMul(string $a, string $b) : string
{
$x = \strlen($a);
$y = \strlen($b);
$maxDigits = \intdiv($this->maxDigits, 2);
$complement = 10 ** $maxDigits;
$result = '0';
for ($i = $x - $maxDigits;; $i -= $maxDigits) {
$blockALength = $maxDigits;
if ($i < 0) {
$blockALength += $i;
$i = 0;
}
$blockA = (int) \substr($a, $i, $blockALength);
$line = '';
$carry = 0;
for ($j = $y - $maxDigits;; $j -= $maxDigits) {
$blockBLength = $maxDigits;
if ($j < 0) {
$blockBLength += $j;
$j = 0;
}
$blockB = (int) \substr($b, $j, $blockBLength);
$mul = $blockA * $blockB + $carry;
$value = $mul % $complement;
$carry = ($mul - $value) / $complement;
$value = (string) $value;
$value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT);
$line = $value . $line;
if ($j === 0) {
break;
}
}
if ($carry !== 0) {
$line = $carry . $line;
}
$line = \ltrim($line, '0');
if ($line !== '') {
$line .= \str_repeat('0', $x - $blockALength - $i);
$result = $this->add($result, $line);
}
if ($i === 0) {
break;
}
}
return $result;
}
/**
* Performs the division of two non-signed large integers.
*
* @param string $a The first operand.
* @param string $b The second operand.
*
* @return string[] The quotient and remainder.
*/
private function doDiv(string $a, string $b) : array
{
$cmp = $this->doCmp($a, $b);
if ($cmp === -1) {
return ['0', $a];
}
$x = \strlen($a);
$y = \strlen($b);
// we now know that a >= b && x >= y
$q = '0'; // quotient
$r = $a; // remainder
$z = $y; // focus length, always $y or $y+1
for (;;) {
$focus = \substr($a, 0, $z);
$cmp = $this->doCmp($focus, $b);
if ($cmp === -1) {
if ($z === $x) { // remainder < dividend
break;
}
$z++;
}
$zeros = \str_repeat('0', $x - $z);
$q = $this->add($q, '1' . $zeros);
$a = $this->sub($a, $b . $zeros);
$r = $a;
if ($r === '0') { // remainder == 0
break;
}
$x = \strlen($a);
if ($x < $y) { // remainder < dividend
break;
}
$z = $y;
}
return [$q, $r];
}
/**
* Compares two non-signed large numbers.
*
* @param string $a The first operand.
* @param string $b The second operand.
*
* @return int [-1, 0, 1]
*/
private function doCmp(string $a, string $b) : int
{
$x = \strlen($a);
$y = \strlen($b);
$cmp = $x <=> $y;
if ($cmp !== 0) {
return $cmp;
}
return \strcmp($a, $b) <=> 0; // enforce [-1, 0, 1]
}
/**
* Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length.
*
* The numbers must only consist of digits, without leading minus sign.
*
* @param string $a The first operand.
* @param string $b The second operand.
*
* @return array{0: string, 1: string, 2: int}
*/
private function pad(string $a, string $b) : array
{
$x = \strlen($a);
$y = \strlen($b);
if ($x > $y) {
$b = \str_repeat('0', $x - $y) . $b;
return [$a, $b, $x];
}
if ($x < $y) {
$a = \str_repeat('0', $y - $x) . $a;
return [$a, $b, $y];
}
return [$a, $b, $x];
}
}

107
vendor/brick/math/src/RoundingMode.php vendored Normal file
View File

@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace Brick\Math;
/**
* Specifies a rounding behavior for numerical operations capable of discarding precision.
*
* Each rounding mode indicates how the least significant returned digit of a rounded result
* is to be calculated. If fewer digits are returned than the digits needed to represent the
* exact numerical result, the discarded digits will be referred to as the discarded fraction
* regardless the digits' contribution to the value of the number. In other words, considered
* as a numerical value, the discarded fraction could have an absolute value greater than one.
*/
final class RoundingMode
{
/**
* Private constructor. This class is not instantiable.
*
* @codeCoverageIgnore
*/
private function __construct()
{
}
/**
* Asserts that the requested operation has an exact result, hence no rounding is necessary.
*
* If this rounding mode is specified on an operation that yields a result that
* cannot be represented at the requested scale, a RoundingNecessaryException is thrown.
*/
public const UNNECESSARY = 0;
/**
* Rounds away from zero.
*
* Always increments the digit prior to a nonzero discarded fraction.
* Note that this rounding mode never decreases the magnitude of the calculated value.
*/
public const UP = 1;
/**
* Rounds towards zero.
*
* Never increments the digit prior to a discarded fraction (i.e., truncates).
* Note that this rounding mode never increases the magnitude of the calculated value.
*/
public const DOWN = 2;
/**
* Rounds towards positive infinity.
*
* If the result is positive, behaves as for UP; if negative, behaves as for DOWN.
* Note that this rounding mode never decreases the calculated value.
*/
public const CEILING = 3;
/**
* Rounds towards negative infinity.
*
* If the result is positive, behave as for DOWN; if negative, behave as for UP.
* Note that this rounding mode never increases the calculated value.
*/
public const FLOOR = 4;
/**
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
*
* Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN.
* Note that this is the rounding mode commonly taught at school.
*/
public const HALF_UP = 5;
/**
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
*
* Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN.
*/
public const HALF_DOWN = 6;
/**
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity.
*
* If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN.
*/
public const HALF_CEILING = 7;
/**
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity.
*
* If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP.
*/
public const HALF_FLOOR = 8;
/**
* Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
*
* Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd;
* behaves as for HALF_DOWN if it's even.
*
* Note that this is the rounding mode that statistically minimizes
* cumulative error when applied repeatedly over a sequence of calculations.
* It is sometimes known as "Banker's rounding", and is chiefly used in the USA.
*/
public const HALF_EVEN = 9;
}

View File

@@ -1,6 +1,7 @@
language: php
php:
- 7.4
- 7.3
- 7.2
- 7.1

View File

@@ -44,7 +44,7 @@ $numberFormatRepository = new NumberFormatRepository;
// Options can be provided to the constructor or the
// individual methods, the locale defaults to 'en' when missing.
$numberFormatter = new NumberFormatter($numberFormatRepository);
echo $numberFormatter->format('1234.99'); // 123,456.99
echo $numberFormatter->format('1234.99'); // 1,234.99
echo $numberFormatter->format('0.75', ['style' => 'percent']); // 75%
$currencyRepository = new CurrencyRepository;
@@ -85,6 +85,7 @@ echo $currency->getName(); // dollar des États-Unis
echo $currency->getSymbol(); // $US
echo $currency->getLocale(); // fr-FR
// Get all currencies, keyed by currency code.
$allCurrencies = $currencyRepository->getAll();
```
@@ -105,6 +106,7 @@ echo $language->getName(); // German
$language = $languageRepository->get('de', 'fr-FR');
echo $language->getName(); // allemand
// Get all languages, keyed by language code.
$allLanguages = $languageRepository->getAll();
```

0
vendor/commerceguys/intl/scripts/fetch_data.sh vendored Normal file → Executable file
View File

View File

@@ -35,8 +35,8 @@ if (!function_exists('collator_create')) {
$ignoredLocales = [
// Esperanto, Interlingua, Volapuk are made up languages.
'eo', 'ia', 'vo',
// Church Slavic, Manx, Prussian are historical languages.
'cu', 'gv', 'prg',
// Church Slavic, Manx, Prussian, Sanskrit are historical languages.
'cu', 'gv', 'prg', 'sa',
// Valencian differs from its parent only by a single character (è/é).
'ca-ES-VALENCIA',
// Africa secondary languages.

View File

@@ -106,9 +106,15 @@ function generate_number_formats()
if ($decimalSeparator != '.') {
$numberFormats[$locale]['decimal_separator'] = $decimalSeparator;
}
if (array_key_exists('currencyDecimal', $data['symbols-numberSystem-' . $numberingSystem])) {
$numberFormats[$locale]['decimal_currency_separator'] = $data['symbols-numberSystem-' . $numberingSystem]['currencyDecimal'];
}
if ($groupingSeparator != ',') {
$numberFormats[$locale]['grouping_separator'] = $groupingSeparator;
}
if (array_key_exists('currencyGroup', $data['symbols-numberSystem-' . $numberingSystem])) {
$numberFormats[$locale]['grouping_currency_separator'] = $data['symbols-numberSystem-' . $numberingSystem]['currencyGroup'];
}
if ($plusSign != '+') {
$numberFormats[$locale]['plus_sign'] = $plusSign;
}

View File

@@ -238,4 +238,18 @@ class CurrencyFormatter implements CurrencyFormatterInterface
throw new InvalidArgumentException(sprintf('Unrecognized currency display "%s".', $options['currency_display']));
}
}
/**
* {@inheritdoc}
*/
protected function getLocalizedSymbols(NumberFormat $numberFormat): array
{
return [
'.' => $numberFormat->getDecimalCurrencySeparator(),
',' => $numberFormat->getGroupingCurrencySeparator(),
'+' => $numberFormat->getPlusSign(),
'-' => $numberFormat->getMinusSign(),
'%' => $numberFormat->getPercentSign(),
];
}
}

View File

@@ -124,13 +124,7 @@ trait FormatterTrait
$number = strtr($number, $this->digits[$numberingSystem]);
}
// Localize symbols.
$replacements = [
'.' => $numberFormat->getDecimalSeparator(),
',' => $numberFormat->getGroupingSeparator(),
'+' => $numberFormat->getPlusSign(),
'-' => $numberFormat->getMinusSign(),
'%' => $numberFormat->getPercentSign(),
];
$replacements = $this->getLocalizedSymbols($numberFormat);
$number = strtr($number, $replacements);
return $number;
@@ -149,15 +143,10 @@ trait FormatterTrait
*/
protected function parseNumber($number, NumberFormat $numberFormat)
{
$replacements = [
$numberFormat->getGroupingSeparator() => '',
// Convert the localized symbols back to their original form.
$numberFormat->getDecimalSeparator() => '.',
$numberFormat->getPlusSign() => '+',
$numberFormat->getMinusSign() => '-',
$numberFormat->getPercentSign() => '%',
// Strip whitespace (spaces and non-breaking spaces).
// Convert localized symbols back to their original form.
$replacements = array_flip($this->getLocalizedSymbols($numberFormat));
// Strip whitespace (spaces and non-breaking spaces).
$replacements += [
' ' => '',
chr(0xC2) . chr(0xA0) => '',
];
@@ -168,6 +157,8 @@ trait FormatterTrait
}
$number = strtr($number, $replacements);
// Strip grouping separators.
$number = str_replace(',', '', $number);
// Convert the accounting format for negative numbers.
if (substr($number, 0, 1) == '(' && substr($number, -1, 1) == ')') {
$number = '-' . str_replace(['(', ')'], '', $number);
@@ -212,4 +203,15 @@ trait FormatterTrait
* @return string[] The patterns, keyed by style.
*/
abstract protected function getAvailablePatterns(NumberFormat $numberFormat);
/**
* Gets the localized symbols for the provided number format.
*
* Used to localize the number in localizeNumber().
*
* @param NumberFormat $numberFormat The number format.
*
* @return array
*/
abstract protected function getLocalizedSymbols(NumberFormat $numberFormat): array;
}

View File

@@ -160,4 +160,18 @@ class NumberFormatter implements NumberFormatterInterface
throw new InvalidArgumentException(sprintf('Unrecognized style "%s".', $options['style']));
}
}
/**
* {@inheritdoc}
*/
protected function getLocalizedSymbols(NumberFormat $numberFormat): array
{
return [
'.' => $numberFormat->getDecimalSeparator(),
',' => $numberFormat->getGroupingSeparator(),
'+' => $numberFormat->getPlusSign(),
'-' => $numberFormat->getMinusSign(),
'%' => $numberFormat->getPercentSign(),
];
}
}

View File

@@ -67,6 +67,13 @@ final class NumberFormat
*/
protected $decimalSeparator = '.';
/**
* The decimal separator for currency amounts.
*
* @var string
*/
protected $decimalCurrencySeparator = '.';
/**
* The grouping separator.
*
@@ -74,6 +81,13 @@ final class NumberFormat
*/
protected $groupingSeparator = ',';
/**
* The grouping separator for currency amounts.
*
* @var string
*/
protected $groupingCurrencySeparator = ',';
/**
* The plus sign.
*
@@ -130,9 +144,19 @@ final class NumberFormat
if (isset($definition['decimal_separator'])) {
$this->decimalSeparator = $definition['decimal_separator'];
}
if (isset($definition['decimal_currency_separator'])) {
$this->decimalCurrencySeparator = $definition['decimal_currency_separator'];
} else {
$this->decimalCurrencySeparator = $this->decimalSeparator;
}
if (isset($definition['grouping_separator'])) {
$this->groupingSeparator = $definition['grouping_separator'];
}
if (isset($definition['grouping_currency_separator'])) {
$this->groupingCurrencySeparator = $definition['grouping_currency_separator'];
} else {
$this->groupingCurrencySeparator = $this->groupingSeparator;
}
if (isset($definition['plus_sign'])) {
$this->plusSign = $definition['plus_sign'];
}
@@ -227,7 +251,17 @@ final class NumberFormat
}
/**
* Gets the grouping separator.
* Gets the decimal separator for currency amounts.
*
* @return string
*/
public function getDecimalCurrencySeparator()
{
return $this->decimalCurrencySeparator;
}
/**
* Gets the grouping separator for currency amounts.
*
* @return string
*/
@@ -236,6 +270,16 @@ final class NumberFormat
return $this->groupingSeparator;
}
/**
* Gets the currency grouping separator.
*
* @return string
*/
public function getGroupingCurrencySeparator()
{
return $this->groupingCurrencySeparator;
}
/**
* Gets the plus sign.
*

View File

@@ -227,6 +227,7 @@ class NumberFormatRepository implements NumberFormatRepositoryInterface
'accounting_currency_pattern' => '#,##0.00 ¤',
'decimal_separator' => ',',
'grouping_separator' => ' ',
'grouping_currency_separator' => '.',
],
'de-CH' => [
'currency_pattern' => '¤ #,##0.00;¤-#,##0.00',
@@ -476,6 +477,7 @@ class NumberFormatRepository implements NumberFormatRepositoryInterface
'currency_pattern' => '#,##0.00 ¤',
'accounting_currency_pattern' => '#,##0.00 ¤;(#,##0.00 ¤)',
'decimal_separator' => ',',
'decimal_currency_separator' => '.',
'grouping_separator' => '',
],
'fr-LU' => [

View File

@@ -19,7 +19,7 @@ private static $installed = array (
'aliases' =>
array (
),
'reference' => '188975ccbd3a9c584238c35dd8c5a8fda7718199',
'reference' => 'f9d24d07dd67148a652610b002126e1e80b11839',
'name' => 'zotlabs/hubzilla',
),
'versions' =>
@@ -33,6 +33,15 @@ private static $installed = array (
),
'reference' => '0740f81829698b84efe17e72501e0f420ea0d611',
),
'brick/math' =>
array (
'pretty_version' => '0.9.1',
'version' => '0.9.1.0',
'aliases' =>
array (
),
'reference' => '283a40c901101e66de7061bd359252c013dcc43c',
),
'bshaffer/oauth2-server-php' =>
array (
'pretty_version' => 'v1.11.1',
@@ -44,12 +53,12 @@ private static $installed = array (
),
'commerceguys/intl' =>
array (
'pretty_version' => 'v1.0.6',
'version' => '1.0.6.0',
'pretty_version' => 'v1.0.7',
'version' => '1.0.7.0',
'aliases' =>
array (
),
'reference' => '47d5d6d60d0cc25f867e337ce229a228bf6be6f8',
'reference' => '0bf0beb12e37ef1a61e0d09dc66cdaa1a23e62e1',
),
'desandro/imagesloaded' =>
array (
@@ -96,23 +105,14 @@ private static $installed = array (
),
'reference' => 'c83178d49e372ca967d1a8c77ae4e051b3a3c75c',
),
'paragonie/random_compat' =>
array (
'pretty_version' => 'v9.99.99',
'version' => '9.99.99.0',
'aliases' =>
array (
),
'reference' => '84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95',
),
'pear/text_languagedetect' =>
array (
'pretty_version' => 'v1.0.0',
'version' => '1.0.0.0',
'pretty_version' => 'v1.0.1',
'version' => '1.0.1.0',
'aliases' =>
array (
),
'reference' => 'bb9ff6f4970f686fac59081e916b456021fe7ba6',
'reference' => '9e253f26cef9a9066f53f200cc3e0684018cb5b5',
),
'psr/log' =>
array (
@@ -123,84 +123,93 @@ private static $installed = array (
),
'reference' => '0f73288fd15629204f9d42b7055f72dacbe811fc',
),
'ramsey/uuid' =>
'ramsey/collection' =>
array (
'pretty_version' => '3.9.3',
'version' => '3.9.3.0',
'pretty_version' => '1.1.1',
'version' => '1.1.1.0',
'aliases' =>
array (
),
'reference' => '7e1633a6964b48589b142d60542f9ed31bd37a92',
'reference' => '24d93aefb2cd786b7edd9f45b554aea20b28b9b1',
),
'rhumsaa/uuid' =>
array (
'replaced' =>
array (
0 => '3.9.3',
),
),
'sabre/dav' =>
'ramsey/uuid' =>
array (
'pretty_version' => '4.1.1',
'version' => '4.1.1.0',
'aliases' =>
array (
),
'reference' => '5736f943c90d8d73d04cd8944d8c913811dc7360',
'reference' => 'cd4032040a750077205918c86049aa0f43d22947',
),
'rhumsaa/uuid' =>
array (
'replaced' =>
array (
0 => '4.1.1',
),
),
'sabre/dav' =>
array (
'pretty_version' => '4.1.3',
'version' => '4.1.3.0',
'aliases' =>
array (
),
'reference' => 'b903eeedfbdcd6cab7935661ec6dc2d90cdf8a1e',
),
'sabre/event' =>
array (
'pretty_version' => '5.1.0',
'version' => '5.1.0.0',
'pretty_version' => '5.1.2',
'version' => '5.1.2.0',
'aliases' =>
array (
),
'reference' => 'd00a17507af0e7544cfe17096372f5d733e3b276',
'reference' => 'c120bec57c17b6251a496efc82b732418b49d50a',
),
'sabre/http' =>
array (
'pretty_version' => '5.1.0',
'version' => '5.1.0.0',
'pretty_version' => '5.1.1',
'version' => '5.1.1.0',
'aliases' =>
array (
),
'reference' => '23446999f1f6e62892bbd89745070aa902dd3539',
'reference' => 'd0aafede6961df6195ce7a8dad49296b0aaee22e',
),
'sabre/uri' =>
array (
'pretty_version' => '2.2.0',
'version' => '2.2.0.0',
'aliases' =>
array (
),
'reference' => '059d11012603be2e32ddb7543602965563ddbb09',
),
'sabre/vobject' =>
array (
'pretty_version' => '4.3.1',
'version' => '4.3.1.0',
'aliases' =>
array (
),
'reference' => 'a7feca8311462e5da16952454e420b92c20d3586',
),
'sabre/xml' =>
array (
'pretty_version' => '2.2.1',
'version' => '2.2.1.0',
'aliases' =>
array (
),
'reference' => '41c6ba148966b10cafd31d1a4e5feb1e2138d95c',
'reference' => 'f502edffafea8d746825bd5f0b923a60fd2715ff',
),
'simplepie/simplepie' =>
'sabre/vobject' =>
array (
'pretty_version' => '1.5.5',
'version' => '1.5.5.0',
'pretty_version' => '4.3.3',
'version' => '4.3.3.0',
'aliases' =>
array (
),
'reference' => 'ae49e2201b6da9c808e5dac437aca356a11831b4',
'reference' => '58f9f9b46a1080c0130bd86f4df9a568aacb9c79',
),
'sabre/xml' =>
array (
'pretty_version' => '2.2.3',
'version' => '2.2.3.0',
'aliases' =>
array (
),
'reference' => 'c3b959f821c19b36952ec4a595edd695c216bfc6',
),
'simplepie/simplepie' =>
array (
'pretty_version' => '1.5.6',
'version' => '1.5.6.0',
'aliases' =>
array (
),
'reference' => '1c68e14ca3ac84346b6e6fe3c5eedf725d0f92c6',
),
'smarty/smarty' =>
array (
@@ -222,18 +231,18 @@ private static $installed = array (
),
'twbs/bootstrap' =>
array (
'pretty_version' => 'v4.5.2',
'version' => '4.5.2.0',
'pretty_version' => 'v4.5.3',
'version' => '4.5.3.0',
'aliases' =>
array (
),
'reference' => '5f2480a90ab911babc53039835fe78c6fc12646d',
'reference' => 'a716fb03f965dc0846df479e14388b1b4b93d7ce',
),
'twitter/bootstrap' =>
array (
'replaced' =>
array (
0 => 'v4.5.2',
0 => 'v4.5.3',
),
),
'zotlabs/hubzilla' =>
@@ -243,7 +252,7 @@ private static $installed = array (
'aliases' =>
array (
),
'reference' => '188975ccbd3a9c584238c35dd8c5a8fda7718199',
'reference' => 'f9d24d07dd67148a652610b002126e1e80b11839',
),
),
);

View File

@@ -6,6 +6,21 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Brick\\Math\\BigDecimal' => $vendorDir . '/brick/math/src/BigDecimal.php',
'Brick\\Math\\BigInteger' => $vendorDir . '/brick/math/src/BigInteger.php',
'Brick\\Math\\BigNumber' => $vendorDir . '/brick/math/src/BigNumber.php',
'Brick\\Math\\BigRational' => $vendorDir . '/brick/math/src/BigRational.php',
'Brick\\Math\\Exception\\DivisionByZeroException' => $vendorDir . '/brick/math/src/Exception/DivisionByZeroException.php',
'Brick\\Math\\Exception\\IntegerOverflowException' => $vendorDir . '/brick/math/src/Exception/IntegerOverflowException.php',
'Brick\\Math\\Exception\\MathException' => $vendorDir . '/brick/math/src/Exception/MathException.php',
'Brick\\Math\\Exception\\NegativeNumberException' => $vendorDir . '/brick/math/src/Exception/NegativeNumberException.php',
'Brick\\Math\\Exception\\NumberFormatException' => $vendorDir . '/brick/math/src/Exception/NumberFormatException.php',
'Brick\\Math\\Exception\\RoundingNecessaryException' => $vendorDir . '/brick/math/src/Exception/RoundingNecessaryException.php',
'Brick\\Math\\Internal\\Calculator' => $vendorDir . '/brick/math/src/Internal/Calculator.php',
'Brick\\Math\\Internal\\Calculator\\BcMathCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/BcMathCalculator.php',
'Brick\\Math\\Internal\\Calculator\\GmpCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/GmpCalculator.php',
'Brick\\Math\\Internal\\Calculator\\NativeCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/NativeCalculator.php',
'Brick\\Math\\RoundingMode' => $vendorDir . '/brick/math/src/RoundingMode.php',
'CommerceGuys\\Intl\\Calculator' => $vendorDir . '/commerceguys/intl/src/Calculator.php',
'CommerceGuys\\Intl\\Currency\\Currency' => $vendorDir . '/commerceguys/intl/src/Currency/Currency.php',
'CommerceGuys\\Intl\\Currency\\CurrencyRepository' => $vendorDir . '/commerceguys/intl/src/Currency/CurrencyRepository.php',
@@ -262,6 +277,7 @@ return array(
'ID3Parser\\ID3Parser' => $vendorDir . '/lukasreschke/id3parser/src/ID3Parser.php',
'ID3Parser\\getID3\\Tags\\getid3_id3v1' => $vendorDir . '/lukasreschke/id3parser/src/getID3/Tags/getid3_id3v1.php',
'ID3Parser\\getID3\\Tags\\getid3_id3v2' => $vendorDir . '/lukasreschke/id3parser/src/getID3/Tags/getid3_id3v2.php',
'ID3Parser\\getID3\\getID3' => $vendorDir . '/lukasreschke/id3parser/src/getID3/getID3.php',
'ID3Parser\\getID3\\getid3_exception' => $vendorDir . '/lukasreschke/id3parser/src/getID3/getid3_exception.php',
'ID3Parser\\getID3\\getid3_handler' => $vendorDir . '/lukasreschke/id3parser/src/getID3/getid3_handler.php',
'ID3Parser\\getID3\\getid3_lib' => $vendorDir . '/lukasreschke/id3parser/src/getID3/getid3_lib.php',
@@ -371,9 +387,40 @@ return array(
'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/DummyTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php',
'Ramsey\\Collection\\AbstractArray' => $vendorDir . '/ramsey/collection/src/AbstractArray.php',
'Ramsey\\Collection\\AbstractCollection' => $vendorDir . '/ramsey/collection/src/AbstractCollection.php',
'Ramsey\\Collection\\AbstractSet' => $vendorDir . '/ramsey/collection/src/AbstractSet.php',
'Ramsey\\Collection\\ArrayInterface' => $vendorDir . '/ramsey/collection/src/ArrayInterface.php',
'Ramsey\\Collection\\Collection' => $vendorDir . '/ramsey/collection/src/Collection.php',
'Ramsey\\Collection\\CollectionInterface' => $vendorDir . '/ramsey/collection/src/CollectionInterface.php',
'Ramsey\\Collection\\DoubleEndedQueue' => $vendorDir . '/ramsey/collection/src/DoubleEndedQueue.php',
'Ramsey\\Collection\\DoubleEndedQueueInterface' => $vendorDir . '/ramsey/collection/src/DoubleEndedQueueInterface.php',
'Ramsey\\Collection\\Exception\\CollectionMismatchException' => $vendorDir . '/ramsey/collection/src/Exception/CollectionMismatchException.php',
'Ramsey\\Collection\\Exception\\InvalidArgumentException' => $vendorDir . '/ramsey/collection/src/Exception/InvalidArgumentException.php',
'Ramsey\\Collection\\Exception\\InvalidSortOrderException' => $vendorDir . '/ramsey/collection/src/Exception/InvalidSortOrderException.php',
'Ramsey\\Collection\\Exception\\NoSuchElementException' => $vendorDir . '/ramsey/collection/src/Exception/NoSuchElementException.php',
'Ramsey\\Collection\\Exception\\OutOfBoundsException' => $vendorDir . '/ramsey/collection/src/Exception/OutOfBoundsException.php',
'Ramsey\\Collection\\Exception\\UnsupportedOperationException' => $vendorDir . '/ramsey/collection/src/Exception/UnsupportedOperationException.php',
'Ramsey\\Collection\\Exception\\ValueExtractionException' => $vendorDir . '/ramsey/collection/src/Exception/ValueExtractionException.php',
'Ramsey\\Collection\\GenericArray' => $vendorDir . '/ramsey/collection/src/GenericArray.php',
'Ramsey\\Collection\\Map\\AbstractMap' => $vendorDir . '/ramsey/collection/src/Map/AbstractMap.php',
'Ramsey\\Collection\\Map\\AbstractTypedMap' => $vendorDir . '/ramsey/collection/src/Map/AbstractTypedMap.php',
'Ramsey\\Collection\\Map\\AssociativeArrayMap' => $vendorDir . '/ramsey/collection/src/Map/AssociativeArrayMap.php',
'Ramsey\\Collection\\Map\\MapInterface' => $vendorDir . '/ramsey/collection/src/Map/MapInterface.php',
'Ramsey\\Collection\\Map\\NamedParameterMap' => $vendorDir . '/ramsey/collection/src/Map/NamedParameterMap.php',
'Ramsey\\Collection\\Map\\TypedMap' => $vendorDir . '/ramsey/collection/src/Map/TypedMap.php',
'Ramsey\\Collection\\Map\\TypedMapInterface' => $vendorDir . '/ramsey/collection/src/Map/TypedMapInterface.php',
'Ramsey\\Collection\\Queue' => $vendorDir . '/ramsey/collection/src/Queue.php',
'Ramsey\\Collection\\QueueInterface' => $vendorDir . '/ramsey/collection/src/QueueInterface.php',
'Ramsey\\Collection\\Set' => $vendorDir . '/ramsey/collection/src/Set.php',
'Ramsey\\Collection\\Tool\\TypeTrait' => $vendorDir . '/ramsey/collection/src/Tool/TypeTrait.php',
'Ramsey\\Collection\\Tool\\ValueExtractorTrait' => $vendorDir . '/ramsey/collection/src/Tool/ValueExtractorTrait.php',
'Ramsey\\Collection\\Tool\\ValueToStringTrait' => $vendorDir . '/ramsey/collection/src/Tool/ValueToStringTrait.php',
'Ramsey\\Uuid\\BinaryUtils' => $vendorDir . '/ramsey/uuid/src/BinaryUtils.php',
'Ramsey\\Uuid\\Builder\\BuilderCollection' => $vendorDir . '/ramsey/uuid/src/Builder/BuilderCollection.php',
'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php',
'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php',
'Ramsey\\Uuid\\Builder\\FallbackBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/FallbackBuilder.php',
'Ramsey\\Uuid\\Builder\\UuidBuilderInterface' => $vendorDir . '/ramsey/uuid/src/Builder/UuidBuilderInterface.php',
'Ramsey\\Uuid\\Codec\\CodecInterface' => $vendorDir . '/ramsey/uuid/src/Codec/CodecInterface.php',
'Ramsey\\Uuid\\Codec\\GuidStringCodec' => $vendorDir . '/ramsey/uuid/src/Codec/GuidStringCodec.php',
@@ -384,39 +431,94 @@ return array(
'Ramsey\\Uuid\\Converter\\NumberConverterInterface' => $vendorDir . '/ramsey/uuid/src/Converter/NumberConverterInterface.php',
'Ramsey\\Uuid\\Converter\\Number\\BigNumberConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Number/BigNumberConverter.php',
'Ramsey\\Uuid\\Converter\\Number\\DegradedNumberConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Number/DegradedNumberConverter.php',
'Ramsey\\Uuid\\Converter\\Number\\GenericNumberConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Number/GenericNumberConverter.php',
'Ramsey\\Uuid\\Converter\\TimeConverterInterface' => $vendorDir . '/ramsey/uuid/src/Converter/TimeConverterInterface.php',
'Ramsey\\Uuid\\Converter\\Time\\BigNumberTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php',
'Ramsey\\Uuid\\Converter\\Time\\DegradedTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/DegradedTimeConverter.php',
'Ramsey\\Uuid\\Converter\\Time\\GenericTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/GenericTimeConverter.php',
'Ramsey\\Uuid\\Converter\\Time\\PhpTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/PhpTimeConverter.php',
'Ramsey\\Uuid\\DegradedUuid' => $vendorDir . '/ramsey/uuid/src/DegradedUuid.php',
'Ramsey\\Uuid\\DeprecatedUuidInterface' => $vendorDir . '/ramsey/uuid/src/DeprecatedUuidInterface.php',
'Ramsey\\Uuid\\DeprecatedUuidMethodsTrait' => $vendorDir . '/ramsey/uuid/src/DeprecatedUuidMethodsTrait.php',
'Ramsey\\Uuid\\Exception\\BuilderNotFoundException' => $vendorDir . '/ramsey/uuid/src/Exception/BuilderNotFoundException.php',
'Ramsey\\Uuid\\Exception\\DateTimeException' => $vendorDir . '/ramsey/uuid/src/Exception/DateTimeException.php',
'Ramsey\\Uuid\\Exception\\DceSecurityException' => $vendorDir . '/ramsey/uuid/src/Exception/DceSecurityException.php',
'Ramsey\\Uuid\\Exception\\InvalidArgumentException' => $vendorDir . '/ramsey/uuid/src/Exception/InvalidArgumentException.php',
'Ramsey\\Uuid\\Exception\\InvalidBytesException' => $vendorDir . '/ramsey/uuid/src/Exception/InvalidBytesException.php',
'Ramsey\\Uuid\\Exception\\InvalidUuidStringException' => $vendorDir . '/ramsey/uuid/src/Exception/InvalidUuidStringException.php',
'Ramsey\\Uuid\\Exception\\UnsatisfiedDependencyException' => $vendorDir . '/ramsey/uuid/src/Exception/UnsatisfiedDependencyException.php',
'Ramsey\\Uuid\\Exception\\NameException' => $vendorDir . '/ramsey/uuid/src/Exception/NameException.php',
'Ramsey\\Uuid\\Exception\\NodeException' => $vendorDir . '/ramsey/uuid/src/Exception/NodeException.php',
'Ramsey\\Uuid\\Exception\\RandomSourceException' => $vendorDir . '/ramsey/uuid/src/Exception/RandomSourceException.php',
'Ramsey\\Uuid\\Exception\\TimeSourceException' => $vendorDir . '/ramsey/uuid/src/Exception/TimeSourceException.php',
'Ramsey\\Uuid\\Exception\\UnableToBuildUuidException' => $vendorDir . '/ramsey/uuid/src/Exception/UnableToBuildUuidException.php',
'Ramsey\\Uuid\\Exception\\UnsupportedOperationException' => $vendorDir . '/ramsey/uuid/src/Exception/UnsupportedOperationException.php',
'Ramsey\\Uuid\\FeatureSet' => $vendorDir . '/ramsey/uuid/src/FeatureSet.php',
'Ramsey\\Uuid\\Fields\\FieldsInterface' => $vendorDir . '/ramsey/uuid/src/Fields/FieldsInterface.php',
'Ramsey\\Uuid\\Fields\\SerializableFieldsTrait' => $vendorDir . '/ramsey/uuid/src/Fields/SerializableFieldsTrait.php',
'Ramsey\\Uuid\\Generator\\CombGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/CombGenerator.php',
'Ramsey\\Uuid\\Generator\\DceSecurityGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/DceSecurityGenerator.php',
'Ramsey\\Uuid\\Generator\\DceSecurityGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/DceSecurityGeneratorInterface.php',
'Ramsey\\Uuid\\Generator\\DefaultNameGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/DefaultNameGenerator.php',
'Ramsey\\Uuid\\Generator\\DefaultTimeGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/DefaultTimeGenerator.php',
'Ramsey\\Uuid\\Generator\\MtRandGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/MtRandGenerator.php',
'Ramsey\\Uuid\\Generator\\OpenSslGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/OpenSslGenerator.php',
'Ramsey\\Uuid\\Generator\\NameGeneratorFactory' => $vendorDir . '/ramsey/uuid/src/Generator/NameGeneratorFactory.php',
'Ramsey\\Uuid\\Generator\\NameGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/NameGeneratorInterface.php',
'Ramsey\\Uuid\\Generator\\PeclUuidNameGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/PeclUuidNameGenerator.php',
'Ramsey\\Uuid\\Generator\\PeclUuidRandomGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/PeclUuidRandomGenerator.php',
'Ramsey\\Uuid\\Generator\\PeclUuidTimeGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/PeclUuidTimeGenerator.php',
'Ramsey\\Uuid\\Generator\\RandomBytesGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/RandomBytesGenerator.php',
'Ramsey\\Uuid\\Generator\\RandomGeneratorFactory' => $vendorDir . '/ramsey/uuid/src/Generator/RandomGeneratorFactory.php',
'Ramsey\\Uuid\\Generator\\RandomGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/RandomGeneratorInterface.php',
'Ramsey\\Uuid\\Generator\\RandomLibAdapter' => $vendorDir . '/ramsey/uuid/src/Generator/RandomLibAdapter.php',
'Ramsey\\Uuid\\Generator\\SodiumRandomGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/SodiumRandomGenerator.php',
'Ramsey\\Uuid\\Generator\\TimeGeneratorFactory' => $vendorDir . '/ramsey/uuid/src/Generator/TimeGeneratorFactory.php',
'Ramsey\\Uuid\\Generator\\TimeGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/TimeGeneratorInterface.php',
'Ramsey\\Uuid\\Guid\\Fields' => $vendorDir . '/ramsey/uuid/src/Guid/Fields.php',
'Ramsey\\Uuid\\Guid\\Guid' => $vendorDir . '/ramsey/uuid/src/Guid/Guid.php',
'Ramsey\\Uuid\\Guid\\GuidBuilder' => $vendorDir . '/ramsey/uuid/src/Guid/GuidBuilder.php',
'Ramsey\\Uuid\\Lazy\\LazyUuidFromString' => $vendorDir . '/ramsey/uuid/src/Lazy/LazyUuidFromString.php',
'Ramsey\\Uuid\\Math\\BrickMathCalculator' => $vendorDir . '/ramsey/uuid/src/Math/BrickMathCalculator.php',
'Ramsey\\Uuid\\Math\\CalculatorInterface' => $vendorDir . '/ramsey/uuid/src/Math/CalculatorInterface.php',
'Ramsey\\Uuid\\Math\\RoundingMode' => $vendorDir . '/ramsey/uuid/src/Math/RoundingMode.php',
'Ramsey\\Uuid\\Nonstandard\\Fields' => $vendorDir . '/ramsey/uuid/src/Nonstandard/Fields.php',
'Ramsey\\Uuid\\Nonstandard\\Uuid' => $vendorDir . '/ramsey/uuid/src/Nonstandard/Uuid.php',
'Ramsey\\Uuid\\Nonstandard\\UuidBuilder' => $vendorDir . '/ramsey/uuid/src/Nonstandard/UuidBuilder.php',
'Ramsey\\Uuid\\Nonstandard\\UuidV6' => $vendorDir . '/ramsey/uuid/src/Nonstandard/UuidV6.php',
'Ramsey\\Uuid\\Provider\\DceSecurityProviderInterface' => $vendorDir . '/ramsey/uuid/src/Provider/DceSecurityProviderInterface.php',
'Ramsey\\Uuid\\Provider\\Dce\\SystemDceSecurityProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Dce/SystemDceSecurityProvider.php',
'Ramsey\\Uuid\\Provider\\NodeProviderInterface' => $vendorDir . '/ramsey/uuid/src/Provider/NodeProviderInterface.php',
'Ramsey\\Uuid\\Provider\\Node\\FallbackNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/FallbackNodeProvider.php',
'Ramsey\\Uuid\\Provider\\Node\\NodeProviderCollection' => $vendorDir . '/ramsey/uuid/src/Provider/Node/NodeProviderCollection.php',
'Ramsey\\Uuid\\Provider\\Node\\RandomNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/RandomNodeProvider.php',
'Ramsey\\Uuid\\Provider\\Node\\StaticNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/StaticNodeProvider.php',
'Ramsey\\Uuid\\Provider\\Node\\SystemNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/SystemNodeProvider.php',
'Ramsey\\Uuid\\Provider\\TimeProviderInterface' => $vendorDir . '/ramsey/uuid/src/Provider/TimeProviderInterface.php',
'Ramsey\\Uuid\\Provider\\Time\\FixedTimeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Time/FixedTimeProvider.php',
'Ramsey\\Uuid\\Provider\\Time\\SystemTimeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Time/SystemTimeProvider.php',
'Ramsey\\Uuid\\Rfc4122\\Fields' => $vendorDir . '/ramsey/uuid/src/Rfc4122/Fields.php',
'Ramsey\\Uuid\\Rfc4122\\FieldsInterface' => $vendorDir . '/ramsey/uuid/src/Rfc4122/FieldsInterface.php',
'Ramsey\\Uuid\\Rfc4122\\NilTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/NilTrait.php',
'Ramsey\\Uuid\\Rfc4122\\NilUuid' => $vendorDir . '/ramsey/uuid/src/Rfc4122/NilUuid.php',
'Ramsey\\Uuid\\Rfc4122\\UuidBuilder' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidBuilder.php',
'Ramsey\\Uuid\\Rfc4122\\UuidInterface' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidInterface.php',
'Ramsey\\Uuid\\Rfc4122\\UuidV1' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV1.php',
'Ramsey\\Uuid\\Rfc4122\\UuidV2' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV2.php',
'Ramsey\\Uuid\\Rfc4122\\UuidV3' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV3.php',
'Ramsey\\Uuid\\Rfc4122\\UuidV4' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV4.php',
'Ramsey\\Uuid\\Rfc4122\\UuidV5' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV5.php',
'Ramsey\\Uuid\\Rfc4122\\Validator' => $vendorDir . '/ramsey/uuid/src/Rfc4122/Validator.php',
'Ramsey\\Uuid\\Rfc4122\\VariantTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/VariantTrait.php',
'Ramsey\\Uuid\\Rfc4122\\VersionTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/VersionTrait.php',
'Ramsey\\Uuid\\Type\\Decimal' => $vendorDir . '/ramsey/uuid/src/Type/Decimal.php',
'Ramsey\\Uuid\\Type\\Hexadecimal' => $vendorDir . '/ramsey/uuid/src/Type/Hexadecimal.php',
'Ramsey\\Uuid\\Type\\Integer' => $vendorDir . '/ramsey/uuid/src/Type/Integer.php',
'Ramsey\\Uuid\\Type\\NumberInterface' => $vendorDir . '/ramsey/uuid/src/Type/NumberInterface.php',
'Ramsey\\Uuid\\Type\\Time' => $vendorDir . '/ramsey/uuid/src/Type/Time.php',
'Ramsey\\Uuid\\Type\\TypeInterface' => $vendorDir . '/ramsey/uuid/src/Type/TypeInterface.php',
'Ramsey\\Uuid\\Uuid' => $vendorDir . '/ramsey/uuid/src/Uuid.php',
'Ramsey\\Uuid\\UuidFactory' => $vendorDir . '/ramsey/uuid/src/UuidFactory.php',
'Ramsey\\Uuid\\UuidFactoryInterface' => $vendorDir . '/ramsey/uuid/src/UuidFactoryInterface.php',
'Ramsey\\Uuid\\UuidInterface' => $vendorDir . '/ramsey/uuid/src/UuidInterface.php',
'Ramsey\\Uuid\\Validator\\GenericValidator' => $vendorDir . '/ramsey/uuid/src/Validator/GenericValidator.php',
'Ramsey\\Uuid\\Validator\\ValidatorInterface' => $vendorDir . '/ramsey/uuid/src/Validator/ValidatorInterface.php',
'Sabre\\CalDAV\\Backend\\AbstractBackend' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php',
'Sabre\\CalDAV\\Backend\\BackendInterface' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/BackendInterface.php',
'Sabre\\CalDAV\\Backend\\NotificationSupport' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php',
@@ -1080,6 +1182,7 @@ return array(
'Zotlabs\\Module\\Article_edit' => $baseDir . '/Zotlabs/Module/Article_edit.php',
'Zotlabs\\Module\\Articles' => $baseDir . '/Zotlabs/Module/Articles.php',
'Zotlabs\\Module\\Attach' => $baseDir . '/Zotlabs/Module/Attach.php',
'Zotlabs\\Module\\Attach_edit' => $baseDir . '/Zotlabs/Module/Attach_edit.php',
'Zotlabs\\Module\\Authorize' => $baseDir . '/Zotlabs/Module/Authorize.php',
'Zotlabs\\Module\\Authtest' => $baseDir . '/Zotlabs/Module/Authtest.php',
'Zotlabs\\Module\\Block' => $baseDir . '/Zotlabs/Module/Block.php',
@@ -1542,6 +1645,7 @@ return array(
'Zotlabs\\Update\\_1237' => $baseDir . '/Zotlabs/Update/_1237.php',
'Zotlabs\\Update\\_1238' => $baseDir . '/Zotlabs/Update/_1238.php',
'Zotlabs\\Update\\_1239' => $baseDir . '/Zotlabs/Update/_1239.php',
'Zotlabs\\Update\\_1240' => $baseDir . '/Zotlabs/Update/_1240.php',
'Zotlabs\\Web\\Controller' => $baseDir . '/Zotlabs/Web/Controller.php',
'Zotlabs\\Web\\HTTPHeaders' => $baseDir . '/Zotlabs/Web/HTTPHeaders.php',
'Zotlabs\\Web\\HTTPSig' => $baseDir . '/Zotlabs/Web/HTTPSig.php',

View File

@@ -18,10 +18,12 @@ return array(
'Sabre\\CardDAV\\' => array($vendorDir . '/sabre/dav/lib/CardDAV'),
'Sabre\\CalDAV\\' => array($vendorDir . '/sabre/dav/lib/CalDAV'),
'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Michelf\\' => array($vendorDir . '/michelf/php-markdown/Michelf'),
'League\\HTMLToMarkdown\\' => array($vendorDir . '/league/html-to-markdown/src'),
'ID3Parser\\' => array($vendorDir . '/lukasreschke/id3parser/src'),
'Hubzilla\\' => array($baseDir . '/include'),
'CommerceGuys\\Intl\\' => array($vendorDir . '/commerceguys/intl/src'),
'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
);

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