Compare commits

..

375 Commits
6.2 ... 7.0.1

Author SHA1 Message Date
Mario
5bdc713afe Merge branch 'dev' 2022-01-28 20:10:11 +00:00
Mario
46eff1c937 changelog 2022-01-28 20:09:29 +00:00
Mario
76e1ea1c02 version 7.0.1 2022-01-28 20:04:43 +00:00
Mario
755076a8e5 Merge branch 'dev' 2022-01-28 20:03:30 +00:00
Mario
b49f7b8b34 fix removing contacts from privacy groups 2022-01-28 19:40:42 +00:00
Mario
c4dd8885e4 $cmd should be a strig and not null 2022-01-28 13:32:28 +00:00
Mario
4c82952b58 formatting and unused variables 2022-01-28 13:29:25 +00:00
Mario
0da69cb9c7 do not use escape_tags() for inbox 2022-01-27 21:56:13 +00:00
Mario
36e244060c escape_tags() will turn & to & and there for mess up the xchan hash 2022-01-27 21:51:56 +00:00
Mario
b13a9f57af fix for #1659 2022-01-27 21:10:13 +00:00
Mario
0aa67ad7f9 typo 2022-01-27 20:34:21 +00:00
Mario
195a3a6827 whitespace 2022-01-27 20:28:34 +00:00
Mario
38ecff1220 some refinement on storing the raw ap message, some comments and make sure the AS->raw is always a json string 2022-01-27 20:27:02 +00:00
Mario
67e64287af missing define of variable, remove deprecated zot-info and ofeed from webfinger 2022-01-26 19:28:04 +00:00
Mario
b022703b0b update to remove the mail app 2022-01-26 18:40:02 +00:00
Mario
e8069c0d93 use item_hidden instead of item_notshown for forum comment announces 2022-01-26 13:58:03 +00:00
Mario
7a1c6b64c2 $act->raw will not always hold the AP raw message. Look for it in iconfig. 2022-01-26 13:02:51 +00:00
Mario
8250cb1e8d always store the raw message 2022-01-26 09:35:08 +00:00
Mario
ffe2c4d42b make sure to escape the author name for the reply_to button 2022-01-24 08:51:14 +00:00
Mario
f06c970628 port z_curl_error() from zap 2022-01-23 20:23:40 +00:00
Mario
99bce46b32 fix doc 2022-01-23 15:08:13 +00:00
Mario
f711913778 fix doc 2022-01-23 15:06:18 +00:00
Mario
a8ac231667 make sure that if an existing contact role changes we will re-assign the permissions to all role members and cleanup 2022-01-23 15:03:26 +00:00
Mario
f7c8791a6d make sure we have an existing default role in any case 2022-01-23 13:43:33 +00:00
Mario
7acc775c91 wrong function name 2022-01-21 07:46:12 +00:00
Mario
c2e21e837f wrong function name 2022-01-21 07:45:42 +00:00
Mario
755d0f54f7 Merge branch '7.0RC' 2022-01-21 07:28:24 +00:00
Mario
f62d66ff25 version 7.0 2022-01-21 07:27:35 +00:00
Mario
406d19f930 Merge branch 'dev' into 7.0RC 2022-01-21 07:27:00 +00:00
Mario
42b13614eb update changelog 2022-01-21 07:26:23 +00:00
Mario
c942bd67fe Merge branch 'dev' into 7.0RC 2022-01-21 07:20:30 +00:00
Mario
b8dc3d74b6 update strings 2022-01-21 07:20:04 +00:00
Mario
38fb263737 string 2022-01-21 07:14:40 +00:00
Mario
b55beed2f9 string update 2022-01-20 14:27:28 +00:00
Mario
e9278c03c1 Merge branch 'dev' into 7.0RC 2022-01-20 10:29:52 +00:00
Mario
ae1fe83784 fix potential issue with ap addressing in mod hq 2022-01-20 10:27:55 +00:00
Mario
717a547c40 Merge branch 'dev' into 7.0RC 2022-01-20 10:03:02 +00:00
Mario
ec491e87ab remove deprecated template 2022-01-20 10:02:39 +00:00
Mario
42e30d0835 fix pgsql profile photo issue 2022-01-20 08:14:03 +00:00
Mario
5b19418e48 fix pgsql profile photo issue 2022-01-20 08:12:14 +00:00
Mario
1bc9a7373f Merge branch 'dev' into 7.0RC 2022-01-19 19:14:54 +00:00
Mario
23e59b5dcc update changelog 2022-01-19 19:14:30 +00:00
Mario
c6b459cf96 drop_item() requires the item id not the item array 2022-01-19 19:11:49 +00:00
Mario
33254b4cac Merge branch 'dev' into 7.0RC 2022-01-19 13:22:23 +00:00
Mario
44da40d18d revert background color 2022-01-19 13:21:32 +00:00
Mario
c742f25801 prevent duplicate ids and adjust spinner color 2022-01-19 13:19:32 +00:00
Mario
b153687bf1 prevent duplicate ids and adjust spinner color 2022-01-19 13:18:47 +00:00
Mario
3318f093da Merge branch 'dev' into 7.0RC 2022-01-19 11:25:06 +00:00
Mario
d98d56c3b5 provide a spinner for edit connection action in threads 2022-01-19 11:24:42 +00:00
Mario
c3f5f6c7ad Merge branch 'dev' into 7.0RC 2022-01-19 10:04:42 +00:00
Mario
5f21edcc53 update changelog 2022-01-19 10:04:20 +00:00
Mario
cd0731cbb0 version RC2 2022-01-19 09:51:32 +00:00
Mario
f392ddec2f Merge branch 'dev' into 7.0RC 2022-01-19 09:41:57 +00:00
Mario
df71168ab7 fix channel app naming and translation and cleanup apps with an db update 2022-01-19 09:41:16 +00:00
Mario
e93b26bf54 Merge branch 'dev' into 'dev'
Fix strings translation

See merge request hubzilla/core!1997
2022-01-19 09:21:55 +00:00
Mario
a73d4a8cbd Merge branch 'dev' into 7.0RC 2022-01-19 08:39:12 +00:00
Mario
20ee57801c we must check if actor.id is empty(). checking for isset() only could still end up in an empty string and produce unexpected results 2022-01-19 08:29:44 +00:00
Max Kostikov
6a270d7f02 Fix strings translation 2022-01-18 22:40:25 +02:00
Mario
68639637c9 RC1 2022-01-18 10:27:37 +00:00
Mario
0d1eabbc33 Merge branch 'dev' into 7.0RC 2022-01-18 10:26:21 +00:00
Mario
dce249f7a9 change name on all associated xchans by matching the url 2022-01-18 10:24:52 +00:00
Mario
1723d4fbd8 fix version 2022-01-18 09:59:54 +00:00
Mario
c4b09f1a4f check for existence of vcard 2022-01-18 10:50:25 +01:00
Mario
788c973c13 vcards are not actually implemented anymore 2022-01-18 10:39:00 +01:00
Mario
465c5c8cfb make sure to use the correct default role 2022-01-18 10:26:13 +01:00
Mario
ee28ba5be1 adjust lock hover text if item_private === 2 2022-01-17 08:23:15 +00:00
Mario
9a22e9cf39 bump dev version 2022-01-16 12:47:26 +00:00
Mario
2513f605b6 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2022-01-16 12:45:58 +00:00
Mario
3b1ffb2028 changelog 2022-01-16 12:45:00 +00:00
Max Kostikov
47c6624e12 Merge branch 'dev' into 'dev'
Update Russian translation

See merge request hubzilla/core!1996
2022-01-14 19:22:43 +00:00
Max Kostikov
b6f1b064d3 Update Russian translation 2022-01-14 21:20:40 +02:00
Mario
17d89467df composer dump autoload 2022-01-14 18:31:38 +00:00
Mario
1282214d48 strings and version 2022-01-14 18:28:33 +00:00
Mario
f4bc6ee615 ux improvements 2022-01-13 13:07:59 +00:00
Mario
f8b8d8c540 make sure to fallback if local_channel and no nav_bg is set 2022-01-13 10:32:36 +00:00
Mario
57ff667438 pwa improvements according to lighthouse 2022-01-13 10:18:53 +00:00
Mario
abe3039926 ux improvements 2022-01-12 21:08:29 +00:00
Mario
82a1117e91 mod profile_photo template cleanup 2022-01-12 13:43:30 +00:00
Mario
b6ff3a4d99 mod profile_photo cleanup 2022-01-12 13:35:51 +00:00
Mario
f4046efcb2 refactor mod profile_photo 2022-01-12 13:09:53 +00:00
Mario
fc1d3831cf revert commit 7e2aecd8 2022-01-11 09:59:07 +00:00
Mario
867deda247 remove legacy mail which has been deprecated since a year 2022-01-11 09:29:18 +00:00
Mario
f8149face5 toc: add headings 2022-01-11 09:17:39 +00:00
Mario
7e2aecd8bb lifetime->expires 2022-01-10 11:37:47 +00:00
Mario
105d121199 set samesite cookie flag to none - some browsers start to default the flag to lax (previous none) 2022-01-10 11:36:20 +00:00
Mario
37d662f2f5 css fixes 2022-01-10 09:29:59 +00:00
Mario
5b50454b4d make toc bbcode find its own container by default 2022-01-09 20:44:30 +00:00
Mario
b5e4c08fc5 fix get_tags() parsing toc bbcodes 2022-01-09 18:25:17 +00:00
Mario
db39cd8b7c fix php error 2022-01-09 16:38:10 +00:00
Mario
a35f741a35 deprecate AccessList::widget() 2022-01-09 15:40:54 +00:00
Mario
fc02e018cb mark group actors as such in items and minor text change 2022-01-08 18:57:59 +00:00
Mario
b14a530efb missing nav_set_selected() 2022-01-08 13:22:15 +00:00
Mario
f70bc571bd css fix 2022-01-08 13:17:19 +00:00
Mario
8cc64176b4 missing files 2022-01-07 20:14:11 +00:00
Mario
7450ac1a31 missing files 2022-01-07 20:07:09 +00:00
Mario
c72e5e3b66 streamline privacy groups 2022-01-07 20:03:40 +00:00
Mario
5e811819e2 add link to create new contact roles 2022-01-07 19:14:14 +00:00
Mario
f1c0034a18 more work on access tokens 2022-01-06 21:09:18 +00:00
Mario
7342cb81a3 bump version 2022-01-04 20:42:43 +00:00
Mario
b40e858556 only display connections widget if there are any connections to show 2022-01-04 20:42:07 +00:00
Mario
95a4ed7d6a do not show blocked or ignored contacts in connections 2022-01-04 20:31:42 +00:00
Mario
2c2d4b6b95 remove suggestions widget from mod directory 2022-01-04 19:57:04 +00:00
Mario
4490eae4fe remove suggestions widget from mod network 2022-01-04 19:55:53 +00:00
Mario
9d59cb0135 minor wording change and fix connections link 2022-01-04 19:48:15 +00:00
Mario
7d348fe69f fix Access^CccessList include and plink in post_activity_item() 2022-01-03 20:20:42 +00:00
Mario
fa8fb9e73f more lockview ui improvements 2022-01-03 11:00:14 +00:00
Mario
f6093872ec minor usability improvement 2022-01-03 09:35:42 +00:00
Mario
e8030e29d9 remove logging 2022-01-02 20:46:44 +00:00
Mario
df8bb0596a port new_token from zap, fixes and more cleanup 2022-01-02 20:45:25 +00:00
Mario
0003e0b8a5 lockview: fix guest links for profile groups and photos, cleanup 2022-01-02 19:33:10 +00:00
Mario
e42703d557 lockview: provide guest links for private resources 2022-01-02 08:49:36 +00:00
Mario
4636e56395 minor theme fixes 2021-12-29 18:45:03 +00:00
Mario
27ebeffad4 update_poll() can be called many times in a row for the same item if a multiple poll is being updated. This could result in the queueworker not processing duplicates. We are now adding the source item mid to the notifier call as the third argument (fragment) so that the queueworker will not think they are duplicates. The fragment is also passed to the deliver_hooks call in the notifier 2021-12-22 09:50:50 +00:00
Mario
07110cee17 add the title to the object 2021-12-21 09:58:07 +00:00
Mario
afa1f1416b string change 2021-12-21 09:20:53 +00:00
Mario
f8dfcab0ca string change 2021-12-21 09:20:21 +00:00
Mario
e14fd920d6 version 2021-12-21 09:19:07 +00:00
Mario
8c10fdae5b missing label 2021-12-21 09:14:48 +00:00
Mario
eee027d9ff Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-12-21 09:09:43 +00:00
Mario
1b1fb5d26a improve pconfig sync at the sending side 2021-12-21 09:08:36 +00:00
Mario Vavti
c36e0805d8 improve pconfig sync at the receiving side 2021-12-21 10:04:51 +01:00
Mario
5c56041185 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-12-18 19:45:42 +00:00
Mario
5aefe0b74f guest token xchan_network = "token" and remove permission checks since the guest tokens are now added to the abook automatically 2021-12-18 19:09:15 +00:00
Max Kostikov
20e0359efd Merge branch 'dev' into 'dev'
Update Russian translation

See merge request hubzilla/core!1995
2021-12-17 22:42:36 +00:00
Max Kostikov
9c790e5a90 Update Russian translation 2021-12-18 00:39:47 +02:00
Mario
9c79b5be77 version 2021-12-17 21:10:00 +00:00
Mario
2d9a4f4e42 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-12-17 21:08:54 +00:00
Mario
565602538c main.js simplify notify_id checks 2021-12-17 21:08:20 +00:00
Mario Vavti
78972725ae mod tokens cleanup and fixes 2021-12-17 22:05:45 +01:00
Mario
5ab90f7791 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-12-17 20:29:06 +01:00
Mario
b90d98fc2b implement background deleting of items in contact_remove() 2021-12-17 20:26:36 +01:00
Mario
eca3ae393b daemon to delete abbok items in the background 2021-12-17 19:25:28 +00:00
Mario
2bd69495d2 missing file 2021-12-17 19:52:54 +01:00
Mario
bfd3da43ac access token refactor 2021-12-17 19:48:09 +01:00
Mario
32a9eaf3b6 update db schemas 2021-12-17 15:30:26 +01:00
Mario
91cea1f28a add template 2021-12-17 15:01:25 +01:00
Mario
220ed35f58 implement contact role deletion 2021-12-17 14:59:25 +01:00
Mario
b1cf5d4e44 nag channel owners to select achannel role if they have not yet done so yet 2021-12-15 12:58:33 +00:00
Mario
fe330ec1bb bump version 2021-12-15 12:18:07 +00:00
Mario
2968bf8241 merge branch perms_ng into dev 2021-12-15 12:17:19 +00:00
Mario Vavti
a40babbf0d version 6.4.2 2021-12-14 11:12:43 +01:00
Mario Vavti
7822257e1c Merge branch 'dev' 2021-12-14 11:11:57 +01:00
Mario Vavti
b37165c62b changelog 2021-12-14 11:09:27 +01:00
Mario Vavti
5e9d267959 mod sse_bs: when processing returned mids, do not assume they are b64 encoded 2021-12-14 11:04:26 +01:00
Mario Vavti
1a1e6b6810 Announce missing in is_response_activity() 2021-12-11 10:56:50 +01:00
Mario Vavti
88140002e7 Merge branch 'dev' 2021-12-03 16:26:10 +01:00
Mario Vavti
efc203d958 fix blured profile images and pdl files 2021-12-03 16:25:42 +01:00
Mario Vavti
42d4cdcc39 Merge branch 'dev' 2021-12-03 15:13:57 +01:00
Mario Vavti
fd433b3eb6 main_width in rem 2021-12-03 15:10:59 +01:00
Mario Vavti
7483adb8ad Merge branch 'dev' 2021-12-03 14:55:39 +01:00
Mario Vavti
45fd462f80 fix content width for dark schema 2021-12-03 14:54:47 +01:00
Mario Vavti
807003adf7 version 6.4.1 2021-12-03 13:47:58 +01:00
Mario Vavti
1b0a17c7db Merge branch 'dev' 2021-12-03 13:46:41 +01:00
Mario Vavti
60b145833c changelog and version 2021-12-03 13:45:19 +01:00
Mario Vavti
39458b2ba8 fix hubloc_site_id in fix_system_urls() on detected site rename events and during sync_locations() if it changed or was stored incorrectly. 2021-11-30 10:50:31 +01:00
Mario Vavti
5de38b3632 mod channel: lookup zotinfo based on hash instead of address 2021-11-29 11:26:29 +01:00
Mario Vavti
6ced3426cf do not deliver to the same channel multiple times in case duplicate hubloc entries for the channel exist 2021-11-29 11:17:14 +01:00
Mario
43460c9d19 re-add css that was removed by accident 2021-11-26 19:29:43 +00:00
Mario
ca17fb01bc profile cleanup and fixes 2021-11-25 07:54:53 +00:00
Mario
a6f65aa9c5 add source option so that the right makros will be used in attach_store() 2021-11-25 07:44:31 +00:00
Mario
ae9a7727d6 maximum width of content region is now calculated in rem 2021-11-24 20:47:18 +00:00
Mario
3ac5d5257b fix issue where likes notices are emited allthough they are actually disabled 2021-11-24 20:19:35 +00:00
Mario
668d7c73ed address issue #1651 (reload page after comment or like) 2021-11-24 20:01:16 +00:00
Mario
c95f708c91 port httpmeta from zap 2021-11-24 09:08:11 +00:00
Mario
06e214e567 more work on responsive aside 2021-11-23 09:17:04 +00:00
Mario
18f8cafee0 minor vcard fixes 2021-11-21 20:17:14 +00:00
Mario
6bd6afac05 streamline profile vcard with xchan vcard 2021-11-21 19:50:36 +00:00
Mario
fe7ecede70 Merge branch '6.4RC' 2021-11-09 09:10:19 +00:00
Mario
089708ab9f version 6.4 2021-11-09 09:08:52 +00:00
Mario
daa844c038 Merge branch 'dev' into 6.4RC 2021-11-09 09:07:55 +00:00
Mario
4049992228 update changelog 2021-11-09 09:07:37 +00:00
Mario
7c576c91b7 Merge branch 'dev' into 6.4RC 2021-10-30 09:08:32 +00:00
Mario
17bd364614 remove logging 2021-10-30 09:07:56 +00:00
Mario
25ffd39519 Merge branch 'dev' into 6.4RC 2021-10-30 09:05:24 +00:00
Mario
acfa527e3e css fixes 2021-10-30 09:04:50 +00:00
Mario
b512416cb3 fix version 2021-10-28 14:17:53 +00:00
Mario
bd116c53a1 update changelog 2021-10-28 14:14:20 +00:00
Mario
1251fca256 remove mcrypt from the docs 2021-10-27 18:42:25 +00:00
Mario
b2fe21709c update sbom 2021-10-27 12:13:47 +00:00
Mario
c082d12b59 bump dev version 2021-10-27 08:11:09 +00:00
Mario
9a4ca44255 version and strings 2021-10-27 08:05:57 +00:00
Mario
6fce9a41b0 remove logging 2021-10-25 08:12:17 +00:00
Mario
65acdd7b15 mod regate: auto-connect the invitee with the inviter if auto channel create is enabled (default). fix issue where the default group was not set in the return array of create_identity() 2021-10-25 08:03:12 +00:00
Mario
a209374cbd invite: fix variable name in template and do not substitute the Â@ in the webfinger address with @+ 2021-10-22 09:52:30 +00:00
Mario
c1bb87db98 mod item: cleanup and remove some unused variables 2021-10-22 08:16:19 +00:00
Mario
8bae40449f remove library/urlify and use vendor/jbroadway/urlify instead 2021-10-22 08:07:19 +00:00
Mario
bc3bb4694a mod import: formating 2021-10-22 07:50:35 +00:00
Mario
22588e58f3 install urlify via composer 2021-10-22 07:46:30 +00:00
Mario
69ba4eb055 whitespace 2021-10-22 07:27:37 +00:00
Mario
687b9fb6a2 comment out warning about db version differences - it is not relevant 2021-10-22 07:26:30 +00:00
Mario
9b9ca9695c import_items: cleanup 2021-10-21 10:36:31 +00:00
Mario
6357c69868 import_items: provide a possibility to import exported zip files by year 2021-10-21 10:29:44 +00:00
Mario
15cb7ab7a1 nativeWikiPage: more whitespace and cleanup 2021-10-20 19:54:59 +00:00
Mario Vavti
2c741bd24d fix for issue #1640 - use item_store() instead of post_activity_item() which is checking for post_comment permission which we might not have in this case. item_store() requires slightly more data but basically does the same. Also some minor improvements and whitespace changes. 2021-10-20 21:35:42 +02:00
Mario
3f72a3beb6 fix xchan vcard rendering 2021-10-20 09:57:45 +00:00
Mario
42de18d96d fix dutch registration email verification email template
(cherry picked from commit c7515b8687)
2021-10-20 10:09:19 +02:00
Mario
4bdbdab399 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-20 08:08:09 +00:00
Mario
c7515b8687 fix dutch registration email verification email template 2021-10-20 08:07:06 +00:00
Mario
c79e39a488 improve code comments 2021-10-20 08:06:29 +00:00
Mario
d639104b71 invite: use the actual set language and check if we have a template for it
(cherry picked from commit c66a5ba732)
2021-10-18 21:44:06 +02:00
Mario
c66a5ba732 invite: use the actual set language and check if we have a template for it 2021-10-18 21:41:08 +02:00
Mario
79f9b49dd7 provide tags indicating the supported protocols in the actor object and probe for zot6 if appropiate 2021-10-18 07:53:45 +00:00
Mario
8b542c250a mod invite: skip the pre-check for valid email 2021-10-16 20:07:34 +00:00
Mario
55237683d2 xrd whitespace 2021-10-16 19:24:58 +00:00
Mario
27401794e1 rename functions after heavy lifting and expand custom sections to all possible sections 2021-10-16 18:10:23 +00:00
Mario
862a7c2dba manifest: remove orientation so that os default will be used and add default theme_color 2021-10-16 14:03:35 +00:00
Mario
bb31a4620e notes: provide translateable strings 2021-10-15 19:30:38 +00:00
Mario
7aaade8b23 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-15 19:17:31 +00:00
Mario
a622f533ad some heavylifting on manual item export 2021-10-15 19:17:06 +00:00
Max Kostikov
0dd27dabd6 Merge branch 'dev' into 'dev'
Update Russian translation

See merge request hubzilla/core!1994
2021-10-15 15:08:16 +00:00
Max Kostikov
8ab464acf2 Update Russian translation 2021-10-15 16:55:21 +02:00
Mario
db18438db2 escape_tags only once 2021-10-15 08:15:41 +00:00
Mario
f885f98611 use the proprt syntax 2021-10-14 18:59:41 +00:00
Mario
9b8b85545f Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-14 18:16:27 +00:00
Mario
532b479f96 provide local copies of the w3.org jsonld documents - addresses issue #1637 2021-10-14 18:15:38 +00:00
Mario
422dfca6d7 fix cacert missing some certificates after last update 2021-10-14 18:13:02 +00:00
Mario
c3d1474f59 Merge branch 'dev' into 'dev'
Support IDNA URLs embedding

See merge request hubzilla/core!1990
2021-10-14 13:16:18 +00:00
Max Kostikov
99873504e4 Support IDNA URLs embedding 2021-10-14 13:16:16 +00:00
Mario
138a67298d version 2021-10-13 11:36:18 +00:00
Mario
aca1551e86 implement AS representation of photos and albums, cleanup and minor fixes 2021-10-13 09:23:59 +00:00
Mario
ca32850a32 fix image object as per AS2 2021-10-12 20:06:11 +00:00
Mario
ba6f069997 HTTPSig: do not query for hubloc_addr 2021-10-12 19:05:21 +00:00
Mario
c6c6b52ccd notes: minor css fix 2021-10-12 18:59:52 +00:00
Mario
96c334e730 bbcode for notes app 2021-10-12 11:27:36 +00:00
Mario
3e503ec3a3 Revert "httpsig: add parentheses"
This reverts commit e455fae334.
2021-10-12 11:25:44 +00:00
Mario
e455fae334 httpsig: add parentheses 2021-10-12 11:25:24 +00:00
Mario
29f3dc2fa3 notifications_widget: fix regression hiding the #nav- * -sub element when there are no more notifications 2021-10-11 09:12:05 +00:00
Mario
aa41b16757 look for hubloc_id_url or hubloc_hash, otherwise we will always fetch the key from the net on platforms where those are different (e.g. mastodon). 2021-10-11 07:41:12 +00:00
Mario
f4d59abe41 streamline logic 2021-10-10 21:13:30 +00:00
Mario
18de58fd8b HTTPSig: cleanup, add some doco and implement missing force mechanism for fetching keys 2021-10-10 21:00:22 +00:00
Mario
4c2b188f8b if we do not have a display path, we are looking at the root directory 2021-10-10 18:16:13 +00:00
Mario
08f65420f4 messages widget: show user-plus icon for connection requests 2021-10-10 09:49:12 +00:00
Mario
b90228b319 make sure we do not select removed channels as importer in onepoll 2021-10-10 08:37:44 +00:00
Mario
ad2c165f26 bbcode: deal with codeblocks before we escape other tags 2021-10-10 08:10:49 +00:00
Mario
af5218593a update composer libs 2021-10-10 07:59:31 +00:00
Mario
f19acd9f50 remove redundant variable and provide missing phpdoc 2021-10-10 07:56:42 +00:00
Mario
59940e7f05 make sure to not include removed channels in xrd or webfinger queries 2021-10-09 19:46:27 +00:00
Mario Vavti
094536e633 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-09 20:08:23 +02:00
Mario Vavti
4f2a439873 mod import: only run automatic content and files import if api version is compatible 2021-10-09 20:08:11 +02:00
Mario
63bdab2b5f use Libzot::fetch_conversation to fetch conversations from search and add an optional force argument to process_delivery 2021-10-09 15:00:21 +00:00
Mario
14733f8482 more bs5 fixes 2021-10-09 08:58:11 +00:00
Mario
b830bbd084 remove hz_syslog and deal with intro notifications in the messages widget 2021-10-08 17:35:36 +00:00
Mario
c1894c5a39 bump version 2021-10-08 13:01:09 +00:00
Mario
e91e488e1f Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-08 12:26:08 +00:00
Mario
e6dac085cb update composer libs 2021-10-08 12:24:19 +00:00
Mario
5023a5b56b Merge branch 'Aksel-dev-patch-31902' into 'dev'
German roles.bb

See merge request hubzilla/core!1992
2021-10-06 15:22:43 +00:00
Aksel K
32d37d1178 Aktualisieren doc/de/roles.bb 2021-10-06 15:12:55 +00:00
Aksel K
848221649e Neue Datei hochladen 2021-10-06 14:56:01 +00:00
Mario
f5f357060b fix alt_pager() 2021-10-06 14:32:49 +00:00
Mario
98eea41865 fix drop_query_params() if no query params are left 2021-10-06 14:32:25 +00:00
Mario
8ac529f5ae simplify 2021-10-06 08:43:40 +00:00
Mario
b5fed08dd4 really de-duplicate entries for dead hubs 2021-10-06 08:33:10 +00:00
Mario
9bfcaf2669 do not show duplicate entries for dead hubs 2021-10-06 08:05:16 +00:00
Mario Vavti
af05ee7e1c Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-06 09:40:15 +02:00
Mario
3918439020 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-06 07:39:34 +00:00
Mario
3b71e115a4 implement sync logic in mod appman 2021-10-06 07:39:10 +00:00
Mario Vavti
c33660a015 move sync logic for apps to mod appman. this way we can re-use the functions without creating sync loops. 2021-10-06 09:36:14 +02:00
Max Kostikov
7963d4cb8f Merge branch 'ab-dev' into 'dev'
Polish translation: fixes and updates

See merge request hubzilla/core!1989
2021-10-05 18:27:42 +00:00
Mario
16281e0e7f disable app sync due to a possible loop 2021-10-04 19:16:38 +00:00
Mario Vavti
72c58e60d7 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-04 20:32:32 +02:00
Mario Vavti
4b48ff2868 also sync the undestroy action 2021-10-04 20:32:19 +02:00
Mario
8e212d30d0 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-04 18:21:08 +00:00
Mario
b9ae396e74 unset id before syncing 2021-10-04 18:20:51 +00:00
Mario Vavti
3a38946f8a more app sync 2021-10-04 20:19:16 +02:00
Andrzej Budziński
d0d3a5454e Polish translation: fixes and updates 2021-10-04 19:24:16 +02:00
Mario Vavti
04c6d77d2c set the sync flag to true 2021-10-04 14:14:30 +02:00
Mario Vavti
c2c5730d00 more app sync fixes 2021-10-04 14:08:39 +02:00
Mario Vavti
21a6dffd5f Revert "more app sync fixes"
This reverts commit 7430989212.
2021-10-04 13:54:45 +02:00
Mario Vavti
7430989212 more app sync fixes 2021-10-04 13:41:54 +02:00
Mario Vavti
2e26a13f81 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-04 13:31:30 +02:00
Mario Vavti
404ebd4d5e app sync fixes - part 2 2021-10-04 13:31:26 +02:00
Mario
11ea7bf0fc app sync fixes - part 1 2021-10-04 11:25:52 +00:00
Mario
6da7fe7d27 provide system app sync 2021-10-04 09:24:13 +00:00
Mario
538e540531 commment out deprecated code 2021-10-04 08:46:56 +00:00
Mario
53138f4b09 fix typo 2021-10-04 08:32:36 +00:00
Mario
3d71367f30 add pem 2021-10-04 08:31:00 +00:00
Mario
bb52ff9b4b fix site lookup and minor cleanup 2021-10-04 08:28:57 +00:00
Mario
ab5b82221c update certs 2021-10-03 18:24:26 +00:00
Mario
5c3b89a68b mod locs: do not show drop icon for local clones and get rid of redundant for loop 2021-10-03 17:48:00 +00:00
Mario
da923d7749 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-03 16:54:04 +00:00
Mario
55d905fdd9 clone channel delete issues - part 2 2021-10-03 16:53:48 +00:00
Mario Vavti
4ba70a3fae clone channel delete issues - part 1 2021-10-03 18:52:14 +02:00
Mario
a5835b0e85 disable pg tests 2021-10-03 12:59:10 +00:00
Mario
ed4e5c9bcf version 6.2.2 2021-10-03 12:40:48 +00:00
Mario
d8793de629 make sure we have a sender here
(cherry picked from commit 0722188ea6)
2021-10-03 14:38:59 +02:00
Mario
f45cb38cd3 make sure to return if we have no xchan here
(cherry picked from commit d6f81e139a)
2021-10-03 14:38:29 +02:00
Mario
0722188ea6 make sure we have a sender here 2021-10-03 12:36:17 +00:00
Mario
d6f81e139a make sure to return if we have no xchan here 2021-10-03 12:22:33 +00:00
Mario
58ee147653 fix typo 2021-10-03 08:20:16 +00:00
Mario
28ad6a6883 remove redundant query and exit with status 410 if channel was removed 2021-10-03 07:18:49 +00:00
Mario
9de8aefa98 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-03 07:11:27 +00:00
Mario
66c273ae8d channel_purge: raise limit by factor of 10 2021-10-03 07:11:12 +00:00
Mario Vavti
b27fbf209e Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-03 09:05:54 +02:00
Mario Vavti
4afd1ac705 add optional $removed argument to channelx_by functions to allow inclusion of removed channels which is required (mod channel, daemon notifier -> refresh_all) if a channel got removed also remove caching to App so we always get a fresh copy. In import_hublocs() add site_id to the array passed to Libzot::gethubs() because it is expected there. 2021-10-03 09:05:32 +02:00
Mario
d556ef59e6 sigh... 2021-10-02 14:09:55 +00:00
Mario
81e972b85c another try on pg test 2021-10-02 14:02:58 +00:00
Mario
3bb04cc323 nope - disable pg test until the issue is resolved 2021-10-02 13:28:35 +00:00
Mario
9ff43f081f another try to fix pg test 2021-10-02 13:23:44 +00:00
Mario
8c014e3b6b see if this fixes pgsql test issue 2021-10-02 13:15:17 +00:00
Mario
d8378a17c9 nav move header outsite of container - fixes app sticking above header when sorting 2021-10-02 13:04:10 +00:00
Mario
574a01727e fix removed channels counted for max id check and disable moving until we decide what to do with it 2021-10-02 12:56:35 +00:00
Mario
b5cea3301d cleanup api_auth and make it fetch the identity if we do not have it cached yet 2021-10-02 12:28:13 +00:00
Mario Vavti
9346a06f35 also check for vnotify 2021-10-01 22:48:09 +02:00
Mario Vavti
250e917c6e only show sse pubs if the app is installed 2021-10-01 22:44:26 +02:00
Mario Vavti
7b1cd37cd6 only bootstrap pubs if the app is installed 2021-10-01 22:30:00 +02:00
Mario Vavti
47e83a15c1 import_progress: deal with the situation where items/files are being imported but there are none to import 2021-10-01 21:52:30 +02:00
Mario
597e847a3b css fixes 2021-10-01 17:17:02 +00:00
Mario Vavti
47f3b41d5e remove redundant notice 2021-10-01 19:15:39 +02:00
Mario Vavti
e7f4c1ffd1 missing template class logic 2021-10-01 18:10:32 +02:00
Mario Vavti
9a87b8bf1b only allow resume if we are not completed yet 2021-10-01 17:24:19 +02:00
Mario Vavti
dac27aab68 remove logging 2021-10-01 17:11:14 +02:00
Mario Vavti
069a5429c9 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-01 17:09:54 +02:00
Mario Vavti
b1eaa810ce improve channel import progress page 2021-10-01 17:09:36 +02:00
Mario
e5e3c268a2 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-10-01 07:14:37 +00:00
Mario
22dff49673 improve file upload performance: start uploading next file only after the previous finished, make sure to not return when uploading via ajax 2021-10-01 07:14:05 +00:00
Max Kostikov
591905c282 Merge branch 'dev' into 'dev'
Fix possible storage conversion stuck on file save error

See merge request hubzilla/core!1988
2021-09-30 20:45:02 +00:00
Mario Vavti
6ed160e4fa wording 2021-09-30 17:50:36 +02:00
Mario Vavti
d462230b82 minor display change 2021-09-30 17:43:29 +02:00
Mario
e48ed9d06d Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-09-30 15:38:49 +00:00
Mario Vavti
6236869ebe implement file totals and add mod import_progress 2021-09-30 17:34:30 +02:00
Mario
2c7c9ae2d7 provide files total 2021-09-30 15:33:10 +00:00
Mario Vavti
b9b4e71f7d provide a very simple status page for content import 2021-09-30 12:25:03 +02:00
Mario Vavti
b0bf646d71 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-09-30 12:10:11 +02:00
Mario Vavti
5cb5ecdb54 implement channel purge daemon and send a notifier purge_all message if the xchan was deleted 2021-09-30 12:09:50 +02:00
Mario Vavti
44fa5ac9a1 add channel purge daemon 2021-09-30 12:06:28 +02:00
Max Kostikov
9a19f7eac9 Fix possible storage conversion stuck on file save error 2021-09-29 23:01:27 +02:00
Mario
af0896bb8b a possible fix for the nested list test 2021-09-29 20:57:16 +00:00
Mario
817237ef77 composer update league/html-to-markdown 2021-09-29 20:36:32 +00:00
Mario Vavti
fbefff6eed Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-09-29 21:47:36 +02:00
Mario Vavti
4ba47698d7 revise content import/export part 2 2021-09-29 21:47:12 +02:00
Mario
2688abf25a revise content import/export part 1 2021-09-29 19:43:22 +00:00
Mario
5eb79bd51e importer daemons: do not call with zap_compat flag and use return instead of kill so that queueworker can go on 2021-09-27 21:58:26 +00:00
Mario
d3f5f778a4 cleanup mod import and implement importer daemons ported from zap 2021-09-27 20:49:37 +02:00
Mario
3eeb2b0ee4 add importer daemons ported from zap 2021-09-27 18:37:09 +00:00
Mario
230a1919dd add outbox 2021-09-27 18:36:04 +00:00
Mario
14004fbf7f support un-starring of apps in app bin 2021-09-27 11:13:16 +00:00
Mario
73e8af98f2 fix notification panel collapsed state not saved if closed manually 2021-09-27 07:01:33 +00:00
Mario
0117a0019b make sure to also include ap hublocs here. we will select zot6 later in the function if present 2021-09-26 18:10:34 +00:00
Mario
35ff8781f0 always promote the outbox 2021-09-26 12:15:14 +00:00
Mario
2d17442f28 externals: if dealing with AP profiles also fetch comments. there is no way to fetch conversations later 2021-09-25 10:17:45 +00:00
Mario
5c179522bb bring back externals (slightly different approach), rename zotfeed to outbox for consistency wit AP naming. warning: this commit requires update of addons! 2021-09-25 09:03:16 +00:00
Mario
b35d95da52 dismiss deleted xchans in query 2021-09-25 08:47:39 +00:00
Mario
feca5afaa5 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-09-23 20:43:49 +00:00
Mario
9cb5274d30 add zot6 specific handling to onepoll 2021-09-23 20:43:37 +00:00
Mario Vavti
2870fd46da Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-09-23 21:54:12 +02:00
Mario Vavti
dff8ef91a6 implement the top option in items_fetch 2021-09-23 21:52:27 +02:00
Mario
0588975e37 Ãfix wrong variable name and streamline icon with unseen notifications icon 2021-09-23 14:59:40 +00:00
Mario
e0600b241a make sure we do not mix up likes on direct messages and commments on direct messages in notices 2021-09-23 12:45:55 +00:00
Mario
d19aa8fb3b make sure entries is initialized 2021-09-23 11:17:45 +00:00
Mario
989a4f3d49 add notices tab to HQ widget 2021-09-23 10:42:37 +00:00
Mario
2a2c4d3e9c fix bs regression 2021-09-23 08:50:44 +00:00
Mario
a451449766 Revert "composer update bootstrap to version 5.1.1"
This reverts commit 89e4006b2d.
2021-09-23 08:33:36 +00:00
Mario
3347fab105 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2021-09-22 16:16:50 +00:00
Mario
33a6c92629 version 2021-09-22 16:16:35 +00:00
Mario
0707d33493 bootstrap update fixes 2021-09-22 16:14:28 +00:00
Mario
89e4006b2d composer update bootstrap to version 5.1.1 2021-09-22 06:38:27 +00:00
Mario Vavti
4eb8921635 improve item_url_replace() - fixes #1507 2021-09-21 11:07:23 +02:00
Mario
26ac452c96 mod manage: do not query info which we do not use in the UI and probably should not due to performance considerations 2021-09-21 07:40:00 +00:00
Mario
7122e1522a perform profile sync after all the fields are updated otherwise we will sync outdated data 2021-09-21 07:33:02 +00:00
Mario
5497adfde6 add option to mark all notices of a thread read if a notice of the thread is clicked (default 1) and fix a php error in find_parent() if $act->obj is not an array 2021-09-18 18:06:32 +00:00
Mario
d4c2e50285 changelog and version 2021-09-16 09:50:08 +00:00
Mario
7ba58208f2 version 6.2.1 and changelog 2021-09-16 08:04:59 +00:00
Mario
cc83677711 Merge branch 'master' of https://framagit.org/hubzilla/core 2021-09-16 07:49:43 +00:00
Mario
c28cce1b54 Merge branch 'dev' 2021-09-16 07:48:13 +00:00
Mario
9052695a13 typo 2021-09-15 11:03:19 +00:00
Mario
46b8db53ab provide a get_cached_actor_provider hook and improve the author/owner handling in Libzot::import() 2021-09-15 10:59:16 +00:00
Mario
0b716b3cc7 fix $desturl set to wrong value (null) 2021-09-13 11:36:41 +00:00
Mario
d115e09abc do not process summary in cleanup_bbcode() fixes issue #1612 2021-09-12 17:54:20 +00:00
Mario
0f803aa3ae when updating the channel name from profiles make sure to lookup the xchan entry by xchan_hash since xchan_url is set to the primary location but we might be updating from a clone
(cherry picked from commit e5dc3c05df)
2021-09-10 14:34:21 +02:00
Mario
e5dc3c05df when updating the channel name from profiles make sure to lookup the xchan entry by xchan_hash since xchan_url is set to the primary location but we might be updating from a clone 2021-09-10 12:29:05 +00:00
Mario
1cfe5c3e16 fix spacing issue for collapsed pinned apps with an image
(cherry picked from commit e2d0269b5c)
2021-09-10 10:12:39 +02:00
Mario
e2d0269b5c fix spacing issue for collapsed pinned apps with an image 2021-09-10 08:11:19 +00:00
Mario
b95c61d2c2 fix language selector reloading the page if not clicking the icon exactly and bs5 modal issues
(cherry picked from commit 7b0f8f2896)
2021-09-08 21:50:25 +02:00
Mario
7b0f8f2896 fix language selector reloading the page if not clicking the icon exactly and bs5 modal issues 2021-09-08 19:48:42 +00:00
Mario
25a81ea500 update release date 2021-09-08 07:50:57 +00:00
1113 changed files with 83274 additions and 56888 deletions

View File

@@ -115,16 +115,16 @@ php7.3_mariadb10.3:
# PHP7.3 with PostgreSQL latest (11)
php7.3_postgres11:
<<: *job_definition_postgres
artifacts: *artifacts_template
#php7.3_postgres11:
# <<: *job_definition_postgres
# artifacts: *artifacts_template
# PHP7.3 with PostgreSQL latest (11)
php7.3_postgres11:
<<: *job_definition_postgres
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
artifacts: *artifacts_template
#php7.3_postgres11:
# <<: *job_definition_postgres
# image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
# artifacts: *artifacts_template
# Generate Doxygen API Documentation and deploy it as GitLab pages

165
CHANGELOG
View File

@@ -1,3 +1,168 @@
Hubzilla 7.0.1 (2022-01-28)
- Fix removing contacts from privacy groups in the contact edit modal
- Fix escape_tags() messing with URLs in actor_store()
- Fix pagination in the cards module if a category is selected
- Remove unused entries in webfinger
- Remove deprecated mail app from apps
- Set item_hidden for forum comment announces
- Fix relaying of signed messages for activitypub
- Fix contact role permissions not re-assigned if the role permission has changed
- Fix default channel role not set in rare cases
Addons
- Pubcrawl: fix webfinger not returning the fetched URL
- Pubcrawl: improved queue handling for rejected deliveries
Hubzilla 7.0 (2022-01-21)
- Provide theme_color and background_color in App::$theme_info for usage in page meta and manifest
- PWA improvements according to lighthouse
- Refactor mod profile_photo
- Remove core legacy mail code
- Set session samesite cookie flag
- Improve toc bbcode for more flexible usecases
- Deprecate include/group in favor of Lib/AccessList
- Deprecate AccessList::widget()
- Mark forum channel profile images with a small icon in the timelines
- Improve privacy groups UI/UX
- Do not show connections widget if there are no connections
- Remove suggestions widget from various modules
- Provide guest access links for private resources in lockview
- Improve pconfig syncing
- deprecate include/group in favor of Lib/AccessList
- Implement background deleting of items in contact_remove()
- Refactor guest access tokens for better usability and provide quick access
- Refactor permissions handling
- Improved poll rendering
Bugfixes
- Fix items not deleted on remote channel purge
- Fix plink in post_activity_item()
- Fix multiple update_poll() calls dismissed in queueworker
- Fix blocked or ignored contacts displayed in connections
- Fix polls for forum channels
Addons:
- Legacy mail: remove
- Deprecate include/group in favor of Lib/AccessList
- Pubcrawl: support pleroma end time for polls
- Pubcrawl: slightly adjust the way we check mastodon direct messages
- Socialauth: scope support and improvements
Hubzilla 6.4.2 (2021-12-14)
- Fix issue in mod sse_bs where returning message id's were assumed to be base64 encoded
- Fix announce activity type not registered as response activity
Hubzilla 6.4.1 (2021-12-03)
- Fix hubloc_site_id in fix_system_urls() on detected site rename events
- Fix duplicate deliveries if duplicate hublocs available
- Redesign profile vcard for improved responsive handling
- Fix profile photos not stored in profile photo folder
- Maximum width of content region is now calculated in rem for improved responsive handling
- Fix likes notices emited allthough they are disabled
- Fix page not reloaded after comment/like in mod photos - issue #1651
- Port improved Lib/HttpMeta from zap
- Improved responsive aside
Hubzilla 6.4 (2021-11-09)
- Automatically connect the invitee with the inviting channel
- Use the composer version of urlify
- Implement zip file import of exported items from mod uexport
- Start sending supported protocols with the actor object
- Split up manual item export to separate sections
- Serve w3.org jsonld documents locally - issue #1637
- Support IDNA URL embedding
- Improve handling of re-installed hubs in lib HTTPSig
- BBcode support for notes widget/app
- Implement a force flag for HTTPSig::get_key()
- Update composer libs
- Use Libzot::fetch_conversation for manual content import
- Implement optional force argument in Libzot::process_delivery
- Improve german doco
- Move sync logic for apps to mod appman
- Provide sync for system apps
- Update certificates
- Return status code 410 if a channel is deleted
- Add optional argument to channelx_by_* functions to allow inclusion of removed channels
- Improve file upload performance
- Introduce progress tracking for channel cloning via network (not compatible with cloning from older versions)
- Improve channel delete performance by moving some actions to background tasks
- Introduce all in one channel cloning via network (not compatible with cloning from older versions)
- Rename zotfeed to outbox but keep an alias for compatibility
- Implement apps un-starring from the app bin via drag and drop
- Re-implement the externals daemon
- Add zot6 specific handling to onepoll
- Implement the top option in items_fetch() which will only return top level items
- Add notices tab to HQ widget
- Improve mod manage performance
- Add option to mark all notices of a thread read if a notice of the thread is clicked (default true)
- Provide a get_cached_actor_provider hook and improve the author/owner handling in Libzot::import()
Bugfixes
- Fix issue where remote channels could not create wiki pages due to wron permission check - issue #1640
- Fix dutch registration email template
- Fix selection of invite template
- Fix too restrictive email check in mod invite
- Fix photos and albums ActivityStreams 2 representation
- Fix keys always fetched from network in lib HTTPSig for some AP implementations
- Fix album display of root directory
- Fix onepoll importing to deleted channels
- Fix rendering of image tags in codeblocks
- Fix webfinger and xrd providing results for removed channels
- Fix alt_pager() providing too many arguments
- Fix drop_query_params() if no query params are provided
- Fix duplicate entries for dead hubs in delivery report
- Fix site lookup
- Fix mod locs displaying drop icons for local channels
- Fix multiple issues with propagating deletes of cloned channels
- Fix apps can be draged outsite of drop areas
- Fix removed channels counted in max id check
- Fix api_auth not fetching the id if it was not cached
- Fix public stream unseen notifications displayed allthough the app is not installed
- Fix possible storage conversion stuck on file save error
- Fix notification panel collapsed state not saved if closed manually
- Fix find_best_identity() dismissing AP hublocs
- Fix likes and commments on direct messages mixed up in notices
- Fix rewrite of links to resources in body fails if nicknames of clones differ - issue #1507
- Fix syncing outdated data due to profile sync done before the fields were updated
- Fix $desturl set to wrong value (null)
Addons
- Cart: add settings URL to the apd file
- Diaspora: remove deprecated included
- Cart: remove deprecated include
- Openid: remove library/urlify in favor of composer installed versions
- Pubcrawl: provide tags indicating the supported protocols
- Pubcrawl: if we do not get an uuid, create a v5 uuid from the mid
- Cart: fix rendering regressions from bootstrap5 upgrade
- Upgrade_info: fix dismiss button
- Pubcrawl: move fetch_provider from core to addon
- Diaspora: fix regression in fetch_provider
- Content_import: fix syntax error
- Queueworker: update priorities
- Pubcrawl: only lookup announce author if we actually deal with an announce
- Pubcrawl: make sure we have the best identity before we make the abook lookup
- Pubcrawl: outbox moved to core
- Diaspora: implement the get_cached_actor_provider hook
Hubzilla 6.2.2 (2021-10-03)
- Fix an issue which could lead to loss of photos under certain conditions
Hubzilla 6.2.1 (2021-09-16)
- Fix regression introduced in 6.2 where Diaspora comments on Hubzilla posts were not relayed
- Fix wrong variable used for refresh under certain conditions
- Fix issue where summary tag was processed in cleanup_bbcode() while it shoud not
- Fix issue where profile name change was not applied if done from non primary location
- Fix spacing issue for collapsed pinned apps with an image
- Fix language selector reloading the page if not clicking exactly the icon
- Fix regression displaying bootstrap modals introduced in 6.2
Hubzilla 6.2 (2021-09-08)
- Deprecate the custom highlight [hl] bbcode in favor of [mark] which is a html5 standard
- Check post_mail permission when receiving a direct message

19
SBOM.md
View File

@@ -2,20 +2,21 @@
|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.2.0|MIT|https://github.com/brick/math.git|
|blueimp/jquery-file-upload|10.32.0.0|MIT|https://github.com/vkhramtsov/jQuery-File-Upload.git|
|brick/math|0.9.3.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.1.0.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|5.0.0.0|MIT|https://github.com/thephpleague/html-to-markdown.git|
|jbroadway/urlify|1.2.2.0|BSD-3-Clause-Clear|https://github.com/jbroadway/urlify.git|
|league/html-to-markdown|5.0.1.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|
|pear/text_languagedetect|1.0.1.0|BSD-2-Clause|https://github.com/pear/Text_LanguageDetect.git|
|phpseclib/phpseclib|2.0.30.0|MIT|https://github.com/phpseclib/phpseclib.git|
|phpseclib/phpseclib|2.0.33.0|MIT|https://github.com/phpseclib/phpseclib.git|
|psr/log|1.1.4.0|MIT|https://github.com/php-fig/log.git|
|ramsey/collection|1.1.3.0|MIT|https://github.com/ramsey/collection.git|
|ramsey/uuid|4.1.1.0|MIT|https://github.com/ramsey/uuid.git|
|ramsey/collection|1.2.2.0|MIT|https://github.com/ramsey/collection.git|
|ramsey/uuid|4.2.3.0|MIT|https://github.com/ramsey/uuid.git|
|sabre/dav|4.1.5.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|
@@ -25,7 +26,11 @@
|simplepie/simplepie|1.5.6.0|BSD-3-Clause|https://github.com/simplepie/simplepie.git|
|smarty/smarty|3.1.39.0|LGPL-3.0|https://github.com/smarty-php/smarty.git|
|symfony/polyfill-ctype|1.23.0.0|MIT|https://github.com/symfony/polyfill-ctype.git|
|twbs/bootstrap|4.6.0.0|MIT|https://github.com/twbs/bootstrap.git|
|symfony/polyfill-php80|1.23.1.0|MIT|https://github.com/symfony/polyfill-php80.git|
|symfony/polyfill-php81|1.23.0.0|MIT|https://github.com/symfony/polyfill-php81.git|
|twbs/bootstrap|5.1.3.0|MIT|https://github.com/twbs/bootstrap.git|
|voku/portable-ascii|1.5.6.0|MIT|https://github.com/voku/portable-ascii.git|
|voku/stop-words|2.0.1.0|MIT|https://github.com/voku/stop-words.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

@@ -89,4 +89,4 @@ class PermissionLimits {
return false;
}
}
}

View File

@@ -17,7 +17,7 @@ class PermissionRoles {
* @return number
*/
static public function version() {
return 2;
return 3;
}
static function role_perms($role) {
@@ -27,6 +27,54 @@ class PermissionRoles {
$ret['role'] = $role;
switch($role) {
case 'public':
$ret['default_collection'] = false;
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki',
'send_stream', 'post_comments', 'post_mail', 'post_wall', 'chat', 'post_like', 'republish'
];
$ret['limits'] = PermissionLimits::Std_Limits();
$ret['limits']['post_comments'] = PERMS_AUTHED;
$ret['limits']['post_mail'] = PERMS_AUTHED;
$ret['limits']['post_like'] = PERMS_AUTHED;
$ret['limits']['chat'] = PERMS_AUTHED;
break;
// Hubzilla default role
case 'personal':
$ret['default_collection'] = true;
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki',
'send_stream', 'post_comments', 'post_mail', 'chat', 'post_like'
];
$ret['limits'] = PermissionLimits::Std_Limits();
$ret['limits']['view_contacts'] = PERMS_SPECIFIC;
break;
case 'group':
$ret['default_collection'] = false;
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'post_wall', 'post_comments',
'post_mail', 'post_like', 'chat'
];
$ret['limits'] = PermissionLimits::Std_Limits();
$ret['channel_type'] = 'group';
break;
// Provide some defaults for the custom role so that we do not start
// with no permissions at all if we create a new channel with this role
case 'custom':
$ret['default_collection'] = true;
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki',
'send_stream', 'post_comments', 'post_mail', 'chat', 'post_like'
];
$ret['limits'] = PermissionLimits::Std_Limits();
break;
/*
case 'social':
$ret['perms_auto'] = false;
$ret['default_collection'] = false;
@@ -193,13 +241,14 @@ class PermissionRoles {
$ret['channel_type'] = 'group';
break;
*/
case 'custom':
default:
break;
}
$x = get_config('system','role_perms');
// let system settings over-ride any or all
if($x && is_array($x) && array_key_exists($role,$x))
$ret = array_merge($ret,$x[$role]);
@@ -284,6 +333,7 @@ class PermissionRoles {
*/
static public function roles() {
$roles = [
t('Social Networking') => [
'social_federation' => t('Social - Federation'),
'social' => t('Social - Mostly Public'),
@@ -317,4 +367,29 @@ class PermissionRoles {
return $roles;
}
/**
* @brief Array with translated role names and grouping.
*
* Return an associative array with role names that can be used
* to create select groups like in \e field_select_grouped.tpl.
*
* @return array
*/
static public function channel_roles() {
$channel_roles = [
//'public' => [t('Public'), t('A very permissive role suited for participation in the fediverse')],
//'personal' => [t('Personal'), t('The $Projectname default role suited for a personal channel')],
//'forum' => [t('Community forum'), t('This role configures your channel to act as an community forum')],
//'custom' => [t('Custom'), t('This role comes with the presets of the personal role but allows you to configure it to your needs')]
'public' => t('Public'),
'personal' => t('Personal'),
'group' => t('Community forum'),
'custom' => t('Custom')
];
call_hooks('list_channel_roles', $channel_roles);
return $channel_roles;
}
}

View File

@@ -41,7 +41,7 @@ class Permissions {
* @return number
*/
static public function version() {
return 2;
return 3;
}
/**
@@ -67,9 +67,9 @@ class Permissions {
'post_comments' => t('Can comment on or like my posts'),
'post_mail' => t('Can send me direct messages'),
'post_like' => t('Can like/dislike profiles and profile things'),
'tag_deliver' => t('Can forward direct messages to all my channel connections (forum)'),
'chat' => t('Can chat with me'),
'republish' => t('Can source my public posts in derived channels'),
'republish' => t('Can source/mirror my public posts in derived channels'),
//'tag_deliver' => t('Can forward to my contacts via direct messages (forum)'),
'delegate' => t('Can administer my channel')
];
@@ -217,25 +217,23 @@ class Permissions {
$my_perms = [];
$permcat = null;
$automatic = 0;
$automatic = get_pconfig($channel_id, 'system', 'autoperms');
// If a default permcat exists, use that
$pc = ((feature_enabled($channel_id, 'permcats')) ? get_pconfig($channel_id, 'system', 'default_permcat') : 'default');
if (!in_array($pc, ['', 'default'])) {
$pcp = new Zlib\Permcat($channel_id);
$permcat = $pcp->fetch($pc);
if ($permcat && $permcat['perms']) {
foreach ($permcat['perms'] as $p) {
$my_perms[$p['name']] = $p['value'];
}
$pc = get_pconfig($channel_id, 'system', 'default_permcat', 'default');
$pcp = new Zlib\Permcat($channel_id);
$permcat = $pcp->fetch($pc);
if ($permcat && $permcat['perms']) {
foreach ($permcat['perms'] as $p) {
$my_perms[$p['name']] = $p['value'];
}
}
// look up the permission role to see if it specified auto-connect
// and if there was no permcat or a default permcat, set the perms
// from the role
/*
$role = get_pconfig($channel_id, 'system', 'permissions_role');
if ($role) {
$xx = PermissionRoles::role_perms($role);
@@ -247,11 +245,12 @@ class Permissions {
$my_perms = Permissions::FilledPerms($default_perms);
}
}
*/
// If we reached this point without having any permission information,
// it is likely a custom permissions role. First see if there are any
// automatic permissions.
/*
if (!$my_perms) {
$m = Permissions::FilledAutoperms($channel_id);
if ($m) {
@@ -259,11 +258,12 @@ class Permissions {
$my_perms = $m;
}
}
*/
// If we reached this point with no permissions, the channel is using
// custom perms but they are not automatic. They will be stored in abconfig with
// the channel's channel_hash (the 'self' connection).
/*
if (!$my_perms) {
$r = q("select channel_hash from channel where channel_id = %d",
intval($channel_id)
@@ -280,10 +280,10 @@ class Permissions {
}
}
}
return (['perms' => $my_perms, 'automatic' => $automatic]);
*/
return (['perms' => $my_perms, 'automatic' => $automatic, 'role' => $pc]);
}
/*
static public function serialise($p) {
$n = [];
if ($p) {
@@ -295,4 +295,5 @@ class Permissions {
}
return implode(',', $n);
}
*/
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Zotlabs\Daemon;
class Channel_purge {
static public function run($argc,$argv) {
cli_startup();
$channel_id = intval($argv[1]);
$channel = q("select * from channel where channel_id = %d and channel_removed = 1",
intval($channel_id)
);
if (! $channel) {
return;
}
do {
$r = q("select id from item where uid = %d and item_deleted = 0 limit 1000",
intval($channel_id)
);
if ($r) {
foreach ($r as $rv) {
drop_item($rv['id'], false);
}
}
} while ($r);
return;
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Zotlabs\Daemon;
use Zotlabs\Web\HTTPSig;
use Zotlabs\Lib\PConfig;
require_once('include/cli_startup.php');
require_once('include/attach.php');
require_once('include/import.php');
class Content_importer {
static public function run($argc,$argv) {
cli_startup();
$page = $argv[1];
$since = $argv[2];
$until = $argv[3];
$channel_address = $argv[4];
$hz_server = urldecode($argv[5]);
$m = parse_url($hz_server);
$channel = channelx_by_nick($channel_address);
if(! $channel) {
logger('channel not found');
return;
}
$headers = [
'X-API-Token' => random_string(),
'X-API-Request' => $hz_server . '/api/z/1.0/item/export_page?f=&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page ,
'Host' => $m['host'],
'(request-target)' => 'get /api/z/1.0/item/export_page?f=&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page ,
];
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512');
$x = z_fetch_url($hz_server . '/api/z/1.0/item/export_page?f=&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page,false,$redirects,[ 'headers' => $headers ]);
// logger('item fetch: ' . print_r($x,true));
if(! $x['success']) {
logger('no API response',LOGGER_DEBUG);
killme();
}
$j = json_decode($x['body'],true);
if(! is_array($j['item']) || ! count($j['item'])) {
PConfig::Set($channel['channel_id'], 'import', 'content_completed', 1);
return;
}
$saved_notification_flags = notifications_off($channel['channel_id']);
import_items($channel,$j['item'],false,((array_key_exists('relocate',$j)) ? $j['relocate'] : null));
notifications_on($channel['channel_id'], $saved_notification_flags);
PConfig::Set($channel['channel_id'], 'import', 'content_progress', [
'items_total' => $j['items_total'],
'items_page' => $j['items_page'],
'items_current_page' => count($j['item']),
'last_page' => $page,
'next_cmd' => ['Content_importer', sprintf('%d',$page + 1), $since, $until, $channel['channel_address'], urlencode($hz_server)]
]);
$page++;
Master::Summon([ 'Content_importer', sprintf('%d',$page), $since, $until, $channel['channel_address'], urlencode($hz_server) ]);
return;
}
}

View File

@@ -36,7 +36,6 @@ class Cron {
// run queue delivery process in the background
Master::Summon(array('Queue'));
Master::Summon(array('Poller'));
/**
@@ -206,10 +205,9 @@ class Cron {
// pull in some public posts
/* $disable_discover_tab = get_config('system', 'disable_discover_tab') || get_config('system', 'disable_discover_tab') === false;
$disable_discover_tab = get_config('system', 'disable_discover_tab') || get_config('system', 'disable_discover_tab') === false;
if (!$disable_discover_tab)
Master::Summon(array('Externals'));
*/
Master::Summon(['Externals']);
$restart = false;

View File

@@ -0,0 +1,25 @@
<?php
namespace Zotlabs\Daemon;
require_once('include/connections.php');
/*
* Daemon to remove 'item' resources in the background from a removed connection
*/
class Delxitems {
static public function run($argc, $argv) {
cli_startup();
if($argc != 3) {
return;
}
remove_abook_items($argv[1], $argv[2]);
return;
}
}

View File

@@ -49,8 +49,9 @@ class Directory {
);
// Now update all the connections
if ($pushall)
if ($pushall) {
Master::Summon(array('Notifier', 'refresh_all', $channel['channel_id']));
}
return;
}
@@ -93,8 +94,8 @@ class Directory {
}
// Now update all the connections
if ($pushall)
if ($pushall) {
Master::Summon(array('Notifier', 'refresh_all', $channel['channel_id']));
}
}
}

View File

@@ -3,6 +3,7 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\ASCollection;
@@ -31,25 +32,55 @@ class Externals {
$url = $arr['url'];
}
else {
$networks = ['zot6'];
if (plugin_is_installed('pubcrawl')) {
$networks[] = 'activitypub';
}
stringify_array_elms($networks);
$networks_str = implode(',', $networks);
$randfunc = db_getfunc('RAND');
// fixme this query does not deal with directory realms.
//$r = q("select site_url, site_pull from site where site_url != '%s'
//and site_flags != %d and site_type = %d
//and site_dead = 0 and site_project like '%s' and site_version > '5.3.1' order by $randfunc limit 1",
//dbesc(z_root()),
//intval(DIRECTORY_MODE_STANDALONE),
//intval(SITE_TYPE_ZOT),
//dbesc('hubzilla%')
//);
$r = q("select site_url, site_pull from site where site_url != '%s'
and site_flags != %d and site_type = %d
and site_dead = 0 and site_project like '%s' and site_version > '5.3.1' order by $randfunc limit 1",
$r = q("SELECT * FROM hubloc
LEFT JOIN abook ON abook_xchan = hubloc_hash
LEFT JOIN site ON site_url = hubloc_url WHERE
hubloc_network IN ( $networks_str ) AND
abook_xchan IS NULL AND
hubloc_url != '%s' AND
hubloc_updated > '%s' AND
hubloc_primary = 1 AND hubloc_deleted = 0 AND
site_dead = 0
ORDER BY $randfunc LIMIT 1",
dbesc(z_root()),
intval(DIRECTORY_MODE_STANDALONE),
intval(SITE_TYPE_ZOT),
dbesc('hubzilla%')
datetime_convert('UTC', 'UTC', 'now - 30 days')
);
if ($r)
$url = $r[0]['site_url'];
$contact = $r[0];
if ($contact) {
$url = $contact['hubloc_id_url'];
}
}
if (!$url) {
continue;
}
$blacklisted = false;
if (!check_siteallowed($url)) {
if (!check_siteallowed($contact['hubloc_url'])) {
logger('blacklisted site: ' . $url);
$blacklisted = true;
}
@@ -59,123 +90,65 @@ class Externals {
// make sure we can eventually break out if somebody blacklists all known sites
if ($blacklisted) {
if ($attempts > 20)
if ($attempts > 5)
break;
$attempts--;
continue;
}
$cl = Activity::get_actor_collections($contact['hubloc_hash']);
if(empty($cl)) {
$cl = get_xconfig($contact['hubloc_hash'], 'activitypub', 'collections');
}
if (is_array($cl) && array_key_exists('outbox', $cl)) {
$url = $cl['outbox'];
}
else {
$url = str_replace('/channel/', '/outbox/', $contact['hubloc_id_url']);
if ($url) {
$url .= '?top=1';
}
}
if ($url) {
logger('fetching outbox: ' . $url);
$max = intval(get_config('system', 'max_imported_posts', 30));
if (intval($max)) {
logger('externals: fetching outbox');
$obj = new ASCollection($url, $importer, 0, 10);
$messages = $obj->get();
$feed_url = $url . '/zotfeed';
$obj = new ASCollection($feed_url, $importer, 0, $max);
$messages = $obj->get();
if ($messages) {
foreach ($messages as $message) {
if (is_string($message)) {
$message = Activity::fetch($message, $importer);
}
if ($messages) {
foreach ($messages as $message) {
if (is_string($message)) {
$message = Activity::fetch($message, $importer);
}
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
Activity::store($importer, $importer['xchan_hash'], $AS, $item, true);
$total++;
if ($message['type'] !== 'Create') {
continue;
}
if ($contact['hubloc_network'] === 'zot6') {
// make sure we only fetch top level items
if (isset($message['object']['inReplyTo'])) {
continue;
}
Libzot::fetch_conversation($importer, $message['object']['id']);
$total++;
continue;
}
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
Activity::store($importer, $contact['abook_xchan'], $AS, $item);
$total++;
}
}
logger('externals: import_public_posts: ' . $total . ' messages imported', LOGGER_DEBUG);
}
logger('fetched messages count: ' . $total);
}
}
return;
/* $total = 0;
$attempts = 0;
logger('externals: startup', LOGGER_DEBUG);
// pull in some public posts
while ($total == 0 && $attempts < 3) {
$arr = ['url' => ''];
call_hooks('externals_url_select', $arr);
if ($arr['url']) {
$url = $arr['url'];
}
else {
$randfunc = db_getfunc('RAND');
// fixme this query does not deal with directory realms.
$r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d and site_dead = 0 order by $randfunc limit 1",
dbesc(z_root()),
intval(DIRECTORY_MODE_STANDALONE),
intval(SITE_TYPE_ZOT)
);
if ($r)
$url = $r[0]['site_url'];
}
$blacklisted = false;
if (!check_siteallowed($url)) {
logger('blacklisted site: ' . $url);
$blacklisted = true;
}
$attempts++;
// make sure we can eventually break out if somebody blacklists all known sites
if ($blacklisted) {
if ($attempts > 20)
break;
$attempts--;
continue;
}
if ($url) {
if ($r[0]['site_pull'] > NULL_DATE)
$mindate = urlencode(datetime_convert('', '', $r[0]['site_pull'] . ' - 1 day'));
else {
$days = get_config('externals', 'since_days');
if ($days === false)
$days = 15;
$mindate = urlencode(datetime_convert('', '', 'now - ' . intval($days) . ' days'));
}
$feedurl = $url . '/zotfeed?f=&mindate=' . $mindate;
logger('externals: pulling public content from ' . $feedurl, LOGGER_DEBUG);
$x = z_fetch_url($feedurl);
if (($x) && ($x['success'])) {
q("update site set site_pull = '%s' where site_url = '%s'",
dbesc(datetime_convert()),
dbesc($url)
);
$j = json_decode($x['body'], true);
if ($j['success'] && $j['messages']) {
$sys = get_sys_channel();
foreach ($j['messages'] as $message) {
// on these posts, clear any route info.
$message['route'] = '';
process_delivery(['hash' => 'undefined'], get_item_elements($message),
[['hash' => $sys['xchan_hash']]], false, true);
$total++;
}
logger('externals: import_public_posts: ' . $total . ' messages imported', LOGGER_DEBUG);
}
}
}
}*/
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace Zotlabs\Daemon;
use Zotlabs\Web\HTTPSig;
use Zotlabs\Lib\PConfig;
require_once('include/cli_startup.php');
require_once('include/attach.php');
require_once('include/import.php');
class File_importer {
static public function run($argc,$argv) {
cli_startup();
$page = $argv[1];
$channel_address = $argv[2];
$hz_server = urldecode($argv[3]);
$m = parse_url($hz_server);
$channel = channelx_by_nick($channel_address);
if(! $channel) {
logger('channel not found');
return;
}
$headers = [
'X-API-Token' => random_string(),
'X-API-Request' => $hz_server . '/api/z/1.0/file/export_page?f=records=1&page=' . $page,
'Host' => $m['host'],
'(request-target)' => 'get /api/z/1.0/file/export_page?f=records=1&page=' . $page,
];
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),true,'sha512');
// TODO: implement total count
$x = z_fetch_url($hz_server . '/api/z/1.0/file/export_page?f=records=1&page=' . $page, false, $redirects, [ 'headers' => $headers ]);
// logger('file fetch: ' . print_r($x,true));
if(! $x['success']) {
logger('no API response',LOGGER_DEBUG);
killme();
}
$j = json_decode($x['body'],true);
if(! is_array($j['results'][0]['attach']) || ! count($j['results'][0]['attach'])) {
PConfig::Set($channel['channel_id'], 'import', 'files_completed', 1);
return;
}
$r = sync_files($channel, $j['results']);
PConfig::Set($channel['channel_id'], 'import', 'files_progress', [
'files_total' => $j['total'],
'files_page' => 1, // export page atm returns just one file
'last_page' => $page,
'next_cmd' => ['File_importer',sprintf('%d',$page + 1), $channel['channel_address'], urlencode($hz_server)]
]);
$page++;
Master::Summon([ 'File_importer',sprintf('%d',$page), $channel['channel_address'], urlencode($hz_server) ]);
return;
}
}

View File

@@ -78,6 +78,10 @@ class Notifier {
static public $encoded_item = null;
static public $channel = null;
static public $private = false;
// $fragment can contain additional info to omit de-duplication in the queueworker.
// E.g. if an item is updated many times in a row from different sources (multiple vote updates) the
// update source mid or a timestamp or random string can be added.
static public $fragment = null;
static public function run($argc, $argv) {
@@ -88,14 +92,12 @@ class Notifier {
logger('notifier: invoked: ' . print_r($argv, true), LOGGER_DEBUG);
$cmd = $argv[1];
$item_id = $argv[2];
if (!$item_id) {
return;
}
self::$deliveries = [];
self::$recipients = [];
self::$env_recips = [];
@@ -104,6 +106,7 @@ class Notifier {
self::$encoded_item = null;
self::$channel = null;
self::$private = false;
self::$fragment = null;
$sys = get_sys_channel();
$normal_mode = true;
@@ -170,7 +173,7 @@ class Notifier {
elseif ($cmd === 'refresh_all') {
logger('notifier: refresh_all: ' . $item_id);
self::$channel = channelx_by_n($item_id);
self::$channel = channelx_by_n($item_id, true);
$r = q("select abook_xchan from abook where abook_channel = %d",
intval($item_id)
@@ -180,6 +183,11 @@ class Notifier {
self::$recipients[] = $rr['abook_xchan'];
}
}
// In case we deleted the channel, our abook entry has already vanished.
// In order to be able to update our clones we need to add ourself here.
self::$recipients[] = self::$channel['channel_hash'];
self::$private = false;
self::$packet_type = 'refresh';
}
@@ -190,14 +198,14 @@ class Notifier {
return;
}
self::$channel = channelx_by_n($item_id);
self::$channel = channelx_by_n($item_id, true);
self::$recipients = [$xchan];
self::$private = true;
self::$packet_type = 'purge';
}
elseif ($cmd === 'purge_all') {
logger('notifier: purge_all: ' . $item_id);
self::$channel = channelx_by_n($item_id);
self::$channel = channelx_by_n($item_id, true);
self::$recipients = [];
self::$private = false;
self::$packet_type = 'purge';
@@ -218,6 +226,8 @@ class Notifier {
// Fetch the target item
self::$fragment = $argv[3] ?? '';
$r = q("SELECT * FROM item WHERE id = %d AND parent != 0",
intval($item_id)
);
@@ -230,7 +240,7 @@ class Notifier {
$target_item = $r[0];
if (in_array($target_item['author']['xchan_network'], ['rss', 'anon'])) {
if (in_array($target_item['author']['xchan_network'], ['rss', 'anon', 'token'])) {
logger('notifier: target item author is not a fetchable actor', LOGGER_DEBUG);
return;
}
@@ -443,7 +453,6 @@ class Notifier {
}
}
$narr = [
'channel' => self::$channel,
'upstream' => $upstream,
@@ -485,7 +494,7 @@ class Notifier {
// Now we have collected recipients (except for external mentions, FIXME)
// Let's reduce this to a set of hubs; checking that the site is not dead.
$hubs = q("select hubloc.*, site.site_crypto, site.site_flags, site.site_dead from hubloc left join site on site_url = hubloc_url
$hubs = dbq("select hubloc.*, site.site_crypto, site.site_flags, site.site_dead from hubloc left join site on site_url = hubloc_url
where hubloc_hash in (" . protect_sprintf(implode(',', self::$recipients)) . ")
and hubloc_error = 0 and hubloc_deleted = 0"
);
@@ -526,16 +535,18 @@ class Notifier {
*/
$hublist = []; // this provides an easily printable list for the logs
$dhubs = []; // delivery hubs where we store our resulting unique array
$keys = []; // array of keys to check uniquness for zot hubs
$urls = []; // array of urls to check uniqueness of hubs from other networks
$hub_env = []; // per-hub envelope so we don't broadcast the entire envelope to all
$dead = []; // known dead hubs - report them as undeliverable
$hublist = []; // this provides an easily printable list for the logs
$dhubs = []; // delivery hubs where we store our resulting unique array
$keys = []; // array of keys to check uniquness for zot hubs
$urls = []; // array of urls to check uniqueness of hubs from other networks
$hub_env = []; // per-hub envelope so we don't broadcast the entire envelope to all
$dead_hosts = []; // known dead hubs - report them as undeliverable
foreach ($hubs as $hub) {
if (isset($hub['site_dead']) && intval($hub['site_dead'])) {
$dead[] = $hub;
if(!in_array($hub['hubloc_host'], $dead_hosts)) {
$dead_hosts[] = $hub['hubloc_host'];
}
continue;
}
@@ -545,7 +556,9 @@ class Notifier {
if (!array_key_exists($hub['hubloc_site_id'], $hub_env)) {
$hub_env[$hub['hubloc_site_id']] = [];
}
$hub_env[$hub['hubloc_site_id']][] = $er;
if (!in_array($er, $hub_env[$hub['hubloc_site_id']])) {
$hub_env[$hub['hubloc_site_id']][] = $er;
}
}
}
}
@@ -623,6 +636,18 @@ class Notifier {
// default: zot protocol
// Prevent zot6 delivery of group comment boosts, which are not required for conversational platforms.
// ActivityPub conversational platforms may wish to filter these if they don't want or require them.
// We will assume here that if $target_item exists and has a verb that it is an actual item structure
// so we won't need to check the existence of the other item fields prior to evaluation.
// This shouldn't produce false positives on comment boosts that were generated on other platforms
// because we won't be delivering them.
if (isset($target_item) && isset($target_item['verb']) && $target_item['verb'] === 'Announce' && $target_item['author_xchan'] === $target_item['owner_xchan'] && ! intval($target_item['item_thread_top'])) {
continue;
}
$hash = new_uuid();
$env = (($hub_env && $hub_env[$hub['hubloc_site_id']]) ? $hub_env[$hub['hubloc_site_id']] : '');
@@ -666,7 +691,7 @@ class Notifier {
// This wastes a process if there are no delivery hooks configured, so check this before launching the new process
$x = q("select * from hook where hook = 'notifier_normal'");
if ($x) {
Master::Summon(['Deliver_hooks', $target_item['id']]);
Master::Summon(['Deliver_hooks', $target_item['id'], self::$fragment]);
}
}
@@ -674,21 +699,19 @@ class Notifier {
do_delivery(self::$deliveries);
}
if ($dead) {
foreach ($dead as $deceased) {
if (is_array($target_item) && (!$target_item['item_deleted']) && (!get_config('system', 'disable_dreport'))) {
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue )
values ( '%s', '%s','%s','%s','%s','%s','%s','%s' ) ",
dbesc($target_item['mid']),
dbesc($deceased['hubloc_host']),
dbesc($deceased['hubloc_host']),
dbesc($deceased['hubloc_host']),
dbesc('undeliverable/unresponsive site'),
dbesc(datetime_convert()),
dbesc(self::$channel['channel_hash']),
dbesc(new_uuid())
);
}
if ($dead_hosts && is_array($target_item) && (!$target_item['item_deleted']) && (!get_config('system', 'disable_dreport'))) {
foreach ($dead_hosts as $deceased_host) {
$r = q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue )
values ( '%s', '%s','%s','%s','%s','%s','%s','%s' ) ",
dbesc($target_item['mid']),
dbesc($deceased_host),
dbesc($deceased_host),
dbesc($deceased_host),
dbesc('undeliverable/unresponsive site'),
dbesc(datetime_convert()),
dbesc(self::$channel['channel_hash']),
dbesc(new_uuid())
);
}
}

View File

@@ -48,15 +48,11 @@ class Onepoll {
$contact = $contacts[0];
$importer_uid = $contact['abook_channel'];
$r = q("SELECT * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1",
intval($importer_uid)
);
$importer = channelx_by_n($importer_uid);
if (!$r)
if (!$importer)
return;
$importer = $r[0];
logger("onepoll: poll: ({$contact['id']}) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}");
$last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE))
@@ -135,19 +131,34 @@ class Onepoll {
$url = $cl['outbox'];
}
else {
$url = str_replace('/poco/', '/zotfeed/', $contact['xchan_connurl']);
$url = str_replace('/poco/', '/outbox/', $contact['xchan_connurl']);
}
if ($url) {
logger('fetching outbox');
$url = $url . '?date_begin=' . urlencode($last_update);
$url = $url . '?date_begin=' . urlencode($last_update);
if($contact['xchan_network'] === 'zot6') {
$url = $url . '&top=1';
}
$obj = new ASCollection($url, $importer, 0, $max);
$messages = $obj->get();
if ($messages) {
foreach ($messages as $message) {
if (is_string($message)) {
$message = Activity::fetch($message, $importer);
}
if ($contact['xchan_network'] === 'zot6') {
// make sure we only fetch top level items
if ($message['type'] === 'Create' && !isset($message['object']['inReplyTo'])) {
Libzot::fetch_conversation($importer, $message['object']['id']);
}
continue;
}
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);

View File

@@ -1,38 +1,37 @@
<?php
<?php
namespace Zotlabs\Lib;
use Zotlabs\Lib\Libsync;
class AccessList {
static function add($uid,$name,$public = 0) {
$ret = false;
static function add($uid, $name, $public = 0) {
$ret = false;
$hash = '';
if ($uid && $name) {
$r = self::byname($uid,$name); // check for dups
$r = self::by_name($uid, $name); // check for dups
if ($r !== false) {
// This could be a problem.
// This could be a problem.
// Let's assume we've just created a list which we once deleted
// all the old members are gone, but the list remains so we don't break any security
// access lists. What we're doing here is reviving the dead list, but old content which
// was restricted to this list may now be seen by the new list members.
// was restricted to this list may now be seen by the new list members.
$z = q("SELECT * FROM pgrp WHERE id = %d LIMIT 1",
intval($r)
);
if(($z) && $z[0]['deleted']) {
if (($z) && $z[0]['deleted']) {
q('UPDATE pgrp SET deleted = 0 WHERE id = %d', intval($z[0]['id']));
notice( t('A deleted list with this name was revived. Existing item permissions <strong>may</strong> apply to this list and any future members. If this is not what you intended, please create another list with a different name.') . EOL);
notice(t('A deleted privacy group with this name was revived. Existing item permissions <strong>may</strong> apply to this privacy group and any future members. If this is not what you intended, please create another privacy group with a different name.') . EOL);
}
return true;
$hash = self::by_id($uid, $r);
return $hash;
}
$hash = new_uuid();
$r = q("INSERT INTO pgrp ( hash, uid, visible, gname )
$r = q("INSERT INTO pgrp ( hash, uid, visible, gname )
VALUES( '%s', %d, %d, '%s' ) ",
dbesc($hash),
intval($uid),
@@ -42,12 +41,12 @@ class AccessList {
$ret = $r;
}
Libsync::build_sync_packet($uid,null,true);
return $ret;
Libsync::build_sync_packet($uid, null, true);
return (($ret) ? $hash : $ret);
}
static function remove($uid,$name) {
static function remove($uid, $name) {
$ret = false;
if ($uid && $name) {
$r = q("SELECT id, hash FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1",
@@ -55,36 +54,36 @@ class AccessList {
dbesc($name)
);
if ($r) {
$group_id = $r[0]['id'];
$group_id = $r[0]['id'];
$group_hash = $r[0]['hash'];
}
else {
return false;
}
// remove group from default posting lists
$r = q("SELECT channel_default_group, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1",
intval($uid)
intval($uid)
);
if ($r) {
$user_info = array_shift($r);
$change = false;
$change = false;
if ($user_info['channel_default_group'] == $group_hash) {
$user_info['channel_default_group'] = '';
$change = true;
$change = true;
}
if (strpos($user_info['channel_allow_gid'], '<' . $group_hash . '>') !== false) {
$user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']);
$change = true;
$change = true;
}
if (strpos($user_info['channel_deny_gid'], '<' . $group_hash . '>') !== false) {
$user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']);
$change = true;
$change = true;
}
if ($change) {
q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s'
q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s'
WHERE channel_id = %d",
intval($user_info['channel_default_group']),
dbesc($user_info['channel_allow_gid']),
@@ -110,16 +109,16 @@ class AccessList {
}
Libsync::build_sync_packet($uid,null,true);
Libsync::build_sync_packet($uid, null, true);
return $ret;
}
// returns the integer id of an access group owned by $uid and named $name
// or false.
static function byname($uid,$name) {
if (! ($uid && $name)) {
static function by_name($uid, $name) {
if (!($uid && $name)) {
return false;
}
$r = q("SELECT id FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1",
@@ -132,11 +131,11 @@ class AccessList {
return false;
}
static function by_id($uid,$id) {
if (! ($uid && $id)) {
static function by_id($uid, $id) {
if (!($uid && $id)) {
return false;
}
$r = q("SELECT * FROM pgrp WHERE uid = %d AND id = %d and deleted = 0",
intval($uid),
intval($id)
@@ -147,10 +146,8 @@ class AccessList {
return false;
}
static function rec_byhash($uid,$hash) {
if (! ( $uid && $hash)) {
static function by_hash($uid, $hash) {
if (!($uid && $hash)) {
return false;
}
$r = q("SELECT * FROM pgrp WHERE uid = %d AND hash = '%s' LIMIT 1",
@@ -163,46 +160,46 @@ class AccessList {
return false;
}
static function member_remove($uid, $name, $member, $gid = 0) {
if (!$gid) {
$gid = self::by_name($uid, $name);
}
static function member_remove($uid,$name,$member) {
$gid = self::byname($uid,$name);
if (! $gid) {
return false;
}
if (! ($uid && $gid && $member)) {
if (!($uid && $gid && $member)) {
return false;
}
$r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' ",
intval($uid),
intval($gid),
dbesc($member)
);
Libsync::build_sync_packet($uid,null,true);
Libsync::build_sync_packet($uid, null, true);
return $r;
}
static function member_add($uid,$name,$member,$gid = 0) {
if (! $gid) {
$gid = self::byname($uid,$name);
static function member_add($uid, $name, $member, $gid = 0) {
if (!$gid) {
$gid = self::by_name($uid, $name);
}
if (! ($gid && $uid && $member)) {
if (!($gid && $uid && $member)) {
return false;
}
$r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1",
$r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1",
intval($uid),
intval($gid),
dbesc($member)
);
if ($r) {
return true; // You might question this, but
// we indicate success because the group member was in fact created
// -- It was just created at another time
return true;
// You might question this, but
// we indicate success because the group member was in fact created
// -- It was just created at another time
}
else {
else {
$r = q("INSERT INTO pgrp_member (uid, gid, xchan)
VALUES( %d, %d, '%s' ) ",
intval($uid),
@@ -210,15 +207,14 @@ class AccessList {
dbesc($member)
);
}
Libsync::build_sync_packet($uid,null,true);
Libsync::build_sync_packet($uid, null, true);
return $r;
}
static function members($uid, $gid) {
$ret = [];
if (intval($gid)) {
$r = q("SELECT * FROM pgrp_member
$r = q("SELECT * FROM pgrp_member
LEFT JOIN abook ON abook_xchan = pgrp_member.xchan left join xchan on xchan_hash = abook_xchan
WHERE gid = %d AND abook_channel = %d and pgrp_member.uid = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ",
intval($gid),
@@ -232,7 +228,7 @@ class AccessList {
return $ret;
}
static function members_xchan($uid,$gid) {
static function members_xchan($uid, $gid) {
$ret = [];
if (intval($gid)) {
$r = q("SELECT xchan FROM pgrp_member WHERE gid = %d AND uid = %d",
@@ -248,99 +244,124 @@ class AccessList {
return $ret;
}
static function members_profile_xchan($uid,$gid) {
static function profile_members_xchan($uid,$gid) {
$ret = [];
if (intval($gid)) {
if(intval($gid)) {
$r = q("SELECT abook_xchan as xchan from abook left join profile on abook_profile = profile_guid where profile.id = %d and profile.uid = %d",
intval($gid),
intval($uid)
);
if ($r) {
foreach($r as $rv) {
$ret[] = $rv['xchan'];
if($r) {
foreach($r as $rr) {
$ret[] = $rr['xchan'];
}
}
}
return $ret;
}
static function select($uid, $options) {
$selected = $options['selected'] ?? '';
$form_id = $options['form_id'] ?? 'accesslist_select';
$label = $options['label'] ?? t('Select a privacy group');
$before = $options['before'] ?? [];
$after = $options['after'] ?? [];
static function select($uid,$group = '') {
$grps = [];
$o = '';
$grps[] = [
'name' => '',
'id' => '0',
'selected' => false
];
if ($before) {
$grps[] = $before;
}
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
intval($uid)
);
$grps[] = [ 'name' => '', 'hash' => '0', 'selected' => '' ];
if ($r) {
foreach ($r as $rr) {
$grps[] = [ 'name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : '') ];
}
if($r) {
foreach($r as $rr) {
$grps[] = [
'name' => $rr['gname'],
'id' => $rr['hash'],
'selected' => ($selected == $rr['hash'])
];
}
}
return replace_macros(get_markup_template('group_selection.tpl'), [
'$label' => t('Add new connections to this access list'),
'$groups' => $grps
]);
if ($after) {
$grps[] = $after;
}
logger('select: ' . print_r($grps,true), LOGGER_DATA);
$o = replace_macros(get_markup_template('group_selection.tpl'), array(
'$label' => $label,
'$form_id' => $form_id,
'$groups' => $grps
));
return $o;
}
static function widget($every="connections",$each="lists",$edit = false, $group_id = 0, $cid = '',$mode = 1) {
$o = '';
/* deprecated
static function widget($every = "connections", $each = "lists", $edit = false, $group_id = 0, $cid = '', $mode = 1) {
$groups = [];
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
intval($_SESSION['uid'])
);
$member_of = [];
if ($cid) {
$member_of = self::containing(local_channel(),$cid);
}
$member_of = self::containing(local_channel(), $cid);
}
if ($r) {
foreach ($r as $rr) {
$selected = (($group_id == $rr['id']) ? ' group-selected' : '');
if ($edit) {
$groupedit = [ 'href' => "lists/".$rr['id'], 'title' => t('edit') ];
}
$groupedit = ['href' => "lists/" . $rr['id'], 'title' => t('edit')];
}
else {
$groupedit = null;
}
$groups[] = [
'id' => $rr['id'],
'enc_cid' => base64url_encode($cid),
'cid' => $cid,
'text' => $rr['gname'],
'selected' => $selected,
'href' => (($mode == 0) ? $each.'?f=&gid='.$rr['id'] : $each."/".$rr['id']) . ((x($_GET,'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : ''),
'edit' => $groupedit,
'ismember' => in_array($rr['id'],$member_of),
'id' => $rr['id'],
'enc_cid' => base64url_encode($cid),
'cid' => $cid,
'text' => $rr['gname'],
'selected' => $selected,
'href' => (($mode == 0) ? $each . '?f=&gid=' . $rr['id'] : $each . "/" . $rr['id']) . ((x($_GET, 'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET, 'order')) ? '&order=' . $_GET['order'] : ''),
'edit' => $groupedit,
'ismember' => in_array($rr['id'], $member_of),
];
}
}
return replace_macros(get_markup_template('group_side.tpl'), [
'$title' => t('Lists'),
'$edittext' => t('Edit list'),
'$createtext' => t('Create new list'),
'$ungrouped' => (($every === 'contacts') ? t('Channels not in any access list') : ''),
'$groups' => $groups,
'$add' => t('add'),
'$title' => t('Privacy Groups'),
'$edittext' => t('Edit group'),
'$createtext' => t('Create new group'),
'$ungrouped' => (($every === 'contacts') ? t('Channels not in any privacy group') : ''),
'$groups' => $groups,
'$add' => t('Add'),
]);
}
*/
static function expand($g) {
if (! (is_array($g) && count($g))) {
if (!(is_array($g) && count($g))) {
return [];
}
@@ -350,8 +371,8 @@ class AccessList {
// private profile linked virtual groups
foreach ($g as $gv) {
if (substr($gv,0,3) === 'vp.') {
$profile_hash = substr($gv,3);
if (substr($gv, 0, 3) === 'vp.') {
$profile_hash = substr($gv, 3);
if ($profile_hash) {
$r = q("select abook_xchan from abook where abook_profile = '%s'",
dbesc($profile_hash)
@@ -366,10 +387,10 @@ class AccessList {
else {
$x[] = $gv;
}
}
}
if ($x) {
stringify_array_elms($x,true);
stringify_array_elms($x, true);
$groups = implode(',', $x);
if ($groups) {
$r = q("SELECT xchan FROM pgrp_member WHERE gid IN ( select id from pgrp where hash in ( $groups ))");
@@ -383,9 +404,8 @@ class AccessList {
return $ret;
}
static function member_of($c) {
$r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id
$r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id
WHERE pgrp_member.xchan = '%s' AND pgrp.deleted = 0 ORDER BY pgrp.gname ASC ",
dbesc($c)
);
@@ -393,7 +413,7 @@ class AccessList {
return $r;
}
static function containing($uid,$c) {
static function containing($uid, $c) {
$r = q("SELECT gid FROM pgrp_member WHERE uid = %d AND pgrp_member.xchan = '%s' ",
intval($uid),
@@ -405,7 +425,8 @@ class AccessList {
foreach ($r as $rv)
$ret[] = $rv['gid'];
}
return $ret;
}
}
}

View File

@@ -8,8 +8,6 @@ use Zotlabs\Access\PermissionRoles;
use Zotlabs\Access\Permissions;
use Zotlabs\Daemon\Master;
use Zotlabs\Web\HTTPSig;
use Zotlabs\Lib\XConfig;
use Zotlabs\Lib\Libzot;
require_once('include/event.php');
require_once('include/html2plain.php');
@@ -104,7 +102,7 @@ class Activity {
if ($x['success']) {
$m = parse_url($url);
if ($m) {
$y = [ 'scheme' => $m['scheme'], 'host' => $m['host'] ];
$y = ['scheme' => $m['scheme'], 'host' => $m['host']];
if (array_key_exists('port', $m))
$y['port'] = $m['port'];
$site_url = unparse_url($y);
@@ -288,21 +286,21 @@ class Activity {
'type' => $type . 'Page',
];
$numpages = $total / App::$pager['itemspage'];
$lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages);
$numpages = $total / App::$pager['itemspage'];
$lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages);
$url_parts = parse_url($id);
$ret['partOf'] = z_root() . '/' . $url_parts['path'];
$extra_query_args = '';
$query_args = null;
if(isset($url_parts['query'])) {
$query_args = null;
if (isset($url_parts['query'])) {
parse_str($url_parts['query'], $query_args);
}
if(is_array($query_args)) {
if (is_array($query_args)) {
unset($query_args['page']);
foreach($query_args as $k => $v)
foreach ($query_args as $k => $v)
$extra_query_args .= '&' . urlencode($k) . '=' . urlencode($v);
}
@@ -376,12 +374,34 @@ class Activity {
return $ret;
}
static function encode_simple_collection($items, $id, $type, $total = 0, $extra = null) {
$ret = [
'id' => z_root() . '/' . $id,
'type' => $type,
'totalItems' => $total,
];
if ($extra) {
$ret = array_merge($ret, $extra);
}
if ($items) {
if ($type === 'OrderedCollection') {
$ret['orderedItems'] = $items;
}
else {
$ret['items'] = $items;
}
}
return $ret;
}
static function encode_item($i) {
$ret = [];
if ($i['verb'] === ACTIVITY_FRIEND) {
// Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note
$objtype = 'Note';
@@ -866,10 +886,6 @@ class Activity {
else
return [];
if (strpos($i['body'], '[/share]') !== false) {
$i['obj'] = null;
}
if ($i['obj']) {
if (!is_array($i['obj'])) {
$i['obj'] = json_decode($i['obj'], true);
@@ -879,8 +895,10 @@ class Activity {
}
$obj = self::encode_object($i['obj']);
if ($obj)
if ($obj) {
$ret['object'] = $obj;
}
else
return [];
}
@@ -1022,7 +1040,7 @@ class Activity {
$tmp = expand_acl($i['allow_cid']);
$list = stringify_array($tmp, true);
if ($list) {
$details = q("select hubloc_id_url from hubloc where hubloc_hash in (" . $list . ") and hubloc_id_url != ''");
$details = q("select hubloc_id_url from hubloc where hubloc_hash in (" . $list . ") and hubloc_id_url != '' and hubloc_deleted = 0");
if ($details) {
foreach ($details as $d) {
$ret[] = $d['hubloc_id_url'];
@@ -1069,10 +1087,11 @@ class Activity {
$ret['type'] = 'Person';
if ($c) {
$role = get_pconfig($c['channel_id'], 'system', 'permissions_role');
if (strpos($role, 'forum') !== false) {
if (get_pconfig($c['channel_id'], 'system', 'group_actor')) {
$ret['type'] = 'Group';
}
$ret['manuallyApprovesFollowers'] = ((get_pconfig($c['channel_id'], 'system', 'autoperms')) ? false : true);
}
if ($c) {
@@ -1095,7 +1114,33 @@ class Activity {
'height' => 300,
'width' => 300,
];
$ret['url'] = $p['xchan_url'];
/* This could be used to distinguish actors by protocol instead of tags,
* array urls are not supported by some AP projects (pixelfed) though.
*
$ret['url'] = [
[
'type' => 'Link',
'rel' => 'alternate',
'mediaType' => 'application/x-zot+json',
'href' => $p['xchan_url']
],
[
'type' => 'Link',
'rel' => 'alternate',
'mediaType' => 'application/activity+json',
'href' => $p['xchan_url']
],
[
'type' => 'Link',
'rel' => 'alternate', // 'me'?
'mediaType' => 'text/html',
'href' => $p['xchan_url']
]
];
*/
$ret['url'] = $p['xchan_url'];
$ret['publicKey'] = [
'id' => $p['xchan_url'],
@@ -1103,15 +1148,24 @@ class Activity {
'publicKeyPem' => $p['xchan_pubkey']
];
if ($c) {
$ret['tag'][] = [
'type' => 'PropertyValue',
'name' => 'Protocol',
'value' => 'zot6'
];
$ret['outbox'] = z_root() . '/outbox/' . $c['channel_address'];
}
$arr = [
'xchan' => $p,
'xchan' => $p,
'encoded' => $ret
];
call_hooks('encode_person', $arr);
$ret = $arr['encoded'];
return $ret;
}
@@ -1119,8 +1173,8 @@ class Activity {
$ret = [];
if ($item[$elm]) {
if (! is_array($item[$elm])) {
$item[$elm] = json_decode($item[$elm],true);
if (!is_array($item[$elm])) {
$item[$elm] = json_decode($item[$elm], true);
}
if ($item[$elm]['type'] === ACTIVITY_OBJ_PHOTO) {
$item[$elm]['id'] = $item['mid'];
@@ -1150,22 +1204,22 @@ class Activity {
}
$acts = [
'http://activitystrea.ms/schema/1.0/post' => 'Create',
'http://activitystrea.ms/schema/1.0/share' => 'Announce',
'http://activitystrea.ms/schema/1.0/update' => 'Update',
'http://activitystrea.ms/schema/1.0/like' => 'Like',
'http://activitystrea.ms/schema/1.0/favorite' => 'Like',
'http://purl.org/zot/activity/dislike' => 'Dislike',
'http://activitystrea.ms/schema/1.0/tag' => 'Add',
'http://activitystrea.ms/schema/1.0/follow' => 'Follow',
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://activitystrea.ms/schema/1.0/post' => 'Create',
'http://activitystrea.ms/schema/1.0/share' => 'Announce',
'http://activitystrea.ms/schema/1.0/update' => 'Update',
'http://activitystrea.ms/schema/1.0/like' => 'Like',
'http://activitystrea.ms/schema/1.0/favorite' => 'Like',
'http://purl.org/zot/activity/dislike' => 'Dislike',
'http://activitystrea.ms/schema/1.0/tag' => 'Add',
'http://activitystrea.ms/schema/1.0/follow' => 'Follow',
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://activitystrea.ms/schema/1.0/stop-following' => 'Unfollow',
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept',
'Invite' => 'Invite',
'Delete' => 'Delete',
'Undo' => 'Undo'
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept',
'Invite' => 'Invite',
'Delete' => 'Delete',
'Undo' => 'Undo'
];
call_hooks('activity_mapper', $acts);
@@ -1198,22 +1252,22 @@ class Activity {
static function activity_decode_mapper($verb) {
$acts = [
'http://activitystrea.ms/schema/1.0/post' => 'Create',
'http://activitystrea.ms/schema/1.0/share' => 'Announce',
'http://activitystrea.ms/schema/1.0/update' => 'Update',
'http://activitystrea.ms/schema/1.0/like' => 'Like',
'http://activitystrea.ms/schema/1.0/favorite' => 'Like',
'http://purl.org/zot/activity/dislike' => 'Dislike',
'http://activitystrea.ms/schema/1.0/tag' => 'Add',
'http://activitystrea.ms/schema/1.0/follow' => 'Follow',
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://activitystrea.ms/schema/1.0/post' => 'Create',
'http://activitystrea.ms/schema/1.0/share' => 'Announce',
'http://activitystrea.ms/schema/1.0/update' => 'Update',
'http://activitystrea.ms/schema/1.0/like' => 'Like',
'http://activitystrea.ms/schema/1.0/favorite' => 'Like',
'http://purl.org/zot/activity/dislike' => 'Dislike',
'http://activitystrea.ms/schema/1.0/tag' => 'Add',
'http://activitystrea.ms/schema/1.0/follow' => 'Follow',
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://activitystrea.ms/schema/1.0/stop-following' => 'Unfollow',
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept',
'Invite' => 'Invite',
'Delete' => 'Delete',
'Undo' => 'Undo'
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept',
'Invite' => 'Invite',
'Delete' => 'Delete',
'Undo' => 'Undo'
];
call_hooks('activity_decode_mapper', $acts);
@@ -1325,7 +1379,7 @@ class Activity {
*
*/
if (in_array($act->type, [ 'Follow', 'Invite', 'Join'])) {
if (in_array($act->type, ['Follow', 'Invite', 'Join'])) {
$their_follow_id = $act->id;
}
@@ -1348,8 +1402,8 @@ class Activity {
}
}
$x = \Zotlabs\Access\PermissionRoles::role_perms('social');
$their_perms = \Zotlabs\Access\Permissions::FilledPerms($x['perms_connect']);
$x = PermissionRoles::role_perms('personal');
$their_perms = Permissions::FilledPerms($x['perms_connect']);
if ($contact && $contact['abook_id']) {
@@ -1423,7 +1477,7 @@ class Activity {
}
$ret = $r[0];
$p = \Zotlabs\Access\Permissions::connect_perms($channel['channel_id']);
$p = Permissions::connect_perms($channel['channel_id']);
$my_perms = $p['perms'];
$automatic = $p['automatic'];
@@ -1444,13 +1498,13 @@ class Activity {
]
);
if($my_perms)
foreach($my_perms as $k => $v)
set_abconfig($channel['channel_id'],$ret['xchan_hash'],'my_perms',$k,$v);
if ($my_perms)
foreach ($my_perms as $k => $v)
set_abconfig($channel['channel_id'], $ret['xchan_hash'], 'my_perms', $k, $v);
if($their_perms)
foreach($their_perms as $k => $v)
set_abconfig($channel['channel_id'],$ret['xchan_hash'],'their_perms',$k,$v);
if ($their_perms)
foreach ($their_perms as $k => $v)
set_abconfig($channel['channel_id'], $ret['xchan_hash'], 'their_perms', $k, $v);
if ($r) {
logger("New ActivityPub follower for {$channel['channel_name']}");
@@ -1465,7 +1519,7 @@ class Activity {
'type' => NOTIFY_INTRO,
'from_xchan' => $ret['xchan_hash'],
'to_xchan' => $channel['channel_hash'],
'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'],
'link' => z_root() . '/connections#' . $new_connection[0]['abook_id'],
]
);
@@ -1499,9 +1553,9 @@ class Activity {
/* If there is a default group for this channel and permissions are automatic, add this member to it */
if ($channel['channel_default_group'] && $automatic) {
$g = Group::rec_byhash($channel['channel_id'], $channel['channel_default_group']);
$g = AccessList::by_hash($channel['channel_id'], $channel['channel_default_group']);
if ($g)
Group::member_add($channel['channel_id'], '', $ret['xchan_hash'], $g['id']);
AccessList::member_add($channel['channel_id'], '', $ret['xchan_hash'], $g['id']);
}
@@ -1540,16 +1594,16 @@ class Activity {
return;
}
/* not implemented
if (array_key_exists('movedTo',$person_obj) && $person_obj['movedTo'] && ! is_array($person_obj['movedTo'])) {
$tgt = self::fetch($person_obj['movedTo']);
if (is_array($tgt)) {
self::actor_store($person_obj['movedTo'],$tgt);
ActivityPub::move($person_obj['id'],$tgt);
}
return;
}
*/
/* not implemented
if (array_key_exists('movedTo',$person_obj) && $person_obj['movedTo'] && ! is_array($person_obj['movedTo'])) {
$tgt = self::fetch($person_obj['movedTo']);
if (is_array($tgt)) {
self::actor_store($person_obj['movedTo'],$tgt);
ActivityPub::move($person_obj['id'],$tgt);
}
return;
}
*/
$ap_hubloc = null;
$hublocs = self::get_actor_hublocs($url);
@@ -1567,7 +1621,7 @@ class Activity {
if ($ap_hubloc) {
// we already have a stored record. Determine if it needs updating.
if ($ap_hubloc['hubloc_updated'] < datetime_convert('UTC','UTC',' now - 3 days') || $force) {
if ($ap_hubloc['hubloc_updated'] < datetime_convert('UTC', 'UTC', ' now - 3 days') || $force) {
$person_obj = self::fetch($url);
}
else {
@@ -1579,7 +1633,7 @@ class Activity {
$url = $person_obj['id'];
}
if (! $url) {
if (!$url) {
return;
}
@@ -1602,6 +1656,19 @@ class Activity {
$name = t('Unknown');
}
$webfinger_addr = '';
$m = parse_url($url);
if ($m) {
$hostname = $m['host'];
$baseurl = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : '');
$site_url = $m['scheme'] . '://' . $m['host'];
}
if (!empty($person_obj['preferredUsername']) && isset($parsed_url['host'])) {
$webfinger_addr = escape_tags($person_obj['preferredUsername']) . '@' . $hostname;
}
$icon = z_root() . '/' . get_default_profile_photo(300);
if ($person_obj['icon']) {
if (is_array($person_obj['icon'])) {
@@ -1636,7 +1703,7 @@ class Activity {
if ($links) {
foreach ($links as $link) {
if (array_key_exists('mediaType', $link) && $link['mediaType'] === 'text/html') {
if (is_array($link) && array_key_exists('mediaType', $link) && $link['mediaType'] === 'text/html') {
$profile = $link['href'];
}
}
@@ -1661,22 +1728,15 @@ class Activity {
}
}
$m = parse_url($url);
if($m) {
$hostname = $m['host'];
$baseurl = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : '');
$site_url = $m['scheme'] . '://' . $m['host'];
}
$r = q("select * from xchan join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s'",
dbesc($url)
);
if($r) {
if ($r) {
// Record exists. Cache existing records for one week at most
// then refetch to catch updated profile photos, names, etc.
$d = datetime_convert('UTC', 'UTC', 'now - 3 days');
if($r[0]['hubloc_updated'] > $d && !$force) {
if ($r[0]['hubloc_updated'] > $d && !$force) {
return;
}
@@ -1686,22 +1746,24 @@ class Activity {
);
// update existing xchan record
q("update xchan set xchan_name = '%s', xchan_guid = '%s', xchan_pubkey = '%s', xchan_network = 'activitypub', xchan_name_date = '%s' where xchan_hash = '%s'",
q("update xchan set xchan_name = '%s', xchan_guid = '%s', xchan_pubkey = '%s', xchan_addr = '%s', xchan_network = 'activitypub', xchan_name_date = '%s' where xchan_hash = '%s'",
dbesc(escape_tags($name)),
dbesc(escape_tags($url)),
dbesc($url),
dbesc(escape_tags($pubkey)),
dbesc(escape_tags($webfinger_addr)),
dbescdate(datetime_convert()),
dbesc($url)
);
// update existing hubloc record
q("update hubloc set hubloc_guid = '%s', hubloc_network = 'activitypub', hubloc_url = '%s', hubloc_host = '%s', hubloc_callback = '%s', hubloc_updated = '%s', hubloc_id_url = '%s' where hubloc_hash = '%s'",
dbesc(escape_tags($url)),
dbesc(escape_tags($baseurl)),
dbesc(escape_tags($hostname)),
dbesc(escape_tags($inbox)),
q("update hubloc set hubloc_guid = '%s', hubloc_addr = '%s', hubloc_network = 'activitypub', hubloc_url = '%s', hubloc_host = '%s', hubloc_callback = '%s', hubloc_updated = '%s', hubloc_id_url = '%s' where hubloc_hash = '%s'",
dbesc($url),
dbesc(escape_tags($webfinger_addr)),
dbesc($baseurl),
dbesc($hostname),
dbesc($inbox),
dbescdate(datetime_convert()),
dbesc(escape_tags($profile)),
dbesc($profile),
dbesc($url)
);
}
@@ -1710,33 +1772,46 @@ class Activity {
xchan_store_lowlevel(
[
'xchan_hash' => escape_tags($url),
'xchan_guid' => escape_tags($url),
'xchan_pubkey' => escape_tags($pubkey),
'xchan_addr' => '',
'xchan_url' => escape_tags($profile),
'xchan_name' => escape_tags($name),
'xchan_name_date' => datetime_convert(),
'xchan_network' => 'activitypub'
'xchan_hash' => $url,
'xchan_guid' => $url,
'xchan_pubkey' => escape_tags($pubkey),
'xchan_addr' => $webfinger_addr,
'xchan_url' => escape_tags($profile),
'xchan_name' => escape_tags($name),
'xchan_name_date' => datetime_convert(),
'xchan_network' => 'activitypub'
]
);
hubloc_store_lowlevel(
[
'hubloc_guid' => escape_tags($url),
'hubloc_hash' => escape_tags($url),
'hubloc_addr' => '',
'hubloc_guid' => $url,
'hubloc_hash' => $url,
'hubloc_addr' => $webfinger_addr,
'hubloc_network' => 'activitypub',
'hubloc_url' => escape_tags($baseurl),
'hubloc_host' => escape_tags($hostname),
'hubloc_callback' => escape_tags($inbox),
'hubloc_url' => $baseurl,
'hubloc_host' => $hostname,
'hubloc_callback' => $inbox,
'hubloc_updated' => datetime_convert(),
'hubloc_primary' => 1,
'hubloc_id_url' => escape_tags($profile)
'hubloc_id_url' => $profile
]
);
}
// We store all ActivityPub actors we can resolve. Some of them may be able to communicate over Zot6. Find them.
// Adding zot discovery urls to the actor record will cause federation to fail with the 20-30 projects which don't accept arrays in the url field.
$actor_protocols = self::get_actor_protocols($person_obj);
if (in_array('zot6', $actor_protocols)) {
$zx = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6'",
dbesc($url)
);
if (!$zx && $webfinger_addr) {
Master::Summon(['Gprobe', bin2hex($webfinger_addr)]);
}
}
$photos = import_xchan_photo($icon, $url);
q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'",
dbescdate(datetime_convert('UTC', 'UTC', $photos[5])),
@@ -1784,9 +1859,9 @@ class Activity {
static function create_note($channel, $observer_hash, $act) {
$s = [];
$s = [];
$is_sys_channel = is_sys_channel($channel['channel_id']);
$parent = ((array_key_exists('inReplyTo', $act->obj)) ? urldecode($act->obj['inReplyTo']) : '');
$parent = ((array_key_exists('inReplyTo', $act->obj)) ? urldecode($act->obj['inReplyTo']) : '');
if ($parent) {
@@ -2040,6 +2115,7 @@ class Activity {
}
static function update_poll($item, $post) {
$multi = false;
$mid = $post['mid'];
$content = $post['title'];
@@ -2124,7 +2200,8 @@ class Activity {
dbesc(datetime_convert()),
intval($item['id'])
);
Master::Summon(['Notifier', 'wall-new', $item['id']]);
Master::Summon(['Notifier', 'wall-new', $item['id'], $post['mid'] /* trick queueworker de-duplication */ ]);
return true;
}
@@ -2138,7 +2215,7 @@ class Activity {
// Unfollow is not defined by ActivityStreams, which prefers Undo->Follow.
// This may have to be revisited if AP projects start using Follow for objects other than actors.
if (in_array($act->type, [ 'Follow', 'Unfollow' ])) {
if (in_array($act->type, ['Follow', 'Unfollow'])) {
return false;
}
@@ -2161,10 +2238,12 @@ class Activity {
$s['parent_mid'] = $act->parent_id;
if (array_key_exists('published', $act->data)) {
$s['created'] = datetime_convert('UTC', 'UTC', $act->data['published']);
$s['created'] = datetime_convert('UTC', 'UTC', $act->data['published']);
$s['commented'] = $s['created'];
}
elseif (array_key_exists('published', $act->obj)) {
$s['created'] = datetime_convert('UTC', 'UTC', $act->obj['published']);
$s['created'] = datetime_convert('UTC', 'UTC', $act->obj['published']);
$s['commented'] = $s['created'];
}
if (array_key_exists('updated', $act->data)) {
$s['edited'] = datetime_convert('UTC', 'UTC', $act->data['updated']);
@@ -2235,10 +2314,10 @@ class Activity {
}
}
if (! array_key_exists('created', $s))
if (!array_key_exists('created', $s))
$s['created'] = datetime_convert();
if (! array_key_exists('edited', $s))
if (!array_key_exists('edited', $s))
$s['edited'] = $s['created'];
$s['title'] = (($response_activity) ? EMPTY_STR : self::bb_content($content, 'name'));
@@ -2447,7 +2526,7 @@ class Activity {
}
if ($act->obj['type'] === 'Image' && strpos($s['body'],'zrl=') === false) {
if ($act->obj['type'] === 'Image' && strpos($s['body'], 'zrl=') === false) {
$ptr = null;
@@ -2559,13 +2638,35 @@ class Activity {
}
}
set_iconfig($s, 'activitypub', 'recips', $act->raw_recips);
$zot_rawmsg = '';
$raw_arr = [];
$parent = (($s['parent_mid'] && $s['parent_mid'] === $s['mid']) ? true : false);
if ($parent) {
$raw_arr = json_decode($act->raw, true);
// This is a zot6 packet and the raw activitypub message json
// is possibly available in the attachement.
if (array_key_exists('signed', $raw_arr) && is_array($act->obj) && is_array($act->obj['attachment'])) {
foreach($act->obj['attachment'] as $a) {
if (
isset($a['type']) && $a['type'] === 'PropertyValue' &&
isset($a['name']) && $a['name'] === 'zot.activitypub.rawmsg' &&
isset($a['value'])
) {
$zot_rawmsg = $a['value'];
break;
}
}
}
if ($zot_rawmsg) {
set_iconfig($s, 'activitypub', 'rawmsg', $zot_rawmsg, 1);
}
else {
set_iconfig($s, 'activitypub', 'rawmsg', $act->raw, 1);
}
set_iconfig($s, 'activitypub', 'recips', $act->raw_recips);
$hookinfo = [
'act' => $act,
's' => $s
@@ -2614,6 +2715,17 @@ class Activity {
// set the owner to the owner of the parent
$item['owner_xchan'] = $p[0]['owner_xchan'];
// quietly reject group comment boosts by group owner
// (usually only sent via ActivityPub so groups will work on microblog platforms)
// This catches those activities if they slipped in via a conversation fetch
if ($p[0]['parent_mid'] !== $item['parent_mid']) {
if ($item['verb'] === 'Announce' && $item['author_xchan'] === $item['owner_xchan']) {
logger('group boost activity by group owner rejected');
return;
}
}
// check permissions against the author, not the sender
$allowed = perm_is_allowed($channel['channel_id'], $item['author_xchan'], 'post_comments');
if ((!$allowed)/* && $permit_mentions*/) {
@@ -3115,7 +3227,7 @@ class Activity {
static function announce_note($channel, $observer_hash, $act) {
$s = [];
$s = [];
$is_sys_channel = is_sys_channel($channel['channel_id']);
if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') && !$is_sys_channel) {
@@ -3375,7 +3487,7 @@ class Activity {
$ret = false;
foreach ($attach as $a) {
if (array_key_exists('type',$a) && stripos($a['type'], 'image') !== false) {
if (array_key_exists('type', $a) && stripos($a['type'], 'image') !== false) {
if (self::media_not_in_body($a['href'], $body)) {
$ret .= "\n\n" . '[img]' . $a['href'] . '[/img]';
}
@@ -3533,7 +3645,7 @@ class Activity {
static function find_best_identity($xchan) {
if (filter_var($xchan, FILTER_VALIDATE_URL)) {
$r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' and hubloc_deleted = 0",
$r = q("SELECT hubloc_hash, hubloc_network FROM hubloc WHERE hubloc_id_url = '%s' AND hubloc_network IN ('zot6', 'activitypub') AND hubloc_deleted = 0",
dbesc($xchan)
);
if ($r) {
@@ -3548,13 +3660,25 @@ class Activity {
}
static function get_cached_actor($id) {
return (XConfig::Get($id,'system','actor_record'));
$actor = XConfig::Get($id, 'system', 'actor_record');
if ($actor) {
return $actor;
}
// try other get_cached_actor providers (e.g. diaspora)
$hookdata = [
'id' => $id,
'actor' => false
];
call_hooks('get_cached_actor_provider', $hookdata);
return $hookdata['actor'];
}
static function get_actor_hublocs($url, $options = 'all') {
$hublocs = false;
switch ($options) {
case 'activitypub':
$hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' and hubloc_deleted = 0 ",
@@ -3579,21 +3703,41 @@ class Activity {
}
static function get_actor_collections($url) {
$ret = [];
$actor_record = XConfig::Get($url,'system','actor_record');
if (! $actor_record) {
$ret = [];
$actor_record = XConfig::Get($url, 'system', 'actor_record');
if (!$actor_record) {
return $ret;
}
foreach ( [ 'inbox','outbox','followers','following' ] as $collection) {
foreach (['inbox', 'outbox', 'followers', 'following'] as $collection) {
if (isset($actor_record[$collection]) && $actor_record[$collection]) {
$ret[$collection] = $actor_record[$collection];
}
}
if (array_path_exists('endpoints/sharedInbox',$actor_record) && $actor_record['endpoints']['sharedInbox']) {
if (array_path_exists('endpoints/sharedInbox', $actor_record) && $actor_record['endpoints']['sharedInbox']) {
$ret['sharedInbox'] = $actor_record['endpoints']['sharedInbox'];
}
return $ret;
}
static function get_actor_protocols($actor) {
$ret = [];
if (!array_key_exists('tag', $actor) || empty($actor['tag']) || !is_array($actor['tag'])) {
return $ret;
}
foreach ($tag as $t) {
if ((isset($t['type']) && $t['type'] === 'PropertyValue') &&
(isset($t['name']) && $t['name'] === 'Protocol') &&
(isset($t['value']) && in_array($t['value'], ['zot6', 'activitypub', 'diaspora']))
) {
$ret[] = $t['value'];
}
}
return $ret;
}
}

View File

@@ -40,6 +40,7 @@ class ActivityStreams {
if (is_array($string)) {
$this->data = $string;
$this->raw = json_encode($string, JSON_UNESCAPED_SLASHES);
}
else {
$this->data = json_decode($string, true);
@@ -285,7 +286,7 @@ class ActivityStreams {
if (!$s) {
return false;
}
return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact']));
return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact']));
}
/**

View File

@@ -3,7 +3,6 @@
namespace Zotlabs\Lib;
use App;
use Zotlabs\Lib\Libsync;
require_once('include/plugin.php');
require_once('include/channel.php');
@@ -22,9 +21,10 @@ class Apps {
* @brief
*
* @param boolean $translate (optional) default true
* @param boolean $sync (optional) default false used if called from sync_sysapps()
* @return array
*/
static public function get_system_apps($translate = true) {
static public function get_system_apps($translate = true, $sync = false) {
$ret = [];
if(is_dir('apps'))
@@ -34,7 +34,7 @@ class Apps {
if($files) {
foreach($files as $f) {
$x = self::parse_app_description($f,$translate);
$x = self::parse_app_description($f, $translate, $sync);
if($x) {
$ret[] = $x;
}
@@ -46,7 +46,7 @@ class Apps {
$path = explode('/',$f);
$plugin = trim($path[1]);
if(plugin_is_installed($plugin)) {
$x = self::parse_app_description($f,$translate);
$x = self::parse_app_description($f, $translate, $sync);
if($x) {
$x['plugin'] = $plugin;
$ret[] = $x;
@@ -67,17 +67,15 @@ class Apps {
static public function get_base_apps() {
$x = get_config('system','base_apps',[
'Connections',
'Contact Roles',
'Network',
'Settings',
'Files',
'Channel Home',
'View Profile',
'Channel',
'Photos',
'Calendar',
'Directory',
'Search',
'Help',
'Profile Photo',
'HQ',
'Post'
]);
@@ -210,9 +208,10 @@ class Apps {
*
* @param string $f filename
* @param boolean $translate (optional) default true
* @param boolean $sync (optional) default false
* @return boolean|array
*/
static public function parse_app_description($f, $translate = true) {
static public function parse_app_description($f, $translate = true, $sync = false) {
$ret = [];
$matches = [];
@@ -258,7 +257,7 @@ class Apps {
if(array_key_exists('categories',$ret))
$ret['categories'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['categories']);
if(array_key_exists('requires',$ret)) {
if(array_key_exists('requires',$ret) && !$sync) {
$requires = explode(',',$ret['requires']);
foreach($requires as $require) {
$require = trim(strtolower($require));
@@ -310,14 +309,16 @@ class Apps {
}
}
}
if(isset($ret)) {
if($translate)
self::translate_system_apps($ret);
return $ret;
if(empty($ret)) {
return false;
}
return false;
if($translate) {
self::translate_system_apps($ret);
}
return $ret;
}
@@ -343,7 +344,7 @@ class Apps {
'Files' => t('Files'),
'Webpages' => t('Webpages'),
'Wiki' => t('Wiki'),
'Channel Home' => t('Channel Home'),
'Channel' => t('Channel'),
'View Profile' => t('View Profile'),
'Photos' => t('Photos'),
'Calendar' => t('Calendar'),
@@ -374,10 +375,10 @@ class Apps {
'OAuth Apps Manager' => t('OAuth Apps Manager'),
'OAuth2 Apps Manager' => t('OAuth2 Apps Manager'),
'PDL Editor' => t('PDL Editor'),
'Permission Categories' => t('Permission Categories'),
'Contact Roles' => t('Contact Roles'),
'Public Stream' => t('Public Stream'),
'My Chatrooms' => t('My Chatrooms'),
'Channel Export' => t('Channel Export'),
'Channel Export' => t('Channel Export')
);
if(array_key_exists('name',$arr)) {
@@ -624,10 +625,12 @@ class Apps {
$app['uid'] = $uid;
if(self::app_installed($uid,$app,true))
if(self::app_installed($uid,$app,true)) {
$x = self::app_update($app);
else
}
else {
$x = self::app_store($app);
}
if($x['success']) {
$r = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
@@ -635,13 +638,12 @@ class Apps {
intval($uid)
);
if($r) {
if(($app['uid']) && (! $r[0]['app_system'])) {
if($app['uid']) {
if($app['categories'] && (! $app['term'])) {
$r[0]['term'] = q("select * from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP),
intval($r[0]['id'])
);
Libsync::build_sync_packet($uid,array('app' => $r[0]));
}
}
}
@@ -670,6 +672,7 @@ class Apps {
}
}
}
return true;
}
@@ -681,38 +684,35 @@ class Apps {
dbesc($app['guid']),
intval($uid)
);
if($x) {
if(! intval($x[0]['app_deleted'])) {
$x[0]['app_deleted'] = 1;
if(self::can_delete($uid,$app)) {
q("delete from app where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
q("delete from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP),
intval($x[0]['id'])
);
/**
* @hooks app_destroy
* Called after app entry got removed from database
* and provide app array from database.
*/
call_hooks('app_destroy', $x[0]);
}
else {
q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
}
if(! intval($x[0]['app_system'])) {
Libsync::build_sync_packet($uid,array('app' => $x));
}
}
else {
self::app_undestroy($uid,$app);
}
if($x && intval($x[0]['app_deleted'])) {
self::app_undestroy($uid, $app);
return;
}
if(self::can_delete($uid,$app)) {
q("delete from app where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
q("delete from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP),
intval($x[0]['id'])
);
/**
* @hooks app_destroy
* Called after app entry got removed from database
* and provide app array from database.
*/
call_hooks('app_destroy', $x[0]);
}
else {
q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
}
}
}
@@ -729,13 +729,11 @@ class Apps {
dbesc($app['guid']),
intval($uid)
);
if($x) {
if($x[0]['app_system']) {
q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
}
if($x && intval($x[0]['app_deleted']) && $x[0]['app_system']) {
q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
}
}
}
@@ -1194,9 +1192,9 @@ class Apps {
$y = explode(',',$arr['categories']);
if($y) {
foreach($y as $t) {
$t = trim($t);
$t = escape_tags(trim($t));
if($t) {
store_item_tag($darray['app_channel'],$x[0]['id'],TERM_OBJ_APP,TERM_CATEGORY,escape_tags($t),escape_tags(z_root() . '/apps/?f=&cat=' . escape_tags($t)));
store_item_tag($darray['app_channel'], $x[0]['id'], TERM_OBJ_APP, TERM_CATEGORY, $t, z_root() . '/apps/?f=&cat=' . $t);
}
}
}

View File

@@ -261,7 +261,8 @@ class Connect {
'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0),
'abook_created' => datetime_convert(),
'abook_updated' => datetime_convert(),
'abook_instance' => (($singleton) ? z_root() : '')
'abook_instance' => (($singleton) ? z_root() : ''),
'abook_role' => get_pconfig($uid, 'system', 'default_permcat', 'default')
]
);
}
@@ -300,7 +301,7 @@ class Connect {
/** If there is a default group for this channel, add this connection to it */
if ($default_group) {
$g = AccessList::rec_byhash($uid,$default_group);
$g = AccessList::by_hash($uid,$default_group);
if ($g) {
AccessList::member_add($uid,'',$xchan_hash,$g['id']);
}

View File

@@ -127,7 +127,7 @@ class Enotify {
logger('notification: mail');
$subject = sprintf( t('[$Projectname:Notify] New direct message received at %s'), $sitename);
$preamble = sprintf( t('%1$s sent you a new direct message at %2$s.'), $sender['xchan_name'], $sitename);
$preamble = sprintf( t('%1$s sent you a new direct message at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s sent you %2$s.'), '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a direct message') . '[/zrl]');
$sitelink = t('Please visit %s to view and/or reply to your direct messages.');
$tsitelink = sprintf( $sitelink, $siteurl . '/hq/' . gen_link_id($params['item']['mid']));
@@ -238,7 +238,7 @@ class Enotify {
$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']);
$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.');
@@ -256,7 +256,7 @@ class Enotify {
$itemlink = $params['link'];
if (array_key_exists('item',$params) && (! activity_match($params['item']['verb'],ACTIVITY_LIKE))) {
if (array_key_exists('item',$params) && activity_match($params['item']['verb'],ACTIVITY_LIKE)) {
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
@@ -318,7 +318,7 @@ class Enotify {
// 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']);
$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.');
@@ -902,7 +902,7 @@ class Enotify {
static public function format_intros($rr) {
return [
'notify_link' => z_root() . '/connections/ifpending',
'notify_link' => z_root() . '/connections#' . $rr['abook_id'],
'name' => $rr['xchan_name'],
'addr' => $rr['xchan_addr'],
'url' => $rr['xchan_url'],

View File

@@ -1,405 +0,0 @@
<?php
namespace Zotlabs\Lib;
use Zotlabs\Lib\Libsync;
class Group {
static function add($uid,$name,$public = 0) {
$ret = false;
if(x($uid) && x($name)) {
$r = self::byname($uid,$name); // check for dups
if($r !== false) {
// This could be a problem.
// Let's assume we've just created a group which we once deleted
// all the old members are gone, but the group remains so we don't break any security
// access lists. What we're doing here is reviving the dead group, but old content which
// was restricted to this group may now be seen by the new group members.
$z = q("SELECT * FROM pgrp WHERE id = %d LIMIT 1",
intval($r)
);
if(($z) && $z[0]['deleted']) {
q('UPDATE pgrp SET deleted = 0 WHERE id = %d', intval($z[0]['id']));
notice( t('A deleted group with this name was revived. Existing item permissions <strong>may</strong> apply to this group and any future members. If this is not what you intended, please create another group with a different name.') . EOL);
}
return true;
}
do {
$dups = false;
$hash = random_string(32) . str_replace(['<','>'],['.','.'], $name);
$r = q("SELECT id FROM pgrp WHERE hash = '%s' LIMIT 1", dbesc($hash));
if($r)
$dups = true;
} while($dups == true);
$r = q("INSERT INTO pgrp ( hash, uid, visible, gname )
VALUES( '%s', %d, %d, '%s' ) ",
dbesc($hash),
intval($uid),
intval($public),
dbesc($name)
);
$ret = $r;
}
Libsync::build_sync_packet($uid,null,true);
return $ret;
}
static function remove($uid,$name) {
$ret = false;
if(x($uid) && x($name)) {
$r = q("SELECT id, hash FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1",
intval($uid),
dbesc($name)
);
if($r) {
$group_id = $r[0]['id'];
$group_hash = $r[0]['hash'];
}
if(! $group_id)
return false;
// remove group from default posting lists
$r = q("SELECT channel_default_group, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1",
intval($uid)
);
if($r) {
$user_info = $r[0];
$change = false;
if($user_info['channel_default_group'] == $group_hash) {
$user_info['channel_default_group'] = '';
$change = true;
}
if(strpos($user_info['channel_allow_gid'], '<' . $group_hash . '>') !== false) {
$user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']);
$change = true;
}
if(strpos($user_info['channel_deny_gid'], '<' . $group_hash . '>') !== false) {
$user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']);
$change = true;
}
if($change) {
q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s'
WHERE channel_id = %d",
intval($user_info['channel_default_group']),
dbesc($user_info['channel_allow_gid']),
dbesc($user_info['channel_deny_gid']),
intval($uid)
);
}
}
// remove all members
$r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d ",
intval($uid),
intval($group_id)
);
// remove group
$r = q("UPDATE pgrp SET deleted = 1 WHERE uid = %d AND gname = '%s'",
intval($uid),
dbesc($name)
);
$ret = $r;
}
Libsync::build_sync_packet($uid,null,true);
return $ret;
}
static function byname($uid,$name) {
if((! $uid) || (! strlen($name)))
return false;
$r = q("SELECT * FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1",
intval($uid),
dbesc($name)
);
if($r)
return $r[0]['id'];
return false;
}
static function rec_byhash($uid,$hash) {
if((! $uid) || (! strlen($hash)))
return false;
$r = q("SELECT * FROM pgrp WHERE uid = %d AND hash = '%s' LIMIT 1",
intval($uid),
dbesc($hash)
);
if($r)
return $r[0];
return false;
}
static function member_remove($uid,$name,$member) {
$gid = self::byname($uid,$name);
if(! $gid)
return false;
if(! ( $uid && $gid && $member))
return false;
$r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' ",
intval($uid),
intval($gid),
dbesc($member)
);
Libsync::build_sync_packet($uid,null,true);
return $r;
}
static function member_add($uid,$name,$member,$gid = 0) {
if(! $gid)
$gid = self::byname($uid,$name);
if((! $gid) || (! $uid) || (! $member))
return false;
$r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1",
intval($uid),
intval($gid),
dbesc($member)
);
if($r)
return true; // You might question this, but
// we indicate success because the group member was in fact created
// -- It was just created at another time
if(! $r)
$r = q("INSERT INTO pgrp_member (uid, gid, xchan)
VALUES( %d, %d, '%s' ) ",
intval($uid),
intval($gid),
dbesc($member)
);
Libsync::build_sync_packet($uid,null,true);
return $r;
}
static function members($gid) {
$ret = array();
if(intval($gid)) {
$r = q("SELECT * FROM pgrp_member
LEFT JOIN abook ON abook_xchan = pgrp_member.xchan left join xchan on xchan_hash = abook_xchan
WHERE gid = %d AND abook_channel = %d and pgrp_member.uid = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ",
intval($gid),
intval(local_channel()),
intval(local_channel())
);
if($r)
$ret = $r;
}
return $ret;
}
static function members_xchan($gid) {
$ret = [];
if(intval($gid)) {
$r = q("SELECT xchan FROM pgrp_member WHERE gid = %d AND uid = %d",
intval($gid),
intval(local_channel())
);
if($r) {
foreach($r as $rr) {
$ret[] = $rr['xchan'];
}
}
}
return $ret;
}
static function members_profile_xchan($uid,$gid) {
$ret = [];
if(intval($gid)) {
$r = q("SELECT abook_xchan as xchan from abook left join profile on abook_profile = profile_guid where profile.id = %d and profile.uid = %d",
intval($gid),
intval($uid)
);
if($r) {
foreach($r as $rr) {
$ret[] = $rr['xchan'];
}
}
}
return $ret;
}
static function select($uid,$group = '') {
$grps = [];
$o = '';
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
intval($uid)
);
$grps[] = array('name' => '', 'hash' => '0', 'selected' => '');
if($r) {
foreach($r as $rr) {
$grps[] = array('name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : ''));
}
}
logger('select: ' . print_r($grps,true), LOGGER_DATA);
$o = replace_macros(get_markup_template('group_selection.tpl'), array(
'$label' => t('Add new connections to this privacy group'),
'$groups' => $grps
));
return $o;
}
static function widget($every="connections",$each="group",$edit = false, $group_id = 0, $cid = '',$mode = 1) {
$o = '';
if(! (local_channel() && feature_enabled(local_channel(),'groups'))) {
return '';
}
$groups = array();
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
intval($_SESSION['uid'])
);
$member_of = array();
if($cid) {
$member_of = self::containing(local_channel(),$cid);
}
if($r) {
foreach($r as $rr) {
$selected = (($group_id == $rr['id']) ? ' group-selected' : '');
if ($edit) {
$groupedit = [ 'href' => "group/".$rr['id'], 'title' => t('edit') ];
}
else {
$groupedit = null;
}
$groups[] = [
'id' => $rr['id'],
'enc_cid' => base64url_encode($cid),
'cid' => $cid,
'text' => $rr['gname'],
'selected' => $selected,
'href' => (($mode == 0) ? $each.'?f=&gid='.$rr['id'] : $each."/".$rr['id']) . ((x($_GET,'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : ''),
'edit' => $groupedit,
'ismember' => in_array($rr['id'],$member_of),
];
}
}
$tpl = get_markup_template("group_side.tpl");
$o = replace_macros($tpl, array(
'$title' => t('Privacy Groups'),
'$edittext' => t('Edit group'),
'$createtext' => t('Add privacy group'),
'$ungrouped' => (($every === 'contacts') ? t('Channels not in any privacy group') : ''),
'$groups' => $groups,
'$add' => t('add'),
));
return $o;
}
static function expand($g) {
if(! (is_array($g) && count($g)))
return array();
$ret = [];
$x = [];
// private profile linked virtual groups
foreach($g as $gv) {
if(substr($gv,0,3) === 'vp.') {
$profile_hash = substr($gv,3);
if($profile_hash) {
$r = q("select abook_xchan from abook where abook_profile = '%s'",
dbesc($profile_hash)
);
if($r) {
foreach($r as $rv) {
$ret[] = $rv['abook_xchan'];
}
}
}
}
else {
$x[] = $gv;
}
}
if($x) {
stringify_array_elms($x,true);
$groups = implode(',', $x);
if($groups) {
$r = q("SELECT xchan FROM pgrp_member WHERE gid IN ( select id from pgrp where hash in ( $groups ))");
if($r) {
foreach($r as $rr) {
$ret[] = $rr['xchan'];
}
}
}
}
return $ret;
}
static function member_of($c) {
$r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id WHERE pgrp_member.xchan = '%s' AND pgrp.deleted = 0 ORDER BY pgrp.gname ASC ",
dbesc($c)
);
return $r;
}
static function containing($uid,$c) {
$r = q("SELECT gid FROM pgrp_member WHERE uid = %d AND pgrp_member.xchan = '%s' ",
intval($uid),
dbesc($c)
);
$ret = array();
if($r) {
foreach($r as $rr)
$ret[] = $rr['gid'];
}
return $ret;
}
}

View File

@@ -141,7 +141,6 @@ class Libsync {
logger('Packet: ' . print_r($info, true), LOGGER_DATA, LOG_DEBUG);
$total = count($synchubs);
foreach ($synchubs as $hub) {
$hash = random_string();
$n = Libzot::build_packet($channel, 'sync', $env_recips, json_encode($info), 'hz', $hub['hubloc_sitekey'], $hub['site_crypto']);
@@ -186,7 +185,6 @@ class Libsync {
require_once('include/import.php');
$result = [];
$keychange = ((array_key_exists('keychange', $arr)) ? true : false);
foreach ($deliveries as $d) {
@@ -232,8 +230,35 @@ class Libsync {
if (array_key_exists('config', $arr) && is_array($arr['config']) && count($arr['config'])) {
foreach ($arr['config'] as $cat => $k) {
foreach ($arr['config'][$cat] as $k => $v)
set_pconfig($channel['channel_id'], $cat, $k, $v);
$pconfig_updated = [];
foreach($arr['config'][$cat] as $k => $v) {
if ($cat === 'hz_delpconfig' && strpos($k, 'b64.') === 0) {
$delpconfig = explode(':', unpack_link_id($k));
// delete the provided pconfig
del_pconfig($channel['channel_id'], $delpconfig[0], $delpconfig[1], $v);
// delete the messenger pconfig
del_pconfig($channel['channel_id'], 'hz_delpconfig', $k);
}
if (strpos($k,'pcfgud:') === 0) {
$realk = substr($k,7);
$pconfig_updated[$realk] = $v;
unset($arr['config'][$cat][$k]);
}
}
foreach($arr['config'][$cat] as $k => $v) {
if (!isset($pconfig_updated[$k])) {
$pconfig_updated[$k] = NULL;
}
if ($cat !== 'hz_delpconfig') {
set_pconfig($channel['channel_id'],$cat,$k,$v,$pconfig_updated[$k]);
}
}
}
}
@@ -246,6 +271,10 @@ class Libsync {
if (array_key_exists('app', $arr) && $arr['app'])
sync_apps($channel, $arr['app']);
if (array_key_exists('sysapp',$arr) && $arr['sysapp']) {
sync_sysapps($channel, $arr['sysapp']);
}
if (array_key_exists('addressbook', $arr) && $arr['addressbook'])
sync_addressbook($channel, $arr['addressbook']);
@@ -255,8 +284,8 @@ class Libsync {
if (array_key_exists('chatroom', $arr) && $arr['chatroom'])
sync_chatrooms($channel, $arr['chatroom']);
if (array_key_exists('mail', $arr) && $arr['mail'])
sync_mail($channel, $arr['mail']);
//if (array_key_exists('mail', $arr) && $arr['mail'])
// sync_mail($channel, $arr['mail']);
if (array_key_exists('event', $arr) && $arr['event'])
sync_events($channel, $arr['event']);
@@ -270,8 +299,8 @@ class Libsync {
// deprecated, maintaining for a few months for upward compatibility
// this should sync webpages, but the logic is a bit subtle
if (array_key_exists('item_id', $arr) && $arr['item_id'])
sync_items($channel, $arr['item_id']);
//if (array_key_exists('item_id', $arr) && $arr['item_id'])
// sync_items($channel, $arr['item_id']);
if (array_key_exists('menu', $arr) && $arr['menu'])
sync_menus($channel, $arr['menu']);
@@ -382,19 +411,42 @@ class Libsync {
// This relies on the undocumented behaviour that red sites send xchan info with the abook
// and import_author_xchan will look them up on all federated networks
if ($abook['abook_xchan'] && $abook['xchan_addr']) {
$found = false;
if ($abook['abook_xchan'] && $abook['xchan_addr'] && (! in_array($abook['xchan_network'], [ 'token', 'unknown' ]))) {
$h = Libzot::get_hublocs($abook['abook_xchan']);
if (!$h) {
if ($h) {
$found = true;
}
else {
$xhash = import_author_xchan(encode_item_xchan($abook));
if (!$xhash) {
if ($xhash) {
$found = true;
}
else {
logger('Import of ' . $abook['xchan_addr'] . ' failed.');
continue;
}
}
}
if (!$found && !in_array($abook['xchan_network'], ['zot6', 'activitypub', 'diaspora'])) {
// just import the record.
$xc = [];
foreach ($abook as $k => $v) {
if (strpos($k,'xchan_') === 0) {
$xc[$k] = $v;
}
}
$r = q("select * from xchan where xchan_hash = '%s'",
dbesc($xc['xchan_hash'])
);
if (! $r) {
xchan_store_lowlevel($xc);
}
}
foreach ($abook as $k => $v) {
if (in_array($k, $disallowed) || (strpos($k, 'abook') !== 0)) {
if (in_array($k, $disallowed) || (strpos($k, 'abook_') !== 0)) {
continue;
}
if (!in_array($k, $fields)) {
@@ -408,6 +460,13 @@ class Libsync {
if (array_key_exists('abook_instance', $clean) && $clean['abook_instance'] && strpos($clean['abook_instance'], z_root()) === false) {
$clean['abook_not_here'] = 1;
// guest pass or access token - don't try to probe since it is one-way
// we are relying on the undocumented behaviour that the abook record also contains the xchan
if ($abook['xchan_network'] === 'token') {
$clean['abook_instance'] .= ',';
$clean['abook_instance'] .= z_root();
}
}
@@ -704,6 +763,15 @@ class Libsync {
$ret = [];
// If a sender reports that the channel has been deleted, delete its hubloc
if (isset($arr['deleted_locally']) && intval($arr['deleted_locally'])) {
q("UPDATE hubloc SET hubloc_deleted = 1, hubloc_updated = '%s' WHERE hubloc_hash = '%s' AND hubloc_url = '%s'",
dbesc(datetime_convert()),
dbesc($sender['hash']),
dbesc($sender['site']['url'])
);
}
if ($arr['locations']) {
if ($absolute)
@@ -757,14 +825,13 @@ class Libsync {
// match as many fields as possible in case anything at all changed.
$r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_id_url = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_site_id = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ",
$r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_id_url = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ",
dbesc($sender['hash']),
dbesc($sender['id']),
dbesc($sender['id_sig']),
dbesc($location['id_url']),
dbesc($location['url']),
dbesc($location['url_sig']),
dbesc($location['site_id']),
dbesc($location['host']),
dbesc($location['address']),
dbesc($location['callback']),
@@ -773,6 +840,15 @@ class Libsync {
if ($r) {
logger('Hub exists: ' . $location['url'], LOGGER_DEBUG);
// generate a new hubloc_site_id if it's wrong due to historical bugs 2021-11-30
if ($r[0]['hubloc_site_id'] !== $location['site_id']) {
q("update hubloc set hubloc_site_id = '%s' where hubloc_id = %d",
dbesc(Libzot::make_xchan_hash($location['url'], $location['sitekey'])),
intval($r[0]['hubloc_id'])
);
}
// update connection timestamp if this is the site we're talking to
// This only happens when called from import_xchan
@@ -861,6 +937,7 @@ class Libsync {
$what .= 'delete_hub ';
$changed = true;
}
continue;
}

View File

@@ -266,7 +266,7 @@ class Libzot {
dbesc($them['xchan_addr'])
);
}
if (!$r) {
if (!$r && array_key_exists('xchan_hash', $them) && $them['xchan_hash']) {
$r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_hash = '%s' order by hubloc_id desc",
dbesc($them['xchan_hash'])
);
@@ -275,8 +275,8 @@ class Libzot {
if ($r) {
foreach ($r as $rr) {
if (intval($rr['hubloc_primary'])) {
$url = $rr['hubloc_id_url'];
$record = $rr;
$url = $rr['hubloc_id_url'];
break;
}
}
if (!$url) {
@@ -284,13 +284,17 @@ class Libzot {
}
}
}
if (!$url) {
logger('zot_refresh: no url');
return false;
}
$m = parse_url($url);
$site_url = unparse_url([ 'scheme' => $m['scheme'], 'host' => $m['host'] ]);
$s = q("select site_dead from site where site_url = '%s' limit 1",
dbesc($url)
dbesc($site_url)
);
if ($s && intval($s[0]['site_dead']) && (!$force)) {
@@ -299,25 +303,25 @@ class Libzot {
}
$record = Zotfinger::exec($url, $channel);
// Check the HTTP signature
// Check the HTTP signature
$hsig = $record['signature'];
if ($hsig && $hsig['signer'] === $url && $hsig['header_valid'] === true && $hsig['content_valid'] === true)
if ($hsig && $hsig['signer'] === $url && $hsig['header_valid'] === true && $hsig['content_valid'] === true) {
$hsig_valid = true;
}
if (!$hsig_valid) {
logger('http signature not valid: ' . print_r($hsig, true));
return false;
}
logger('zot-info: ' . print_r($record, true), LOGGER_DATA, LOG_DEBUG);
$x = self::import_xchan($record['data'], (($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED));
if (!$x['success'])
if (!$x['success']) {
return false;
}
if ($channel && $record['data']['permissions']) {
$permissions = explode(',', $record['data']['permissions']);
@@ -357,8 +361,9 @@ class Libzot {
// we have as we may have updated the year after sending a notification; and resetting
// to the one we just received would cause us to create duplicated events.
if (substr($r[0]['abook_dob'], 5) == substr($next_birthday, 5))
if (substr($r[0]['abook_dob'], 5) == substr($next_birthday, 5)) {
$next_birthday = $r[0]['abook_dob'];
}
$y = q("update abook set abook_dob = '%s'
where abook_xchan = '%s' and abook_channel = %d
@@ -368,20 +373,23 @@ class Libzot {
intval($channel['channel_id'])
);
if (!$y)
if (!$y) {
logger('abook update failed');
}
else {
// if we were just granted read stream permission and didn't have it before, try to pull in some posts
if ((!$old_read_stream_perm) && (intval($permissions['view_stream'])))
if (!$old_read_stream_perm && intval($permissions['view_stream'])) {
Master::Summon(['Onepoll', $r[0]['abook_id']]);
}
}
}
else {
$p = Permissions::connect_perms($channel['channel_id']);
$my_perms = $p['perms'];
$p = Permissions::connect_perms($channel['channel_id']);
$my_perms = $p['perms'];
$automatic = $p['automatic'];
$role = (($automatic) ? $p['role'] : '');
// new connection
@@ -403,7 +411,8 @@ class Libzot {
'abook_created' => datetime_convert(),
'abook_updated' => datetime_convert(),
'abook_dob' => $next_birthday,
'abook_pending' => intval(($automatic) ? 0 : 1)
'abook_pending' => intval(($automatic) ? 0 : 1),
'abook_role' => $role
]
);
@@ -419,53 +428,62 @@ class Libzot {
);
if ($new_connection) {
if (!Permissions::PermsCompare($new_perms, $previous_perms))
if (!Permissions::PermsCompare($new_perms, $previous_perms)) {
Master::Summon(['Notifier', 'permission_create', $new_connection[0]['abook_id']]);
}
Enotify::submit(
[
'type' => NOTIFY_INTRO,
'from_xchan' => $x['hash'],
'to_xchan' => $channel['channel_hash'],
'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id']
'link' => z_root() . '/connections#' . $new_connection[0]['abook_id']
]
);
if (intval($permissions['view_stream'])) {
if (intval(get_pconfig($channel['channel_id'], 'perm_limits', 'send_stream') & PERMS_PENDING)
|| (!intval($new_connection[0]['abook_pending'])))
|| (!intval($new_connection[0]['abook_pending']))) {
Master::Summon(['Onepoll', $new_connection[0]['abook_id']]);
}
}
// If there is a default group for this channel, add this connection to it
// for pending connections this will happens at acceptance time.
// for pending connections this will happen at acceptance time.
if (!intval($new_connection[0]['abook_pending'])) {
$default_group = $channel['channel_default_group'];
if ($default_group) {
$g = Group::rec_byhash($channel['channel_id'], $default_group);
if ($g)
Group::member_add($channel['channel_id'], '', $x['hash'], $g['id']);
$g = AccessList::by_hash($channel['channel_id'], $default_group);
if ($g) {
AccessList::member_add($channel['channel_id'], '', $x['hash'], $g['id']);
}
}
}
unset($new_connection[0]['abook_id']);
unset($new_connection[0]['abook_account']);
unset($new_connection[0]['abook_channel']);
$abconfig = load_abconfig($channel['channel_id'], $new_connection['abook_xchan']);
if ($abconfig)
if ($abconfig) {
$new_connection['abconfig'] = $abconfig;
}
Libsync::build_sync_packet($channel['channel_id'], ['abook' => $new_connection]);
}
}
}
return true;
}
return false;
}
/**
* @brief Look up if channel is known and previously verified.
*
@@ -479,6 +497,7 @@ class Libzot {
* * \e string \b id_sig => id signed with conversant's private key
* * \e string \b location => URL of the origination hub of this communication
* * \e string \b location_sig => URL signed with conversant's private key
* * \e string \b site_id => URL signed with conversant's private key
* @param boolean $multiple (optional) default false
*
* @return array|null
@@ -488,7 +507,7 @@ class Libzot {
static function gethub($arr, $multiple = false) {
if ($arr['id'] && $arr['id_sig'] && $arr['location'] && $arr['location_sig']) {
if ($arr['id'] && $arr['id_sig'] && $arr['location'] && $arr['location_sig'] && $arr['site_id']) {
if (!check_siteallowed($arr['location'])) {
logger('blacklisted site: ' . $arr['location']);
@@ -512,9 +531,9 @@ class Libzot {
logger('Found', LOGGER_DEBUG);
return (($multiple) ? $r : $r[0]);
}
logger('Not found: ' . print_r($arr, true), LOGGER_DEBUG);
}
logger('Not found: ' . print_r($arr, true), LOGGER_DEBUG);
logger('Incomplete array: ' . print_r($arr, true), LOGGER_DEBUG);
return false;
}
@@ -616,7 +635,6 @@ class Libzot {
*/
static function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
/**
* @hooks import_xchan
* Called when processing the result of zot_finger() to store the result
@@ -666,6 +684,7 @@ class Libzot {
$arr['connect_url'] = '';
if ($r) {
if ($arr['photo'] && array_key_exists('updated', $arr['photo']) && $arr['photo']['updated'] > $r[0]['xchan_photo_date'])
$import_photos = true;
@@ -1126,6 +1145,7 @@ class Libzot {
if ($env['encoding'] === 'activitystreams') {
$AS = new ActivityStreams($data);
if (!$AS->is_valid()) {
logger('Activity rejected: ' . print_r($data, true));
return;
@@ -1141,8 +1161,6 @@ class Libzot {
}
$deliveries = null;
if (array_key_exists('recipients', $env) && count($env['recipients'])) {
@@ -1200,31 +1218,41 @@ class Libzot {
if (in_array($env['type'], ['activity', 'response'])) {
$r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s' ",
if(empty($AS->actor['id'])) {
logger('No actor id!');
return;
}
$r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s'",
dbesc($AS->actor['id'])
);
if ($r) {
// selects a zot6 hash if available, otherwise use whatever we have
$r = self::zot_record_preferred($r);
$r = self::zot_record_preferred($r);
$arr['author_xchan'] = $r['hubloc_hash'];
}
if (!$arr['author_xchan']) {
if (! $arr['author_xchan']) {
logger('No author!');
return;
}
$s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($env['sender'])
);
$arr['owner_xchan'] = $env['sender'];
// in individual delivery, change owner if needed
if ($s) {
$arr['owner_xchan'] = $s[0]['hubloc_hash'];
if(filter_var($env['sender'], FILTER_VALIDATE_URL)) {
// in individual delivery, change owner if needed
$s = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($env['sender'])
);
if ($s) {
$arr['owner_xchan'] = $s[0]['hubloc_hash'];
}
}
else {
$arr['owner_xchan'] = $env['sender'];
if (! $arr['owner_xchan']) {
logger('No owner!');
return;
}
if ($private && (!intval($arr['item_private']))) {
@@ -1329,7 +1357,7 @@ class Libzot {
static function find_parent($env, $act) {
if ($act) {
if (in_array($act->type, ['Like', 'Dislike'])) {
if (in_array($act->type, ['Like', 'Dislike']) && is_array($act->obj)) {
return $act->obj['id'];
}
if ($act->parent_id) {
@@ -1459,10 +1487,11 @@ class Libzot {
* @param boolean $relay
* @param boolean $public (optional) default false
* @param boolean $request (optional) default false
* @param boolean $force (optional) default false - should only be set for manual fetch
* @return array
*/
static function process_delivery($sender, $act, $arr, $deliveries, $relay, $public = false, $request = false) {
static function process_delivery($sender, $act, $arr, $deliveries, $relay, $public = false, $request = false, $force = false) {
$result = [];
@@ -1564,7 +1593,8 @@ class Libzot {
if ((!$tag_delivery) && (!$local_public)) {
$allowed = (perm_is_allowed($channel['channel_id'], $sender, $perm));
if (!$allowed) {
if ((!$allowed) && $perm === 'post_comments') {
$parent = q("select * from item where mid = '%s' and uid = %d limit 1",
dbesc($arr['parent_mid']),
intval($channel['channel_id'])
@@ -1590,7 +1620,7 @@ class Libzot {
// doesn't exist.
if ($perm === 'send_stream') {
if (get_pconfig($channel['channel_id'], 'system', 'hyperdrive', false) || $arr['verb'] === ACTIVITY_SHARE) {
if ($force || get_pconfig($channel['channel_id'], 'system', 'hyperdrive', false) || $arr['verb'] === ACTIVITY_SHARE) {
$allowed = true;
}
}
@@ -1714,7 +1744,7 @@ class Libzot {
}
}
$ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'",
$ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'",
intval($channel['channel_id']),
dbesc($arr['owner_xchan'])
);
@@ -1865,7 +1895,7 @@ class Libzot {
return $result;
}
static public function fetch_conversation($channel, $mid) {
static public function fetch_conversation($channel, $mid, $force = false) {
// Use Zotfinger to create a signed request
@@ -1969,7 +1999,7 @@ class Libzot {
logger('FOF Activity received: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG);
logger('FOF Activity recipient: ' . $channel['channel_hash'], LOGGER_DATA, LOG_DEBUG);
$result = self::process_delivery($arr['owner_xchan'], $AS, $arr, [$channel['channel_hash']], false, false, true);
$result = self::process_delivery($arr['owner_xchan'], $AS, $arr, [$channel['channel_hash']], false, false, true, $force);
if ($result) {
$ret = array_merge($ret, $result);
}
@@ -2636,8 +2666,8 @@ class Libzot {
);
if ($r) {
logger('found another site that is not dead: ' . $r[0]['hubloc_url'], LOGGER_DEBUG, LOG_INFO);
$desturl = $r[0]['hubloc_url'];
logger('found another site that is not dead: ' . $r[0]['hubloc_id_url'], LOGGER_DEBUG, LOG_INFO);
$desturl = $r[0]['hubloc_id_url'];
}
else {
return $hash;
@@ -2745,7 +2775,6 @@ class Libzot {
}
$e = $r[0];
$id = $e['channel_id'];
$sys_channel = (intval($e['channel_system']) ? true : false);
@@ -2758,28 +2787,6 @@ class Libzot {
if ($deleted || $censored || $sys_channel)
$searchable = false;
$public_forum = false;
$role = get_pconfig($e['channel_id'], 'system', 'permissions_role');
if ($role === 'forum' || $role === 'repository') {
$public_forum = true;
}
else {
// check if it has characteristics of a public forum based on custom permissions.
$m = Permissions::FilledAutoperms($e['channel_id']);
if ($m) {
foreach ($m as $k => $v) {
if ($k == 'tag_deliver' && intval($v) == 1)
$ch++;
if ($k == 'send_stream' && intval($v) == 0)
$ch++;
}
if ($ch == 2)
$public_forum = true;
}
}
// This is for birthdays and keywords, but must check access permissions
$p = q("select * from profile where uid = %d and is_default = 1",
intval($e['channel_id'])
@@ -2848,6 +2855,7 @@ class Libzot {
];
$ret['channel_role'] = get_pconfig($e['channel_id'], 'system', 'permissions_role', 'custom');
$ret['channel_type'] = ((get_pconfig($e['channel_id'], 'system', 'group_actor')) ? 'group' : 'normal');
$hookinfo = [
'channel_id' => $id,
@@ -2863,16 +2871,19 @@ class Libzot {
$ret['protocols'] = $hookinfo['protocols'];
$ret['searchable'] = $searchable;
$ret['adult_content'] = $adult_channel;
$ret['public_forum'] = $public_forum;
// now all forums (public, restricted, and private) set the public_forum flag. So it really means "is a group"
// and has nothing to do with accessibility.
$ret['public_forum'] = get_pconfig($e['channel_id'], 'system', 'group_actor');
$ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'], 'post_comments'));
$ret['mail'] = map_scope(PermissionLimits::Get($e['channel_id'], 'post_mail'));
if ($deleted)
$ret['deleted'] = $deleted;
if (intval($e['channel_removed']))
if (intval($e['channel_removed'])) {
$ret['deleted_locally'] = true;
}
// premium or other channel desiring some contact with potential followers before connecting.
// This is a template - %s will be replaced with the follow_url we discover for the return channel.

View File

@@ -12,8 +12,8 @@ class NativeWiki {
public static function listwikis($channel, $observer_hash) {
$sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash);
$wikis = q("SELECT * FROM item
WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra",
$wikis = q("SELECT * FROM item
WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra",
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
intval($channel['channel_id'])
);
@@ -49,7 +49,7 @@ class NativeWiki {
$mid = z_root() . '/item/' . $uuid;
$arr = array(); // Initialize the array of parameters for the post
$item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0);
$item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0);
$wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName'];
$arr['aid'] = $channel['channel_account_id'];
$arr['uuid'] = $uuid;
@@ -61,8 +61,8 @@ class NativeWiki {
$arr['resource_id'] = $resource_id;
$arr['owner_xchan'] = $channel['channel_hash'];
$arr['author_xchan'] = $observer_hash;
$arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']);
$arr['llink'] = $arr['plink'];
$arr['plink'] = $mid;
$arr['llink'] = z_root() . '/display/' . gen_link_id($mid);
$arr['title'] = $wiki['htmlName']; // name of new wiki;
$arr['allow_cid'] = $ac['allow_cid'];
$arr['allow_gid'] = $ac['allow_gid'];
@@ -133,13 +133,13 @@ class NativeWiki {
// update acl for any existing wiki pages
q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d where resource_type = 'nwikipage' and resource_id = '%s'",
dbesc($item['allow_cid']),
dbesc($item['allow_gid']),
dbesc($item['deny_cid']),
dbesc($item['deny_gid']),
dbesc($item['item_private']),
dbesc($item['allow_cid']),
dbesc($item['allow_gid']),
dbesc($item['deny_cid']),
dbesc($item['deny_gid']),
dbesc($item['item_private']),
dbesc($arr['resource_id'])
);
);
if($update['item_id']) {
@@ -211,12 +211,12 @@ class NativeWiki {
public static function get_wiki($channel_id, $observer_hash, $resource_id) {
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
$item = q("SELECT * FROM item WHERE uid = %d AND resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0
$item = q("SELECT * FROM item WHERE uid = %d AND resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0
$sql_extra ORDER BY id LIMIT 1",
intval($channel_id),
intval($channel_id),
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
dbesc($resource_id)
);
@@ -224,7 +224,7 @@ class NativeWiki {
return [ 'wiki' => null ];
}
else {
$w = $item[0]; // wiki item table record
// Get wiki metadata
$rawName = get_iconfig($w, 'wiki', 'rawName');
@@ -246,20 +246,20 @@ class NativeWiki {
public static function exists_by_name($uid, $urlName) {
$sql_extra = item_permissions_sql($uid);
$sql_extra = item_permissions_sql($uid);
$item = q("SELECT item.id, resource_id FROM item left join iconfig on iconfig.iid = item.id
WHERE resource_type = '%s' AND iconfig.v = '%s' AND uid = %d
AND item_deleted = 0 $sql_extra limit 1",
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
//dbesc(urldecode($urlName)),
$item = q("SELECT item.id, resource_id FROM item left join iconfig on iconfig.iid = item.id
WHERE resource_type = '%s' AND iconfig.v = '%s' AND uid = %d
AND item_deleted = 0 $sql_extra limit 1",
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
//dbesc(urldecode($urlName)),
dbesc(self::name_decode($urlName)),
intval($uid)
);
if($item) {
return array('id' => $item[0]['id'], 'resource_id' => $item[0]['resource_id']);
}
}
else {
return array('id' => null, 'resource_id' => null);
}
@@ -277,7 +277,7 @@ class NativeWiki {
$r = q("SELECT * FROM item WHERE uid = %d and resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1",
intval($owner_id),
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
dbesc($resource_id)
);
@@ -285,8 +285,6 @@ class NativeWiki {
return array('read' => false, 'write' => false, 'success' => true);
}
else {
// TODO: Create a new permission setting for wiki analogous to webpages. Until
// then, use webpage permissions
$write = perm_is_allowed($owner_id, $observer_hash,'write_wiki');
return array('read' => true, 'write' => $write, 'success' => true);
}

View File

@@ -2,14 +2,15 @@
namespace Zotlabs\Lib;
use \Zotlabs\Lib as Zlib;
use App;
use Zotlabs\Access\PermissionLimits;
class NativeWikiPage {
static public function page_list($channel_id,$observer_hash, $resource_id) {
static public function page_list($channel_id, $observer_hash, $resource_id) {
// TODO: Create item table records for pages so that metadata like title can be applied
$w = Zlib\NativeWiki::get_wiki($channel_id,$observer_hash,$resource_id);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
$pages[] = [
'resource_id' => '',
@@ -18,134 +19,149 @@ class NativeWikiPage {
'link_id' => 'id_wiki_home_0'
];
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
$sql_extra = item_permissions_sql($channel_id, $observer_hash);
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and item_deleted = 0
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and item_deleted = 0
$sql_extra order by title asc",
dbesc($resource_id),
intval($channel_id)
);
if($r) {
if ($r) {
$x = [];
$y = [];
foreach($r as $rv) {
if(! in_array($rv['mid'],$x)) {
foreach ($r as $rv) {
if (!in_array($rv['mid'], $x)) {
$y[] = $rv;
$x[] = $rv['mid'];
}
}
$items = fetch_post_tags($y,true);
$items = fetch_post_tags($y, true);
foreach($items as $page_item) {
$title = get_iconfig($page_item['id'],'nwikipage','pagetitle',t('(No Title)'));
if(urldecode($title) !== 'Home') {
foreach ($items as $page_item) {
$title = get_iconfig($page_item['id'], 'nwikipage', 'pagetitle', t('(No Title)'));
if (urldecode($title) !== 'Home') {
$pages[] = [
'resource_id' => $resource_id,
'title' => escape_tags($title),
//'url' => str_replace('%2F','/',urlencode(str_replace('%2F','/',urlencode($title)))),
'url' => Zlib\NativeWiki::name_encode($title),
'url' => NativeWiki::name_encode($title),
'link_id' => 'id_' . substr($resource_id, 0, 10) . '_' . $page_item['id']
];
}
}
}
return array('pages' => $pages, 'wiki' => $w);
return ['pages' => $pages, 'wiki' => $w];
}
static public function create_page($channel_id, $observer_hash, $name, $resource_id, $mimetype = 'text/bbcode') {
static public function create_page($channel, $observer_hash, $name, $resource_id, $mimetype = 'text/bbcode') {
logger('mimetype: ' . $mimetype);
if(! in_array($mimetype,[ 'text/markdown','text/bbcode','text/plain','text/html' ]))
if (!in_array($mimetype, ['text/markdown', 'text/bbcode', 'text/plain', 'text/html']))
$mimetype = 'text/markdown';
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
$w = NativeWiki::get_wiki($channel['channel_id'], $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
if (!$w['wiki']) {
return ['content' => null, 'message' => 'Error reading wiki', 'success' => false];
}
// backslashes won't work well in the javascript functions
$name = str_replace('\\','',$name);
$name = str_replace('\\', '', $name);
$uuid = new_uuid();
$mid = z_root() . '/item/' . $uuid;
// create an empty activity
$arr = [];
$arr['uid'] = $channel_id;
$arr['author_xchan'] = $observer_hash;
$arr['mimetype'] = $mimetype;
$arr['title'] = $name;
$arr['resource_type'] = 'nwikipage';
$arr['resource_id'] = $resource_id;
$arr['allow_cid'] = $w['wiki']['allow_cid'];
$arr['allow_gid'] = $w['wiki']['allow_gid'];
$arr['deny_cid'] = $w['wiki']['deny_cid'];
$arr['deny_gid'] = $w['wiki']['deny_gid'];
$arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel_id,'view_wiki'),true);
$arr = [];
$arr['aid'] = $channel['channel_account_id'];
$arr['uid'] = $channel['channel_id'];
$arr['mid'] = $mid;
$arr['parent_mid'] = $w['wiki']['mid'];
$arr['parent'] = $w['wiki']['parent'];
$arr['uuid'] = $uuid;
$arr['item_hidden'] = $w['wiki']['item_hidden'];
$arr['plink'] = $mid;
$arr['llink'] = z_root() . '/display/' . gen_link_id($mid);
$arr['author_xchan'] = $observer_hash;
$arr['mimetype'] = $mimetype;
$arr['title'] = $name;
$arr['resource_type'] = 'nwikipage';
$arr['resource_id'] = $resource_id;
$arr['allow_cid'] = $w['wiki']['allow_cid'];
$arr['allow_gid'] = $w['wiki']['allow_gid'];
$arr['deny_cid'] = $w['wiki']['deny_cid'];
$arr['deny_gid'] = $w['wiki']['deny_gid'];
$arr['item_private'] = $w['wiki']['item_private'];
$arr['item_wall'] = 1;
$arr['item_origin'] = 1;
$arr['item_thread_top'] = 1;
$arr['verb'] = ACTIVITY_CREATE;
$arr['obj_type'] = 'Document';
// TODO: add an object?
$arr['public_policy'] = map_scope(PermissionLimits::Get($channel['channel_id'], 'view_wiki'), true);
// We may wish to change this some day.
$arr['item_unpublished'] = 1;
set_iconfig($arr,'nwikipage','pagetitle',(($name) ? $name : t('(No Title)')),true);
set_iconfig($arr, 'nwikipage', 'pagetitle', (($name) ? $name : t('(No Title)')), true);
$p = item_store($arr, false, false);
$p = post_activity_item($arr, false, false);
if($p['item_id']) {
$page = [
if ($p['item_id']) {
$page = [
'rawName' => $name,
'htmlName' => escape_tags($name),
//'urlName' => urlencode($name),
'urlName' => Zlib\NativeWiki::name_encode($name)
//'urlName' => urlencode($name),
'urlName' => NativeWiki::name_encode($name)
];
return array('page' => $page, 'item_id' => $p['item_id'], 'item' => $p['activity'], 'wiki' => $w, 'message' => '', 'success' => true);
return ['page' => $page, 'item_id' => $p['item_id'], 'item' => $p['activity'], 'wiki' => $w, 'message' => '', 'success' => true];
}
return [ 'success' => false, 'message' => t('Wiki page create failed.') ];
return ['success' => false, 'message' => t('Wiki page create failed.')];
}
static public function rename_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$pageNewName = ((array_key_exists('pageNewName',$arr)) ? $arr['pageNewName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
$pageNewName = ((array_key_exists('pageNewName', $arr)) ? $arr['pageNewName'] : '');
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if(! $w['wiki']) {
return array('message' => t('Wiki not found.'), 'success' => false);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return ['message' => t('Wiki not found.'), 'success' => false];
}
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
intval($channel_id),
dbesc($pageNewName)
);
if($ic) {
return [ 'success' => false, 'message' => t('Destination name already exists') ];
if ($ic) {
return ['success' => false, 'message' => t('Destination name already exists')];
}
$ids = [];
$ic = q("select *, item.id as item_id from iconfig left join item on iconfig.iid = item.id
$ic = q("select *, item.id as item_id from iconfig left join item on iconfig.iid = item.id
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
intval($channel_id),
dbesc($pageUrlName)
);
if($ic) {
foreach($ic as $c) {
set_iconfig($c['item_id'],'nwikipage','pagetitle',$pageNewName);
if ($ic) {
foreach ($ic as $c) {
set_iconfig($c['item_id'], 'nwikipage', 'pagetitle', $pageNewName);
$ids[] = $c['item_id'];
}
@@ -154,105 +170,101 @@ class NativeWikiPage {
dbesc($pageNewName)
);
$page = [
'rawName' => $pageNewName,
'htmlName' => escape_tags($pageNewName),
$page = [
'rawName' => $pageNewName,
'htmlName' => escape_tags($pageNewName),
//'urlName' => urlencode(escape_tags($pageNewName))
'urlName' => Zlib\NativeWiki::name_encode($pageNewName)
'urlName' => NativeWiki::name_encode($pageNewName)
];
return [ 'success' => true, 'page' => $page ];
return ['success' => true, 'page' => $page];
}
return [ 'success' => false, 'message' => t('Page not found') ];
return ['success' => false, 'message' => t('Page not found')];
}
static public function get_page_content($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? intval($arr['channel_id']) : 0);
$revision = ((array_key_exists('revision',$arr)) ? intval($arr['revision']) : (-1));
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id', $arr)) ? intval($arr['channel_id']) : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return ['content' => null, 'message' => 'Error reading wiki', 'success' => false];
}
$item = self::load_page($arr);
if($item) {
if ($item) {
$content = $item['body'];
return [
return [
'content' => $content,
'mimeType' => $w['mimeType'],
'pageMimeType' => $item['mimetype'],
'message' => '',
'pageMimeType' => $item['mimetype'],
'message' => '',
'success' => true
];
}
return array('content' => null, 'message' => t('Error reading page content'), 'success' => false);
return ['content' => null, 'message' => t('Error reading page content'), 'success' => false];
}
static public function page_history($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return array('history' => null, 'message' => 'Error reading wiki', 'success' => false);
return ['history' => null, 'message' => 'Error reading wiki', 'success' => false];
}
$items = self::load_page_history($arr);
$history = [];
if($items) {
if ($items) {
$processed = 0;
foreach($items as $item) {
if($processed > 1000)
foreach ($items as $item) {
if ($processed > 1000)
break;
$processed ++;
$history[] = [
$processed++;
$history[] = [
'revision' => $item['revision'],
'date' => datetime_convert('UTC',date_default_timezone_get(),$item['edited']),
'name' => $item['author']['xchan_name'],
'title' => get_iconfig($item,'nwikipage','commit_msg')
'date' => datetime_convert('UTC', date_default_timezone_get(), $item['edited']),
'name' => $item['author']['xchan_name'],
'title' => get_iconfig($item, 'nwikipage', 'commit_msg')
];
}
return [ 'success' => true, 'history' => $history ];
return ['success' => true, 'history' => $history];
}
return [ 'success' => false ];
return ['success' => false];
}
static public function load_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1));
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
$revision = ((array_key_exists('revision', $arr)) ? $arr['revision'] : (-1));
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
if (!$w['wiki']) {
return ['content' => null, 'message' => 'Error reading wiki', 'success' => false];
}
$ids = '';
@@ -262,32 +274,32 @@ class NativeWikiPage {
dbesc($pageUrlName)
);
if($ic) {
foreach($ic as $c) {
if($ids)
if ($ic) {
foreach ($ic as $c) {
if ($ids)
$ids .= ',';
$ids .= intval($c['iid']);
}
}
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
$sql_extra = item_permissions_sql($channel_id, $observer_hash);
if($revision == (-1))
if ($revision == (-1))
$sql_extra .= " order by revision desc ";
elseif($revision)
elseif ($revision)
$sql_extra .= " and revision = " . intval($revision) . " ";
$r = null;
if($ids) {
if ($ids) {
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) $sql_extra limit 1",
dbesc($resource_id),
intval($channel_id)
);
if($r) {
$items = fetch_post_tags($r,true);
if ($r) {
$items = fetch_post_tags($r, true);
return $items[0];
}
}
@@ -298,15 +310,14 @@ class NativeWikiPage {
static public function load_page_history($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1));
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return ['content' => null, 'message' => 'Error reading wiki', 'success' => false];
}
$ids = '';
@@ -315,28 +326,28 @@ class NativeWikiPage {
intval($channel_id),
dbesc($pageUrlName)
);
if($ic) {
foreach($ic as $c) {
if($ids)
if ($ic) {
foreach ($ic as $c) {
if ($ids)
$ids .= ',';
$ids .= intval($c['iid']);
}
}
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
$sql_extra = item_permissions_sql($channel_id, $observer_hash);
$sql_extra .= " order by revision desc ";
$r = null;
if($ids) {
if ($ids) {
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) and item_deleted = 0 $sql_extra",
dbesc($resource_id),
intval($channel_id)
);
if($r) {
if ($r) {
xchan_query($r);
$items = fetch_post_tags($r,true);
$items = fetch_post_tags($r, true);
return $items;
}
}
@@ -346,31 +357,30 @@ class NativeWikiPage {
static public function save_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$content = ((array_key_exists('content',$arr)) ? $arr['content'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : 0);
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
$content = ((array_key_exists('content', $arr)) ? $arr['content'] : '');
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return array('message' => t('Error reading wiki'), 'success' => false);
return ['message' => t('Error reading wiki'), 'success' => false];
}
// fetch the most recently saved revision.
// fetch the most recently saved revision.
$item = self::load_page($arr);
if(! $item) {
return array('message' => t('Page not found'), 'success' => false);
if (!$item) {
return ['message' => t('Page not found'), 'success' => false];
}
$mimetype = $item['mimetype'];
// change just the fields we need to change to create a revision;
// change just the fields we need to change to create a revision;
unset($item['id']);
unset($item['author']);
@@ -381,8 +391,8 @@ class NativeWikiPage {
$item['edited'] = datetime_convert();
$item['mimetype'] = $mimetype;
if($item['iconfig'] && is_array($item['iconfig']) && count($item['iconfig'])) {
for($x = 0; $x < count($item['iconfig']); $x ++) {
if ($item['iconfig'] && is_array($item['iconfig']) && count($item['iconfig'])) {
for ($x = 0; $x < count($item['iconfig']); $x++) {
unset($item['iconfig'][$x]['id']);
unset($item['iconfig'][$x]['iid']);
}
@@ -390,168 +400,164 @@ class NativeWikiPage {
$ret = item_store($item, false, false);
if($ret['item_id'])
return array('message' => '', 'item_id' => $ret['item_id'], 'filename' => $pageUrlName, 'success' => true);
if ($ret['item_id'])
return ['message' => '', 'item_id' => $ret['item_id'], 'filename' => $pageUrlName, 'success' => true];
else
return array('message' => t('Page update failed.'), 'success' => false);
}
return ['message' => t('Page update failed.'), 'success' => false];
}
static public function delete_page($arr) {
$pageUrlName = (array_key_exists('pageUrlName',$arr) ? $arr['pageUrlName'] : '');
$resource_id = (array_key_exists('resource_id',$arr) ? $arr['resource_id'] : '');
$observer_hash = (array_key_exists('observer_hash',$arr) ? $arr['observer_hash'] : '');
$channel_id = (array_key_exists('channel_id',$arr) ? $arr['channel_id'] : 0);
$pageUrlName = (array_key_exists('pageUrlName', $arr) ? $arr['pageUrlName'] : '');
$resource_id = (array_key_exists('resource_id', $arr) ? $arr['resource_id'] : '');
$observer_hash = (array_key_exists('observer_hash', $arr) ? $arr['observer_hash'] : '');
$channel_id = (array_key_exists('channel_id', $arr) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if(! $w['wiki']) {
return [ 'success' => false, 'message' => t('Error reading wiki') ];
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return ['success' => false, 'message' => t('Error reading wiki')];
}
$ids = [];
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
intval($channel_id),
dbesc($pageUrlName)
);
if($ic) {
foreach($ic as $c) {
if ($ic) {
foreach ($ic as $c) {
$ids[] = intval($c['iid']);
}
}
if($ids) {
if ($ids) {
drop_items($ids, true, DROPITEM_PHASE1);
return [ 'success' => true ];
return ['success' => true];
}
return [ 'success' => false, 'message' => t('Nothing deleted') ];
return ['success' => false, 'message' => t('Nothing deleted')];
}
static public function revert_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$commitHash = ((array_key_exists('commitHash',$arr)) ? $arr['commitHash'] : null);
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$commitHash = ((array_key_exists('commitHash', $arr)) ? $arr['commitHash'] : null);
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
if (! $commitHash) {
return array('message' => 'No commit was provided', 'success' => false);
if (!$commitHash) {
return ['message' => 'No commit was provided', 'success' => false];
}
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return array('message' => 'Error reading wiki', 'success' => false);
return ['message' => 'Error reading wiki', 'success' => false];
}
$x = $arr;
if(intval($commitHash) > 0) {
if (intval($commitHash) > 0) {
unset($x['commitHash']);
$x['revision'] = intval($commitHash) - 1;
$loaded = self::load_page($x);
$loaded = self::load_page($x);
if($loaded) {
if ($loaded) {
$content = $loaded['body'];
return [ 'content' => $content, 'success' => true ];
return ['content' => $content, 'success' => true];
}
return [ 'success' => false ];
return ['success' => false];
}
}
static public function compare_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$currentCommit = ((array_key_exists('currentCommit',$arr)) ? $arr['currentCommit'] : (-1));
$compareCommit = ((array_key_exists('compareCommit',$arr)) ? $arr['compareCommit'] : 0);
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$compareCommit = ((array_key_exists('compareCommit', $arr)) ? $arr['compareCommit'] : 0);
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return array('message' => t('Error reading wiki'), 'success' => false);
return ['message' => t('Error reading wiki'), 'success' => false];
}
$x = $arr;
$x = $arr;
$x['revision'] = (-1);
$currpage = self::load_page($x);
if($currpage)
if ($currpage)
$currentContent = $currpage['body'];
$x['revision'] = $compareCommit;
$comppage = self::load_page($x);
if($comppage)
$comppage = self::load_page($x);
if ($comppage)
$compareContent = $comppage['body'];
if($currpage && $comppage) {
if ($currpage && $comppage) {
require_once('library/class.Diff.php');
$diff = \Diff::toTable(\Diff::compare($currentContent, $compareContent));
return [ 'success' => true, 'diff' => $diff ];
return ['success' => true, 'diff' => $diff];
}
return [ 'success' => false, 'message' => t('Compare: object not found.') ];
return ['success' => false, 'message' => t('Compare: object not found.')];
}
static public function commit($arr) {
$commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated'));
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : t('Untitled'));
$commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated'));
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
if(array_key_exists('resource_id', $arr)) {
if (array_key_exists('resource_id', $arr)) {
$resource_id = $arr['resource_id'];
}
else {
return array('message' => t('Wiki resource_id required for git commit'), 'success' => false);
return ['message' => t('Wiki resource_id required for git commit'), 'success' => false];
}
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('message' => t('Error reading wiki'), 'success' => false);
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return ['message' => t('Error reading wiki'), 'success' => false];
}
$page = self::load_page($arr);
if($page) {
set_iconfig($page['id'],'nwikipage','commit_msg',escape_tags($commit_msg),true);
return [ 'success' => true, 'item_id' => $page['id'], 'page' => $page ];
if ($page) {
set_iconfig($page['id'], 'nwikipage', 'commit_msg', escape_tags($commit_msg), true);
return ['success' => true, 'item_id' => $page['id'], 'page' => $page];
}
return [ 'success' => false, 'message' => t('Page not found.') ];
return ['success' => false, 'message' => t('Page not found.')];
}
static public function convert_links($s, $wikiURL) {
if (strpos($s,'[[') !== false) {
if (strpos($s, '[[') !== false) {
preg_match_all("/\[\[(.*?)\]\]/", $s, $match);
$pages = $pageURLs = array();
$pages = $pageURLs = [];
foreach ($match[1] as $m) {
// TODO: Why do we need to double urlencode for this to work?
//$pageURLs[] = urlencode(urlencode(escape_tags($m)));
$titleUri = explode('|',$m);
$page = $titleUri[0] ?? '';
$title = $titleUri[1] ?? $page;
$pageURLs[] = Zlib\NativeWiki::name_encode(escape_tags($page));
$pages[] = $title;
$titleUri = explode('|', $m);
$page = $titleUri[0] ?? '';
$title = $titleUri[1] ?? $page;
$pageURLs[] = NativeWiki::name_encode(escape_tags($page));
$pages[] = $title;
}
$idx = 0;
while(strpos($s,'[[') !== false) {
$replace = '<a href="'.$wikiURL.'/'.$pageURLs[$idx].'">'.$pages[$idx].'</a>';
$s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1);
while (strpos($s, '[[') !== false) {
$replace = '<a href="' . $wikiURL . '/' . $pageURLs[$idx] . '">' . $pages[$idx] . '</a>';
$s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1);
$idx++;
}
}
@@ -564,21 +570,21 @@ class NativeWikiPage {
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$pageHistory = self::page_history([
'channel_id' => \App::$profile_uid,
'channel_id' => App::$profile_uid,
'observer_hash' => get_observer_hash(),
'resource_id' => $resource_id,
'pageUrlName' => $pageUrlName
]);
return replace_macros(get_markup_template('nwiki_page_history.tpl'), array(
return replace_macros(get_markup_template('nwiki_page_history.tpl'), [
'$pageHistory' => $pageHistory['history'],
'$permsWrite' => $arr['permsWrite'],
'$name_lbl' => t('Name'),
'$msg_label' => t('Message','wiki_history'),
'$msg_label' => t('Message', 'wiki_history'),
'$date_lbl' => t('Date'),
'$revert_btn' => t('Revert'),
'$compare_btn' => t('Compare')
));
]);
}
@@ -590,14 +596,14 @@ class NativeWikiPage {
* @return string
*/
static public function generate_toc($s) {
if (strpos($s,'[toc]') !== false) {
if (strpos($s, '[toc]') !== false) {
//$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render
$toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/
$s = preg_replace("/\[toc\]/", $toc_md, $s, -1);
$s = preg_replace("/\[toc\]/", $toc_md, $s, -1);
}
return $s;
}
/**
* Converts a select set of bbcode tags. Much of the code is copied from include/bbcode.php
@@ -605,27 +611,27 @@ class NativeWikiPage {
* @return string
*/
static public function bbcode($s) {
$s = str_replace(array('[baseurl]', '[sitename]'), array(z_root(), get_config('system', 'sitename')), $s);
$s = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $s);
$s = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $s);
$s = str_replace(['[baseurl]', '[sitename]'], [z_root(), get_config('system', 'sitename')], $s);
$s = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_callback', $s);
$s = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_necallback', $s);
$observer = \App::get_observer();
$observer = App::get_observer();
if ($observer) {
$s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">';
$s2 = '</span>';
$s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">';
$s2 = '</span>';
$obsBaseURL = $observer['xchan_connurl'];
$obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL);
$s = str_replace('[observer.baseurl]', $obsBaseURL, $s);
$s = str_replace('[observer.url]', $observer['xchan_url'], $s);
$s = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $s);
$s = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $s);
$s = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $s);
$s = str_replace('[observer.photo]', '', $s);
}
$s = str_replace('[observer.baseurl]', $obsBaseURL, $s);
$s = str_replace('[observer.url]', $observer['xchan_url'], $s);
$s = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $s);
$s = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $s);
$s = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $s);
$s = str_replace('[observer.photo]', '', $s);
}
else {
$s = str_replace('[observer.baseurl]', '', $s);
$s = str_replace('[observer.url]', '', $s);
@@ -637,62 +643,63 @@ class NativeWikiPage {
return $s;
}
static public function get_file_ext($arr) {
if($arr['mimetype'] === 'text/bbcode')
if ($arr['mimetype'] === 'text/bbcode')
return '.bb';
elseif($arr['mimetype'] === 'text/markdown')
elseif ($arr['mimetype'] === 'text/markdown')
return '.md';
elseif($arr['mimetype'] === 'text/plain')
elseif ($arr['mimetype'] === 'text/plain')
return '.txt';
}
// This function is derived from
// This function is derived from
// http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php
static public function toc($content) {
// ensure using only "\n" as line-break
$source = str_replace(["\r\n", "\r"], "\n", $content);
// look for markdown TOC items
preg_match_all(
'/^(?:=|-|#).*$/m',
$source,
$matches,
PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE
);
// preprocess: iterate matched lines to create an array of items
// where each item is an array(level, text)
$file_size = strlen($source);
foreach ($matches[0] as $item) {
$found_mark = substr($item[0], 0, 1);
if ($found_mark == '#') {
// text is the found item
$item_text = $item[0];
$item_level = strrpos($item_text, '#') + 1;
$item_text = substr($item_text, $item_level);
} else {
// text is the previous line (empty if <hr>)
$item_offset = $item[1];
$prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2));
$item_text =
substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1);
$item_text = trim($item_text);
$item_level = $found_mark == '=' ? 1 : 2;
// ensure using only "\n" as line-break
$source = str_replace(["\r\n", "\r"], "\n", $content);
// look for markdown TOC items
preg_match_all(
'/^(?:=|-|#).*$/m',
$source,
$matches,
PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE
);
// preprocess: iterate matched lines to create an array of items
// where each item is an array(level, text)
$file_size = strlen($source);
foreach ($matches[0] as $item) {
$found_mark = substr($item[0], 0, 1);
if ($found_mark == '#') {
// text is the found item
$item_text = $item[0];
$item_level = strrpos($item_text, '#') + 1;
$item_text = substr($item_text, $item_level);
}
else {
// text is the previous line (empty if <hr>)
$item_offset = $item[1];
$prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2));
$item_text =
substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1);
$item_text = trim($item_text);
$item_level = $found_mark == '=' ? 1 : 2;
}
if (!trim($item_text) or strpos($item_text, '|') !== FALSE) {
// item is an horizontal separator or a table header, don't mind
continue;
}
$raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)];
}
if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) {
// item is an horizontal separator or a table header, don't mind
continue;
}
$raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)];
}
$o = '';
foreach($raw_toc as $t) {
foreach ($raw_toc as $t) {
$level = intval($t['level']);
$text = $t['text'];
$text = $t['text'];
switch ($level) {
case 1:
$li = '* ';
@@ -712,7 +719,7 @@ class NativeWikiPage {
}
$o .= $li . $text . "\n";
}
return $o;
return $o;
}
}

View File

@@ -2,6 +2,8 @@
namespace Zotlabs\Lib;
use App;
/**
* @brief Class for handling channel specific configurations.
*
@@ -32,15 +34,15 @@ class PConfig {
if(is_null($uid) || $uid === false)
return false;
if(! is_array(\App::$config)) {
if(! is_array(App::$config)) {
btlogger('App::$config not an array');
}
if(! array_key_exists($uid, \App::$config)) {
\App::$config[$uid] = array();
if(! array_key_exists($uid, App::$config)) {
App::$config[$uid] = array();
}
if(! is_array(\App::$config[$uid])) {
if(! is_array(App::$config[$uid])) {
btlogger('App::$config[$uid] not an array: ' . $uid);
}
@@ -52,12 +54,12 @@ class PConfig {
foreach($r as $rr) {
$k = $rr['k'];
$c = $rr['cat'];
if(! array_key_exists($c, \App::$config[$uid])) {
\App::$config[$uid][$c] = array();
\App::$config[$uid][$c]['config_loaded'] = true;
if(! array_key_exists($c, App::$config[$uid])) {
App::$config[$uid][$c] = array();
App::$config[$uid][$c]['config_loaded'] = true;
}
\App::$config[$uid][$c][$k] = $rr['v'];
\App::$config[$uid][$c]['pcfgud:'.$k] = $rr['updated'];
App::$config[$uid][$c][$k] = $rr['v'];
App::$config[$uid][$c]['pcfgud:'.$k] = $rr['updated'];
}
}
}
@@ -86,15 +88,15 @@ class PConfig {
if(is_null($uid) || $uid === false)
return $default;
if(! array_key_exists($uid, \App::$config))
if(! array_key_exists($uid, App::$config))
self::Load($uid);
if((! array_key_exists($family, \App::$config[$uid])) || (! array_key_exists($key, \App::$config[$uid][$family])))
if((! array_key_exists($family, App::$config[$uid])) || (! array_key_exists($key, App::$config[$uid][$family])))
return $default;
return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key]))
? unserialize(\App::$config[$uid][$family][$key])
: \App::$config[$uid][$family][$key]
return ((! is_array(App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', App::$config[$uid][$family][$key]))
? unserialize(App::$config[$uid][$family][$key])
: App::$config[$uid][$family][$key]
);
}
@@ -133,6 +135,7 @@ class PConfig {
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
$new = false;
$update = false;
$now = datetime_convert();
if (! $updated) {
@@ -143,23 +146,22 @@ class PConfig {
$updated = datetime_convert('UTC','UTC','-2 seconds');
}
$hash = hash('sha256',$family.':'.$key);
$hash = gen_link_id($family.':'.$key);
if (self::Get($uid, 'hz_delpconfig', $hash) !== false) {
if (self::Get($uid, 'hz_delpconfig', $hash) > $now) {
logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR);
return self::Get($uid,$family,$key);
} else {
self::Delete($uid,'hz_delpconfig',$hash);
self::Delete($uid, 'hz_delpconfig', $hash);
}
}
if(self::Get($uid, $family, $key) === false) {
if(! array_key_exists($uid, \App::$config))
\App::$config[$uid] = array();
if(! array_key_exists($family, \App::$config[$uid]))
\App::$config[$uid][$family] = array();
if(! array_key_exists($uid, App::$config))
App::$config[$uid] = array();
if(! array_key_exists($family, App::$config[$uid]))
App::$config[$uid][$family] = array();
$ret = q("INSERT INTO pconfig ( uid, cat, k, v, updated ) VALUES ( %d, '%s', '%s', '%s', '%s' ) ",
intval($uid),
@@ -177,13 +179,14 @@ class PConfig {
logger("Error: Insert to pconfig failed.",LOGGER_NORMAL, LOG_ERR);
}
\App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
$new = true;
App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
}
else {
$new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now);
$update = (App::$config[$uid][$family]['pcfgud:'.$key] < $now);
if ($new) {
if ($update) {
// @NOTE There is still a possible race condition under limited circumstances
// where a value will be updated by another thread with more current data than
@@ -198,7 +201,7 @@ class PConfig {
dbesc($key)
);
\App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
} else {
logger('Refusing to update pconfig with outdated info.', LOGGER_NORMAL, LOG_ERR);
@@ -211,16 +214,16 @@ class PConfig {
// set in the life of this page. We need this to
// synchronise channel clones.
if(! array_key_exists('transient', \App::$config[$uid]))
\App::$config[$uid]['transient'] = array();
if(! array_key_exists($family, \App::$config[$uid]['transient']))
\App::$config[$uid]['transient'][$family] = array();
if(! array_key_exists('transient', App::$config[$uid]))
App::$config[$uid]['transient'] = array();
if(! array_key_exists($family, App::$config[$uid]['transient']))
App::$config[$uid]['transient'][$family] = array();
\App::$config[$uid][$family][$key] = $value;
App::$config[$uid][$family][$key] = $value;
if ($new) {
\App::$config[$uid]['transient'][$family][$key] = $value;
\App::$config[$uid]['transient'][$family]['pcfgud:'.$key] = $updated;
if ($new || $update) {
App::$config[$uid]['transient'][$family][$key] = $value;
App::$config[$uid]['transient'][$family]['pcfgud:'.$key] = $updated;
}
if($ret)
@@ -253,7 +256,7 @@ class PConfig {
$updated = ($updated) ? $updated : datetime_convert('UTC','UTC','-2 seconds');
$now = datetime_convert();
$newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now);
$newer = (App::$config[$uid][$family]['pcfgud:'.$key] < $now);
if (! $newer) {
logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR);
@@ -262,12 +265,12 @@ class PConfig {
$ret = false;
if (isset(\App::$config[$uid][$family][$key])) {
unset(\App::$config[$uid][$family][$key]);
if (isset(App::$config[$uid][$family][$key])) {
unset(App::$config[$uid][$family][$key]);
}
if (isset(\App::$config[$uid][$family]['pcfgud:'.$key])) {
unset(\App::$config[$uid][$family]['pcfgud:'.$key]);
if (isset(App::$config[$uid][$family]['pcfgud:'.$key])) {
unset(App::$config[$uid][$family]['pcfgud:'.$key]);
}
$ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'",
@@ -278,9 +281,9 @@ class PConfig {
// Synchronize delete with clones.
if ($family != 'hz_delpconfig') {
$hash = hash('sha256',$family.':'.$key);
set_pconfig($uid,'hz_delpconfig',$hash,$updated);
if ($family !== 'hz_delpconfig') {
$hash = gen_link_id($family.':'.$key);
set_pconfig($uid, 'hz_delpconfig', $hash, $updated);
}
return $ret;

View File

@@ -4,6 +4,7 @@ namespace Zotlabs\Lib;
use Zotlabs\Access\PermissionRoles;
use Zotlabs\Access\Permissions;
use Zotlabs\Daemon\Master;
/**
* @brief Permission Categories. Permission rules for various classes of connections.
@@ -38,33 +39,33 @@ class Permcat {
// first check role perms for a perms_connect setting
$role = get_pconfig($channel_id,'system','permissions_role');
if($role) {
$role = get_pconfig($channel_id, 'system', 'permissions_role');
if ($role) {
$x = PermissionRoles::role_perms($role);
if($x['perms_connect']) {
if ($x['perms_connect']) {
$perms = Permissions::FilledPerms($x['perms_connect']);
}
}
// if no role perms it may be a custom role, see if there any autoperms
if(! $perms) {
if (!$perms) {
$perms = Permissions::FilledAutoPerms($channel_id);
}
// if no autoperms it may be a custom role with manual perms
if(! $perms) {
if (!$perms) {
$r = q("select channel_hash from channel where channel_id = %d",
intval($channel_id)
);
if($r) {
if ($r) {
$x = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'my_perms'",
intval($channel_id),
dbesc($r[0]['channel_hash'])
);
if($x) {
foreach($x as $xv) {
if ($x) {
foreach ($x as $xv) {
$perms[$xv['k']] = intval($xv['v']);
}
}
@@ -73,25 +74,27 @@ class Permcat {
// nothing was found - create a filled permission array where all permissions are 0
if(! $perms) {
if (!$perms) {
$perms = Permissions::FilledPerms([]);
}
$this->permcats[] = [
'name' => 'default',
'localname' => t('default','permcat'),
'localname' => t('Default', 'permcat'),
'perms' => Permissions::Operms($perms),
'raw_perms' => $perms,
'system' => 1
];
$p = $this->load_permcats($channel_id);
if($p) {
for($x = 0; $x < count($p); $x++) {
if ($p) {
for ($x = 0; $x < count($p); $x++) {
$this->permcats[] = [
'name' => $p[$x][0],
'localname' => $p[$x][1],
'perms' => Permissions::Operms(Permissions::FilledPerms($p[$x][2])),
'raw_perms' => Permissions::FilledPerms($p[$x][2]),
'system' => intval($p[$x][3])
];
}
@@ -116,9 +119,9 @@ class Permcat {
* * \e bool \b error if $name not found in permcats true
*/
public function fetch($name) {
if($name && $this->permcats) {
foreach($this->permcats as $permcat) {
if(strcasecmp($permcat['name'], $name) === 0) {
if ($name && $this->permcats) {
foreach ($this->permcats as $permcat) {
if (strcasecmp($permcat['name'], $name) === 0) {
return $permcat;
}
}
@@ -128,31 +131,28 @@ class Permcat {
}
public function load_permcats($uid) {
/*
$permcats = [
[ 'follower', t('follower','permcat'),
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
'post_like' ], 1
],
[ 'contributor', t('contributor','permcat'),
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
'post_wall','post_comments','write_wiki','post_like','tag_deliver','chat' ], 1
],
[ 'publisher', t('publisher','permcat'),
[ 'contributor', t('Contributor','permcat'),
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages',
'write_storage','post_wall','write_pages','write_wiki','post_comments','post_like','tag_deliver',
'chat', 'republish' ], 1
]
'write_storage','post_wall','write_pages','write_wiki','post_comments', 'post_mail', 'post_like',
'chat' ], 1
],
[ 'muted', t('Muted','permcat'),
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
'post_comments','write_wiki','post_like' ], 1
],
];
if($uid) {
*/
if ($uid) {
$x = q("select * from pconfig where uid = %d and cat = 'permcat'",
intval($uid)
);
if($x) {
foreach($x as $xv) {
$value = ((preg_match('|^a:[0-9]+:{.*}$|s', $xv['v'])) ? unserialize($xv['v']) : $xv['v']);
$permcats[] = [ $xv['k'], $xv['k'], $value, 0 ];
if ($x) {
foreach ($x as $xv) {
$value = ((preg_match('|^a:[0-9]+:{.*}$|s', $xv['v'])) ? unserialize($xv['v']) : $xv['v']);
$permcats[] = [$xv['k'], $xv['k'], $value, 0];
}
}
}
@@ -167,11 +167,11 @@ class Permcat {
}
static public function find_permcat($arr, $name) {
if((! $arr) || (! $name))
if ((!$arr) || (!$name))
return false;
foreach($arr as $p)
if($p['name'] == $name)
foreach ($arr as $p)
if ($p['name'] == $name)
return $p['value'];
}
@@ -183,4 +183,105 @@ class Permcat {
PConfig::Delete($channel_id, 'permcat', $name);
}
}
/**
* @brief assign a contact role to contacts
*
* @param array $channel
* @param string $role the name of the role
* @param array $contacts an array of contact hashes
*/
public static function assign($channel, $role, $contacts) {
if (!isset($channel['channel_id'])) {
return;
}
if (!is_array($contacts) || empty($contacts)) {
return;
}
if (!$role) {
// lookup the default
$role = get_pconfig($channel['channel_id'], 'system', 'default_permcat', 'default');
}
// Doublecheck that we do not assign a role to ourself.
// It does not make a difference but could be confusing.
if (in_array($channel['channel_hash'], $contacts)) {
$contacts = array_diff($contacts, [$channel['channel_hash']]);
}
$all_perms = Permissions::Perms();
$permcats = new Permcat($channel['channel_id']);
$role_perms = $permcats->fetch($role);
if (isset($role_perms['error'])) {
return false;
}
$perms = $role_perms['raw_perms'];
$values_sql = '';
stringify_array_elms($contacts, true);
if ($all_perms && $perms) {
foreach ($contacts as $contact) {
foreach ($all_perms as $perm => $desc) {
if (array_key_exists($perm, $perms)) {
$values_sql .= " (" . intval($channel['channel_id']) . ", " . protect_sprintf($contact) . ", 'my_perms', '" . dbesc($perm) . "', " . intval($perms[$perm]) . "),";
}
else {
$values_sql .= " (" . intval($channel['channel_id']) . ", " . protect_sprintf($contact) . ", 'my_perms', '" . dbesc($perm) . "', 0), ";
}
}
}
}
$values_sql = rtrim($values_sql, ',');
dbq("DELETE FROM abconfig WHERE chan = " . intval($channel['channel_id']) . " AND cat = 'my_perms' AND xchan IN (" . protect_sprintf(implode(',', $contacts)) . ")");
dbq("INSERT INTO abconfig ( chan, xchan, cat, k, v ) VALUES $values_sql");
q("UPDATE abook SET abook_role = '%s'
WHERE abook_xchan IN (" . protect_sprintf(implode(',', $contacts)) . ") AND abook_channel = %d",
dbesc($role),
intval($channel['channel_id'])
);
$r = q("SELECT abook.*, xchan.* FROM abook LEFT JOIN xchan ON abook.abook_xchan = xchan.xchan_hash WHERE abook.abook_xchan IN (" . protect_sprintf(implode(',', $contacts)) . ") AND abook.abook_channel = %d AND abook_self = 0",
intval($channel['channel_id'])
);
foreach ($r as $rr) {
if (intval($rr['abook_self'])) {
continue;
}
Master::Summon([
'Notifier',
'permission_update',
$rr['abook_id']
]);
$clone = $rr;
unset($clone['abook_id']);
unset($clone['abook_account']);
unset($clone['abook_channel']);
$abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']);
if ($abconfig)
$clone['abconfig'] = $abconfig;
Libsync::build_sync_packet(0 /* use the current local_channel */, ['abook' => [$clone]]);
}
return true;
}
}

View File

@@ -195,7 +195,7 @@ class Queue {
$channel = null;
if($outq['outq_channel']) {
$channel = channelx_by_n($outq['outq_channel']);
$channel = channelx_by_n($outq['outq_channel'], true);
}
$host_crypto = null;

View File

@@ -2,7 +2,9 @@
namespace Zotlabs\Lib;
use App;
use Zotlabs\Lib\Apps;
use Zotlabs\Access\AccessList;
require_once('include/text.php');
@@ -58,6 +60,9 @@ class ThreadItem {
$child = new ThreadItem($item);
$this->add_child($child);
}
// performance: we have already added the children
unset($this->data['children']);
}
// allow a site to configure the order and content of the reaction emoji list
@@ -98,11 +103,25 @@ class ThreadItem {
$conv = $this->get_conversation();
$observer = $conv->get_observer();
$lock = (((intval($item['item_private'])) || (($item['uid'] == local_channel()) && (strlen($item['allow_cid']) || strlen($item['allow_gid'])
|| strlen($item['deny_cid']) || strlen($item['deny_gid']))))
? t('Private Message')
$acl = new AccessList(false);
$acl->set($item);
$lock = ((intval($item['item_private']) || ($item['uid'] == local_channel() && $acl->is_private()))
? t('Restricted message')
: false);
$locktype = $item['item_private'];
// 1 = restricted message, 2 = direct message
$locktype = intval($item['item_private']);
if ($locktype === 2) {
$lock = t('Direct message');
}
// 0 = limited based on public policy
if ($item['uid'] == local_channel() && intval($item['item_private']) && !$acl->is_private() && strlen($item['public_policy'])) {
$lock = t('Public Policy');
$locktype = 0;
}
$shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false);
@@ -110,6 +129,16 @@ class ThreadItem {
if($item['author']['xchan_network'] === 'rss')
$shareable = true;
// @fixme
// Have recently added code to properly handle polls in group reshares by redirecting all of the poll responses to the group.
// Sharing a poll using a regular embedded share is harder because the poll will need to fork. This is due to comment permissions.
// The original poll author may not accept responses from strangers. Forking the poll will receive responses from the sharer's
// followers, but there's no elegant way to merge these two sets of results together. For now, we'll disable sharing polls.
if ($item['obj_type'] === 'Question') {
$shareable = false;
}
$privacy_warning = false;
if(intval($item['item_private']) && ($item['owner']['xchan_network'] === 'activitypub')) {
$recips = get_iconfig($item['parent'], 'activitypub', 'recips');
@@ -384,6 +413,12 @@ class ThreadItem {
$pinned_items = ($allowed_type ? get_pconfig($item['uid'], 'pinned', $item['item_type'], []) : []);
$pinned = ((!empty($pinned_items) && in_array($midb64, $pinned_items)) ? true : false);
$contact = [];
if(App::$contacts && array_key_exists($item['author_xchan'], App::$contacts)) {
$contact = App::$contacts[$item['author_xchan']];
}
$tmp_item = array(
'template' => $this->get_template(),
'mode' => $mode,
@@ -401,6 +436,7 @@ class ThreadItem {
'mids' => $json_mids,
'parent' => $item['parent'],
'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
'author_is_group_actor' => (($item['author']['xchan_pubforum']) ? t('Forum') : ''),
'isevent' => $isevent,
'attend' => $attend,
'consensus' => $consensus,
@@ -503,7 +539,9 @@ class ThreadItem {
'wait' => t('Please wait'),
'thread_level' => $thread_level,
'settings' => $settings,
'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? gen_link_id($item['thr_parent']) : '')
'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? gen_link_id($item['thr_parent']) : ''),
'contact_id' => (($contact) ? $contact['abook_id'] : '')
);
$arr = array('item' => $item, 'output' => $tmp_item);

View File

@@ -87,4 +87,4 @@ class ZotURL {
return ids_to_array($r,'hubloc_url');
}
}
}

View File

@@ -3,9 +3,9 @@
namespace Zotlabs\Module;
use Zotlabs\Lib\Libzotdir;
use Zotlabs\Lib\AccessList;
require_once 'include/acl_selectors.php';
require_once 'include/group.php';
/**
* @brief ACL selector json backend.
@@ -123,7 +123,7 @@ class Acl extends \Zotlabs\Web\Controller {
"name" => t('Profile','acl') . ' ' . $rv['profile_name'],
"id" => 'vp' . $rv['id'],
"xid" => 'vp.' . $rv['profile_guid'],
"uids" => group_get_profile_members_xchan(local_channel(), $rv['id']),
"uids" => AccessList::profile_members_xchan(local_channel(), $rv['id']),
"link" => ''
);
}
@@ -146,14 +146,14 @@ class Acl extends \Zotlabs\Web\Controller {
if($r) {
foreach($r as $g){
// logger('acl: group: ' . $g['gname'] . ' members: ' . group_get_members_xchan($g['id']));
// logger('acl: group: ' . $g['gname'] . ' members: ' . AccessList::members_xchan(local_channel(), $g['id']));
$groups[] = array(
"type" => "g",
"photo" => "images/twopeople.png",
"name" => $g['gname'],
"id" => $g['id'],
"xid" => $g['hash'],
"uids" => group_get_members_xchan($g['id']),
"uids" => AccessList::members_xchan(local_channel(), $g['id']),
"link" => ''
);
}

View File

@@ -339,12 +339,15 @@ class Site {
// now invert the logic for the setting.
$discover_tab = (1 - $discover_tab);
$perm_roles = \Zotlabs\Access\PermissionRoles::roles();
$default_role = get_config('system','default_permissions_role','social');
$perm_roles = \Zotlabs\Access\PermissionRoles::channel_roles();
$default_role = get_config('system', 'default_permissions_role', 'personal');
if (!in_array($default_role, array_keys($perm_roles))) {
$default_role = 'personal';
}
$role = array('permissions_role' , t('Default permission role for new accounts'), $default_role, t('This role will be used for the first channel created after registration.'),$perm_roles);
$homelogin = get_config('system','login_on_homepage');
$enable_context_help = get_config('system','enable_context_help');

103
Zotlabs/Module/Album.php Normal file
View File

@@ -0,0 +1,103 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\Config;
use Zotlabs\Web\HTTPSig;
require_once('include/security.php');
require_once('include/attach.php');
require_once('include/photo/photo_driver.php');
require_once('include/photos.php');
class Album extends Controller {
function init() {
if (ActivityStreams::is_as_request()) {
$sigdata = HTTPSig::verify(EMPTY_STR);
if ($sigdata['portable_id'] && $sigdata['header_valid']) {
$portable_id = $sigdata['portable_id'];
if (!check_channelallowed($portable_id)) {
http_status_exit(403, 'Permission denied');
}
if (!check_siteallowed($sigdata['signer'])) {
http_status_exit(403, 'Permission denied');
}
observer_auth($portable_id);
}
elseif (Config::get('system', 'require_authenticated_fetch', false)) {
http_status_exit(403, 'Permission denied');
}
$observer_xchan = get_observer_hash();
$allowed = false;
$bear = Activity::token_from_request();
if ($bear) {
logger('bear: ' . $bear, LOGGER_DEBUG);
}
$channel = null;
if (argc() > 1) {
$channel = channelx_by_nick(argv(1));
}
if (!$channel) {
http_status_exit(404, 'Not found.');
}
$sql_extra = permissions_sql($channel['channel_id'], $observer_xchan);
if (argc() > 2) {
$folder = argv(2);
$r = q("select * from attach where is_dir = 1 and hash = '%s' and uid = %d $sql_extra limit 1",
dbesc($folder),
intval($channel['channel_id'])
);
$allowed = (($r) ? attach_can_view($channel['channel_id'], $observer_xchan, $r[0]['hash'] /*,$bear */) : false);
}
else {
$folder = EMPTY_STR;
$allowed = perm_is_allowed($channel['channel_id'], $observer_xchan, 'view_storage');
}
if (!$allowed) {
http_status_exit(403, 'Permission denied.');
}
$x = q("select * from attach where folder = '%s' and uid = %d $sql_extra",
dbesc($folder),
intval($channel['channel_id'])
);
$contents = [];
if ($x) {
foreach ($x as $xv) {
if (intval($xv['is_dir'])) {
continue;
}
if (!attach_can_view($channel['channel_id'], $observer_xchan, $xv['hash'] /*,$bear*/)) {
continue;
}
if (intval($xv['is_photo'])) {
$contents[] = z_root() . '/photo/' . $xv['hash'];
}
}
}
$obj = Activity::encode_simple_collection($contents, App::$query_string, 'OrderedCollection', count($contents));
as_return_and_die($obj, $channel);
}
}
}

View File

@@ -2,9 +2,9 @@
namespace Zotlabs\Module;
//require_once('include/apps.php');
use \Zotlabs\Lib as Zlib;
use App;
use Zotlabs\Lib\Apps;
use Zotlabs\Lib\Libsync;
class Appman extends \Zotlabs\Web\Controller {
@@ -33,9 +33,9 @@ class Appman extends \Zotlabs\Web\Controller {
'categories' => escape_tags($_REQUEST['categories'])
);
$_REQUEST['appid'] = Zlib\Apps::app_install(local_channel(),$arr);
$_REQUEST['appid'] = Apps::app_install(local_channel(),$arr);
if(Zlib\Apps::app_installed(local_channel(),$arr))
if(Apps::app_installed(local_channel(),$arr))
info( t('App installed.') . EOL);
goaway(z_root() . '/apps');
@@ -43,7 +43,7 @@ class Appman extends \Zotlabs\Web\Controller {
}
$papp = Zlib\Apps::app_decode($_POST['papp']);
$papp = Apps::app_decode($_POST['papp']);
if(! is_array($papp)) {
notice( t('Malformed app.') . EOL);
@@ -51,13 +51,51 @@ class Appman extends \Zotlabs\Web\Controller {
}
if($_POST['install']) {
Zlib\Apps::app_install(local_channel(),$papp);
if(Zlib\Apps::app_installed(local_channel(),$papp))
Apps::app_install(local_channel(),$papp);
if(Apps::app_installed(local_channel(),$papp))
info( t('App installed.') . EOL);
$sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1",
intval(local_channel()),
dbesc($papp['guid'])
);
if (!$sync) {
return;
}
if (intval($sync[0]['app_system'])) {
Libsync::build_sync_packet($uid, ['sysapp' => $sync]);
}
else {
Libsync::build_sync_packet($uid, ['app' => $sync]);
}
}
if($_POST['delete']) {
Zlib\Apps::app_destroy(local_channel(),$papp);
// Fetch the app for sync before it is deleted (if it is deletable))
$sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1",
intval(local_channel()),
dbesc($papp['guid'])
);
if (!$sync) {
return;
}
Apps::app_destroy(local_channel(), $papp);
// Now flag it deleted
$sync[0]['app_deleted'] = 1;
if (intval($sync[0]['app_system'])) {
Libsync::build_sync_packet($uid, ['sysapp' => $sync]);
}
else {
Libsync::build_sync_packet($uid, ['app' => $sync]);
}
}
if($_POST['edit']) {
@@ -65,11 +103,35 @@ class Appman extends \Zotlabs\Web\Controller {
}
if($_POST['feature']) {
Zlib\Apps::app_feature(local_channel(), $papp, $_POST['feature']);
Apps::app_feature(local_channel(), $papp, $_POST['feature']);
$sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1",
intval(local_channel()),
dbesc($papp['guid'])
);
if (intval($sync[0]['app_system'])) {
Libsync::build_sync_packet($uid, ['sysapp' => $sync]);
}
else {
Libsync::build_sync_packet($uid, ['app' => $sync]);
}
}
if($_POST['pin']) {
Zlib\Apps::app_feature(local_channel(), $papp, $_POST['pin']);
Apps::app_feature(local_channel(), $papp, $_POST['pin']);
$sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1",
intval(local_channel()),
dbesc($papp['guid'])
);
if (intval($sync[0]['app_system'])) {
Libsync::build_sync_packet($uid, ['sysapp' => $sync]);
}
else {
Libsync::build_sync_packet($uid, ['app' => $sync]);
}
}
if($_POST['aj']) {
@@ -92,14 +154,14 @@ class Appman extends \Zotlabs\Web\Controller {
return;
}
$channel = \App::get_channel();
$channel = App::get_channel();
if(argc() > 3) {
if(argv(2) === 'moveup') {
Zlib\Apps::moveup(local_channel(),argv(1),argv(3));
Apps::moveup(local_channel(),argv(1),argv(3));
}
if(argv(2) === 'movedown') {
Zlib\Apps::movedown(local_channel(),argv(1),argv(3));
Apps::movedown(local_channel(),argv(1),argv(3));
}
goaway(z_root() . '/apporder');
}
@@ -133,7 +195,7 @@ class Appman extends \Zotlabs\Web\Controller {
}
}
$embed = array('embed', t('Embed code'), Zlib\Apps::app_encode($app,true),'', 'onclick="this.select();"');
$embed = array('embed', t('Embed code'), Apps::app_encode($app,true),'', 'onclick="this.select();"');
}

View File

@@ -14,7 +14,7 @@ class Apschema extends \Zotlabs\Web\Controller {
'zot' => z_root() . '/apschema#',
'id' => '@id',
'type' => '@type',
'commentPolicy' => 'as:commentPolicy',
'commentPolicy' => 'zot:commentPolicy',
'meData' => 'zot:meData',
'meDataType' => 'zot:meDataType',
'meEncoding' => 'zot:meEncoding',
@@ -33,6 +33,9 @@ class Apschema extends \Zotlabs\Web\Controller {
'PropertyValue' => 'schema:PropertyValue',
'value' => 'schema:value',
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
'magicEnv' => [
'@id' => 'zot:magicEnv',
'@type' => '@id'
@@ -50,7 +53,7 @@ class Apschema extends \Zotlabs\Web\Controller {
'guid' => 'diaspora:guid',
'Hashtag' => 'as:Hashtag'
]
];

View File

@@ -53,14 +53,7 @@ class Channel extends Controller {
$profile = argv(1);
}
// Do not use channelx_by_nick() here since it will dismiss deleted channels.
// We need to provide zotinfo for deleted channels so that directories can pick up the info.
$r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' LIMIT 1",
dbesc($which)
);
$channel = $r[0];
$channel = channelx_by_nick($which, true);
if (!$channel) {
http_status_exit(404, 'Not found');
@@ -73,8 +66,7 @@ class Channel extends Controller {
$sigdata = HTTPSig::verify(file_get_contents('php://input'), EMPTY_STR, 'zot6');
if ($sigdata && $sigdata['signer'] && $sigdata['header_valid']) {
$data = json_encode(Libzot::zotinfo(['address' => $channel['channel_address'], 'target_url' => $sigdata['signer']]));
$data = json_encode(Libzot::zotinfo(['guid_hash' => $channel['channel_hash'], 'target_url' => $sigdata['signer']]));
$s = q("select site_crypto, hubloc_sitekey from site left join hubloc on hubloc_url = site_url where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($sigdata['signer'])
);
@@ -100,7 +92,11 @@ class Channel extends Controller {
}
if ($channel['channel_removed']) {
http_status_exit(404, 'Not found');
http_status_exit(410, 'Gone');
}
if (get_pconfig($channel['channel_id'], 'system', 'index_opt_out')) {
App::$meta->set('robots', 'noindex, noarchive');
}
if (ActivityStreams::is_as_request($channel)) {

View File

@@ -2,32 +2,32 @@
namespace Zotlabs\Module;
use App;
use Zotlabs\Lib\Permcat;
require_once('include/socgraph.php');
require_once('include/selectors.php');
require_once('include/group.php');
class Connections extends \Zotlabs\Web\Controller {
function init() {
if(! local_channel())
return;
App::$profile_uid = local_channel();
$channel = App::get_channel();
if($channel)
head_set_icon($channel['xchan_photo_s']);
}
function get() {
$sort_type = 0;
$o = '';
if(! local_channel()) {
notice( t('Permission denied.') . EOL);
return login();
@@ -44,13 +44,13 @@ class Connections extends \Zotlabs\Web\Controller {
$pending = false;
$unconnected = false;
$all = false;
if(! $_REQUEST['aj'])
$_SESSION['return_url'] = App::$query_string;
$search_flags = "";
$head = '';
if(argc() == 2) {
switch(argv(1)) {
case 'active':
@@ -106,7 +106,7 @@ class Connections extends \Zotlabs\Web\Controller {
// $head = t('Unconnected');
// $unconnected = true;
// break;
case 'all':
$head = t('All');
break;
@@ -115,19 +115,19 @@ class Connections extends \Zotlabs\Web\Controller {
$active = true;
$head = t('Active');
break;
}
$sql_extra = $search_flags;
if(argv(1) === 'pending')
$sql_extra .= " and abook_ignored = 0 ";
}
else {
$sql_extra = " and abook_blocked = 0 ";
$unblocked = true;
}
switch($_REQUEST['order']) {
case 'name_desc':
$sql_order = 'xchan_name DESC';
@@ -143,32 +143,32 @@ class Connections extends \Zotlabs\Web\Controller {
}
$search = ((x($_REQUEST,'search')) ? notags(trim($_REQUEST['search'])) : '');
$tabs = array(
/*
array(
'label' => t('Suggestions'),
'url' => z_root() . '/suggest',
'url' => z_root() . '/suggest',
'sel' => '',
'title' => t('Suggest new connections'),
),
*/
'active' => array(
'label' => t('Active Connections'),
'url' => z_root() . '/connections/active',
'url' => z_root() . '/connections/active',
'sel' => ($active) ? 'active' : '',
'title' => t('Show active connections'),
),
'pending' => array(
'label' => t('New Connections'),
'url' => z_root() . '/connections/pending',
'url' => z_root() . '/connections/pending',
'sel' => ($pending) ? 'active' : '',
'title' => t('Show pending (new) connections'),
),
/*
array(
'label' => t('Unblocked'),
@@ -177,55 +177,55 @@ class Connections extends \Zotlabs\Web\Controller {
'title' => t('Only show unblocked connections'),
),
*/
'blocked' => array(
'label' => t('Blocked'),
'url' => z_root() . '/connections/blocked',
'sel' => ($blocked) ? 'active' : '',
'title' => t('Only show blocked connections'),
),
'ignored' => array(
'label' => t('Ignored'),
'url' => z_root() . '/connections/ignored',
'sel' => ($ignored) ? 'active' : '',
'title' => t('Only show ignored connections'),
),
'archived' => array(
'label' => t('Archived/Unreachable'),
'url' => z_root() . '/connections/archived',
'sel' => ($archived) ? 'active' : '',
'title' => t('Only show archived/unreachable connections'),
),
'hidden' => array(
'label' => t('Hidden'),
'url' => z_root() . '/connections/hidden',
'sel' => ($hidden) ? 'active' : '',
'title' => t('Only show hidden connections'),
),
// array(
// 'label' => t('Unconnected'),
// 'url' => z_root() . '/connections/unconnected',
// 'sel' => ($unconnected) ? 'active' : '',
// 'title' => t('Only show one-way connections'),
// ),
'all' => array(
'label' => t('All Connections'),
'url' => z_root() . '/connections',
'url' => z_root() . '/connections',
'sel' => ($all) ? 'active' : '',
'title' => t('Show all connections'),
),
);
//$tab_tpl = get_markup_template('common_tabs.tpl');
//$t = replace_macros($tab_tpl, array('$tabs'=>$tabs));
$searching = false;
if($search) {
$search_hdr = $search;
@@ -233,12 +233,12 @@ class Connections extends \Zotlabs\Web\Controller {
$searching = true;
}
$sql_extra .= (($searching) ? protect_sprintf(" AND xchan_name like '%$search_txt%' ") : "");
if($_REQUEST['gid']) {
$sql_extra .= " and xchan_hash in ( select xchan from pgrp_member where gid = " . intval($_REQUEST['gid']) . " and uid = " . intval(local_channel()) . " ) ";
}
$r = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash
$r = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash
where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra ",
intval(local_channel())
);
@@ -246,19 +246,27 @@ class Connections extends \Zotlabs\Web\Controller {
App::set_pager_total($r[0]['total']);
$total = $r[0]['total'];
}
$r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash
WHERE abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra ORDER BY $sql_order LIMIT %d OFFSET %d ",
intval(local_channel()),
intval(App::$pager['itemspage']),
intval(App::$pager['start'])
);
$roles = new Permcat(local_channel());
$roles_list = $roles->listing();
$roles_dict = [];
foreach ($roles_list as $role) {
$roles_dict[$role['name']] = $role['localname'];
}
$contacts = array();
if($r) {
vcard_query($r);
//vcard_query($r);
foreach($r as $rr) {
@@ -268,7 +276,7 @@ class Connections extends \Zotlabs\Web\Controller {
$phone = $rr['vcard']['tels'][0]['nr'];
else
$phone = '';
$status_str = '';
$status = array(
((intval($rr['abook_active'])) ? t('Active') : ''),
@@ -306,7 +314,7 @@ class Connections extends \Zotlabs\Web\Controller {
$perminfo['connperms'] .= t('Nothing');
}
foreach($status as $str) {
if(!$str)
continue;
@@ -314,19 +322,16 @@ class Connections extends \Zotlabs\Web\Controller {
$status_str .= ', ';
}
$status_str = rtrim($status_str, ', ');
$contacts[] = array(
'img_hover' => sprintf( t('%1$s [%2$s]'),$rr['xchan_name'],$rr['xchan_url']),
'edit_hover' => t('Edit connection'),
'edit' => t('Edit'),
'delete_hover' => t('Delete connection'),
'id' => $rr['abook_id'],
'thumb' => $rr['xchan_photo_m'],
'thumb' => $rr['xchan_photo_m'],
'name' => $rr['xchan_name'],
'classes' => ((intval($rr['abook_archived']) || intval($rr['abook_not_here'])) ? 'archived' : ''),
'link' => z_root() . '/connedit/' . $rr['abook_id'],
'deletelink' => z_root() . '/connedit/' . intval($rr['abook_id']) . '/drop',
'delete' => t('Delete'),
'url' => chanlink_hash($rr['xchan_hash']),
'webbie_label' => t('Channel address'),
'webbie' => $rr['xchan_addr'],
@@ -337,6 +342,7 @@ class Connections extends \Zotlabs\Web\Controller {
'phone' => $phone,
'status_label' => t('Status'),
'status' => $status_str,
'states' => $status,
'connected_label' => t('Connected'),
'connected' => datetime_convert('UTC',date_default_timezone_get(),$rr['abook_created'], 'c'),
'approve_hover' => t('Approve connection'),
@@ -349,13 +355,22 @@ class Connections extends \Zotlabs\Web\Controller {
'perminfo' => $perminfo,
'connect' => (intval($rr['abook_not_here']) ? t('Connect') : ''),
'follow' => z_root() . '/follow/?f=&url=' . urlencode($rr['xchan_hash']) . '&interactive=0',
'connect_hover' => t('Connect at this location')
'connect_hover' => t('Connect at this location'),
'role' => $roles_dict[$rr['abook_role']],
'pending' => intval($rr['abook_pending'])
);
}
}
}
$limit = service_class_fetch(local_channel(),'total_channels');
if($limit !== false) {
$abook_usage_message = sprintf( t("You have %1$.0f of %2$.0f allowed connections."), $$total, $limit);
}
else {
$abook_usage_message = '';
}
if($_REQUEST['aj']) {
if($contacts) {
$o = replace_macros(get_markup_template('contactsajax.tpl'),array(
@@ -371,27 +386,30 @@ class Connections extends \Zotlabs\Web\Controller {
}
else {
$o .= "<script> var page_query = '" . escape_tags(urlencode($_GET['q'])) . "'; var extra_args = '" . extra_query_args() . "' ; </script>";
$o .= replace_macros(get_markup_template('connections.tpl'),array(
$o .= replace_macros(get_markup_template('connections.tpl'), [
'$header' => t('Connections') . (($head) ? ': ' . $head : ''),
'$tabs' => $tabs,
'$total' => $total,
'$search' => $search_hdr,
'$label' => t('Search'),
'$role_label' => t('Contact role'),
'$desc' => t('Search your connections'),
'$finding' => (($searching) ? t('Connections search') . ": '" . $search . "'" : ""),
'$finding' => (($searching) ? t('Contact search') . ": '" . $search . "'" : ""),
'$submit' => t('Find'),
'$edit' => t('Edit'),
'$approve' => t('Approve'),
'$cmd' => App::$cmd,
'$contacts' => $contacts,
'$paginate' => paginate($a),
));
'$abook_usage_message' => $abook_usage_message,
'$group_label' => t('This is a group/forum channel')
]);
}
if(! $contacts)
$o .= '<div id="content-complete"></div>';
return $o;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,675 @@
<?php
namespace Zotlabs\Module;
/* @file Cobtactedit.php
* @brief In this file the connection-editor form is generated and evaluated.
*
*
*/
use App;
use Sabre\VObject\Reader;
use Zotlabs\Lib\Apps;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Libsync;
use Zotlabs\Daemon\Master;
use Zotlabs\Web\Controller;
use Zotlabs\Access\Permissions;
use Zotlabs\Access\PermissionLimits;
use Zotlabs\Web\HTTPHeaders;
use Zotlabs\Lib\Permcat;
use Zotlabs\Lib\AccessList;
require_once('include/socgraph.php');
require_once('include/selectors.php');
require_once('include/group.php');
require_once('include/photos.php');
class Contactedit extends Controller {
/* @brief Initialize the connection-editor
*
*
*/
function init() {
if (!local_channel())
return;
if ((argc() >= 2) && intval(argv(1))) {
$r = q("SELECT abook.*, xchan.* FROM abook LEFT JOIN xchan ON abook_xchan = xchan_hash
WHERE abook_channel = %d AND abook_id = %d AND abook_self = 0 AND xchan_deleted = 0",
intval(local_channel()),
intval(argv(1))
);
if (!$r) {
json_return_and_die([
'success' => false,
'message' => t('Invalid abook_id')
]);
}
App::$poi = $r[0];
}
}
/* @brief Evaluate posted values and set changes
*
*/
function post() {
if (!local_channel())
return;
$contact_id = intval(argv(1));
if (!$contact_id)
return;
$channel = App::get_channel();
$contact = App::$poi;
if (!$contact) {
notice(t('Could not access contact record.') . EOL);
killme();
}
call_hooks('contact_edit_post', $_REQUEST);
if (Apps::system_app_installed(local_channel(), 'Privacy Groups')) {
$pgrp_ids = q("SELECT id FROM pgrp WHERE deleted = 0 AND uid = %d",
intval(local_channel())
);
foreach($pgrp_ids as $pgrp) {
if (array_key_exists('pgrp_id_' . $pgrp['id'], $_REQUEST)) {
AccessList::member_add(local_channel(), '', $contact['abook_xchan'], $pgrp['id']);
}
else {
AccessList::member_remove(local_channel(), '', $contact['abook_xchan'], $pgrp['id']);
}
}
}
$profile_id = ((array_key_exists('profile_assign', $_REQUEST)) ? $_REQUEST['profile_assign'] : $contact['abook_profile']);
if ($profile_id) {
$r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND uid = %d LIMIT 1",
dbesc($profile_id),
intval(local_channel())
);
if (!count($r)) {
notice(t('Could not locate selected profile.') . EOL);
return;
}
}
$abook_incl = ((array_key_exists('abook_incl', $_REQUEST)) ? escape_tags($_REQUEST['abook_incl']) : $contact['abook_incl']);
$abook_excl = ((array_key_exists('abook_excl', $_REQUEST)) ? escape_tags($_REQUEST['abook_excl']) : $contact['abook_excl']);
$abook_role = ((array_key_exists('permcat', $_REQUEST)) ? escape_tags($_REQUEST['permcat']) : $contact['abook_role']);
if (!array_key_exists('closeness', $_REQUEST)) {
$_REQUEST['closeness'] = 80;
}
$closeness = intval($_REQUEST['closeness']);
if ($closeness < 0 || $closeness > 99) {
$closeness = 80;
}
$new_friend = ((intval($contact['abook_pending'])) ? true : false);
\Zotlabs\Lib\Permcat::assign($channel, $abook_role, [$contact['abook_xchan']]);
$abook_pending = (($new_friend) ? 0 : $contact['abook_pending']);
$r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d,
abook_incl = '%s', abook_excl = '%s'
where abook_id = %d AND abook_channel = %d",
dbesc($profile_id),
intval($closeness),
intval($abook_pending),
dbesc($abook_incl),
dbesc($abook_excl),
intval($contact_id),
intval(local_channel())
);
$_REQUEST['success'] = false;
if ($r) {
$_REQUEST['success'] = true;
}
if (!intval($contact['abook_self'])) {
if ($new_friend) {
Master::Summon(['Notifier', 'permission_accept', $contact_id]);
}
Master::Summon([
'Notifier',
(($new_friend) ? 'permission_create' : 'permission_update'),
$contact_id
]);
}
if ($new_friend) {
$default_group = $channel['channel_default_group'];
if ($default_group) {
$g = AccessList::by_hash(local_channel(), $default_group);
if ($g) {
AccessList::member_add(local_channel(), '', $contact['abook_xchan'], $g['id']);
}
}
// Check if settings permit ("post new friend activity" is allowed, and
// friends in general or this friend in particular aren't hidden)
// and send out a new friend activity
$pr = q("select * from profile where uid = %d and is_default = 1 and hide_friends = 0",
intval($channel['channel_id'])
);
if (($pr) && (!intval($contact['abook_hidden'])) && (intval(get_pconfig($channel['channel_id'], 'system', 'post_newfriend')))) {
$xarr = [];
$xarr['item_wall'] = 1;
$xarr['item_origin'] = 1;
$xarr['item_thread_top'] = 1;
$xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash'];
$xarr['allow_cid'] = $channel['channel_allow_cid'];
$xarr['allow_gid'] = $channel['channel_allow_gid'];
$xarr['deny_cid'] = $channel['channel_deny_cid'];
$xarr['deny_gid'] = $channel['channel_deny_gid'];
$xarr['item_private'] = (($xarr['allow_cid'] || $xarr['allow_gid'] || $xarr['deny_cid'] || $xarr['deny_gid']) ? 1 : 0);
$xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . $contact['xchan_url'] . ']' . $contact['xchan_name'] . '[/zrl]';
$xarr['body'] .= "\n\n\n" . '[zrl=' . $contact['xchan_url'] . '][zmg=80x80]' . $contact['xchan_photo_m'] . '[/zmg][/zrl]';
post_activity_item($xarr);
}
// pull in a bit of content if there is any to pull in
Master::Summon(['Onepoll', $contact_id]);
}
// Refresh the structure in memory with the new data
$this->init();
if ($new_friend) {
$arr = ['channel_id' => local_channel(), 'abook' => App::$poi];
call_hooks('accept_follow', $arr);
}
$this->contactedit_clone();
$this->get();
killme();
return;
}
/* @brief Generate content of contact edit page
*
*
*/
function get() {
if (!local_channel()) {
killme();
}
if (!App::$poi) {
killme();
}
$channel = App::get_channel();
$contact_id = App::$poi['abook_id'];
$contact = App::$poi;
$section = ((array_key_exists('section', $_REQUEST)) ? $_REQUEST['section'] : 'roles');
$sub_section = ((array_key_exists('sub_section', $_REQUEST)) ? $_REQUEST['sub_section'] : '');
if (argc() == 3) {
$cmd = argv(2);
$ret = $this->do_action($contact, $cmd);
$contact = App::$poi;
$tools_html = replace_macros(get_markup_template("contact_edit_tools.tpl"), [
'$tools_label' => t('Contact Tools'),
'$tools' => $this->get_tools($contact),
]);
$ret['tools'] = $tools_html;
json_return_and_die($ret);
}
$groups = [];
if (Apps::system_app_installed(local_channel(), 'Privacy Groups')) {
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
intval(local_channel())
);
$member_of = AccessList::containing(local_channel(), $contact['xchan_hash']);
if ($r) {
foreach ($r as $rr) {
$default_group = false;
if ($rr['hash'] === $channel['channel_default_group']) {
$default_group = true;
}
$groups[] = [
'pgrp_id_' . $rr['id'],
$rr['gname'],
// if it's a new contact preset the default group if we have one
(($default_group && $contact['abook_pending']) ? 1 : in_array($rr['id'], $member_of)),
'',
[t('No'), t('Yes')]
];
}
}
}
$slide = '';
if (Apps::system_app_installed(local_channel(), 'Affinity Tool')) {
$labels = [
t('Me'),
t('Family'),
t('Friends'),
t('Acquaintances'),
t('All')
];
call_hooks('affinity_labels', $labels);
$label_str = '';
if ($labels) {
foreach ($labels as $l) {
if ($label_str) {
$label_str .= ", '|'";
$label_str .= ", '" . $l . "'";
}
else
$label_str .= "'" . $l . "'";
}
}
$slider_tpl = get_markup_template('contact_slider.tpl');
$slideval = intval($contact['abook_closeness']);
$slide = replace_macros($slider_tpl, [
'$min' => 1,
'$val' => $slideval,
'$labels' => $label_str,
]);
}
$perms = [];
$global_perms = Permissions::Perms();
$existing = get_all_perms(local_channel(), $contact['abook_xchan'], false);
$unapproved = ['pending', t('Approve this contact'), '', t('Accept contact to allow communication'), [t('No'), ('Yes')]];
$multiprofs = ((feature_enabled(local_channel(), 'multi_profiles')) ? true : false);
$theirs = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'their_perms'",
intval(local_channel()),
dbesc($contact['abook_xchan'])
);
$their_perms = [];
if ($theirs) {
foreach ($theirs as $t) {
$their_perms[$t['k']] = $t['v'];
}
}
foreach ($global_perms as $k => $v) {
$thisperm = $existing[$k];
$checkinherited = PermissionLimits::Get(local_channel(), $k);
$perms[] = ['perms_' . $k, $v, ((array_key_exists($k, $their_perms)) ? intval($their_perms[$k]) : ''), $thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '0' : '1'), '', $checkinherited];
}
$pcat = new Permcat(local_channel());
$pcatlist = $pcat->listing();
$default_role = get_pconfig(local_channel(), 'system', 'default_permcat');
$current_permcat = (($contact['abook_pending']) ? $default_role : $contact['abook_role']);
$roles_dict = [];
foreach ($pcatlist as $role) {
$roles_dict[$role['name']] = $role['localname'];
}
if (!$current_permcat) {
notice(t('Please select a role for this contact!') . EOL);
$permcats[] = '';
}
if ($pcatlist) {
foreach ($pcatlist as $pc) {
$permcats[$pc['name']] = $pc['localname'];
}
}
$locstr = locations_by_netid($contact['xchan_hash']);
if (!$locstr) {
$locstr = unpunify($contact['xchan_url']);
}
$clone_warn = '';
$clonable = in_array($contact['xchan_network'], ['zot6', 'rss']);
if (!$clonable) {
$clone_warn = '<strong>';
$clone_warn .= ((intval($contact['abook_not_here']))
? t('This contact is unreachable from this location.')
: t('This contact may be unreachable from other channel locations.')
);
$clone_warn .= '</strong><br>' . t('Location independence is not supported by their network.');
}
$header_card = '<img src="' . $contact['xchan_photo_s'] . '" class="rounded" style="width: 3rem; height: 3rem;">&nbsp; ' . $contact['xchan_name'];
$header_html = replace_macros(get_markup_template("contact_edit_header.tpl"), [
'$img_src' => $contact['xchan_photo_s'],
'$name' => $contact['xchan_name'],
'$addr' => (($contact['xchan_addr']) ? $contact['xchan_addr'] : $contact['xchan_url']),
'$href' => ((is_matrix_url($contact['xchan_url'])) ? zid($contact['xchan_url']) : $contact['xchan_url']),
'$link_label' => t('View profile'),
'$is_group' => $contact['xchan_pubforum'],
'$group_label' => t('This is a group/forum channel')
]);
$tools_html = replace_macros(get_markup_template("contact_edit_tools.tpl"), [
'$tools_label' => t('Contact Tools'),
'$tools' => $this->get_tools($contact),
]);
$tpl = get_markup_template("contact_edit.tpl");
$o = replace_macros($tpl, [
'$permcat' => ['permcat', t('Select a role for this contact'), $current_permcat, '', $permcats],
'$permcat_new' => t('Contact roles'),
'$permcat_value' => bin2hex($current_permcat),
// '$addr' => unpunify($contact['xchan_addr']),
// '$primeurl' => unpunify($contact['xchan_url']),
'$section' => $section,
'$sub_section' => $sub_section,
'$groups' => $groups,
// '$addr_text' => t('This contacts\'s primary address is'),
// '$loc_text' => t('Available locations:'),
// '$locstr' => $locstr,
// '$unclonable' => $clone_warn,
'$lbl_slider' => t('Slide to adjust your degree of friendship'),
'$connfilter' => feature_enabled(local_channel(), 'connfilter'),
'$connfilter_label' => t('Custom Filter'),
'$incl' => ['abook_incl', t('Only import posts with this text'), $contact['abook_incl'], t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')],
'$excl' => ['abook_excl', t('Do not import posts with this text'), $contact['abook_excl'], t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')],
'$slide' => $slide,
// '$pending_label' => t('Contact Pending Approval'),
// '$is_pending' => (intval($contact['abook_pending']) ? 1 : ''),
// '$unapproved' => $unapproved,
'$submit' => ((intval($contact['abook_pending'])) ? t('Approve contact') : t('Submit')),
'$close' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 80),
'$them' => t('Their'),
'$me' => t('My'),
'$perms' => $perms,
// '$lastupdtext' => t('Last update:'),
// '$last_update' => relative_date($contact['abook_connected']),
'$profile_select' => contact_profile_assign($contact['abook_profile']),
'$multiprofs' => $multiprofs,
'$contact_id' => $contact['abook_id'],
// '$name' => $contact['xchan_name'],
'$roles_label' => t('Roles'),
'$compare_label' => t('Compare permissions'),
'$permission_label' => t('Permission'),
'$pgroups_label' => t('Privacy groups'),
'$profiles_label' => t('Profiles'),
'$affinity_label' => t('Affinity'),
'$filter_label' => t('Content filter')
]);
$arr = ['contact' => $contact, 'output' => $o];
call_hooks('contact_edit', $arr);
if (is_ajax()) {
json_return_and_die([
'success' => ((intval($_REQUEST['success'])) ? intval($_REQUEST['success']) : 1),
'message' => (($_REQUEST['success']) ? t('Contact updated') : t('Contact update failed')),
'id' => $contact_id,
'title' => $header_html,
'role' => ((intval($contact['abook_pending'])) ? '' : $roles_dict[$current_permcat]),
'body' => $arr['output'],
'tools' => $tools_html,
'submit' => ((intval($contact['abook_pending'])) ? t('Approve connection') : t('Submit')),
'pending' => intval($contact['abook_pending'])
]);
}
return $arr['output'];
}
function contactedit_clone() {
if (!App::$poi)
return;
$channel = App::get_channel();
$clone = App::$poi;
unset($clone['abook_id']);
unset($clone['abook_account']);
unset($clone['abook_channel']);
$abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']);
if ($abconfig)
$clone['abconfig'] = $abconfig;
Libsync::build_sync_packet(0 /* use the current local_channel */, ['abook' => [$clone]]);
}
function do_action($contact, $cmd) {
$ret = [
'sucess' => false,
'message' => ''
];
if ($cmd === 'resetphoto') {
q("update xchan set xchan_photo_date = '2001-01-01 00:00:00' where xchan_hash = '%s'",
dbesc($contact['xchan_hash'])
);
$cmd = 'refresh';
}
if ($cmd === 'refresh') {
if ($contact['xchan_network'] === 'zot6') {
if (Libzot::refresh($contact, App::get_channel())) {
$ret['success'] = true;
$ret['message'] = t('Refresh succeeded');
}
else {
$ret['message'] = t('Refresh failed - channel is currently unavailable');
}
}
else {
// if you are on a different network we'll force a refresh of the connection basic info
Master::Summon(['Notifier', 'permission_update', $contact['abook_id']]);
$ret['success'] = true;
$ret['message'] = t('Refresh succeeded');
}
return $ret;
}
if ($cmd === 'block') {
if (abook_toggle_flag($contact, ABOOK_FLAG_BLOCKED)) {
$this->init(); // refresh data
$this->contactedit_clone();
$ret['success'] = true;
$ret['message'] = t('Block status updated');
}
else {
$ret['success'] = false;
$ret['message'] = t('Block failed');
}
return $ret;
}
if ($cmd === 'ignore') {
if (abook_toggle_flag($contact, ABOOK_FLAG_IGNORED)) {
$this->init(); // refresh data
$this->contactedit_clone();
$ret['success'] = true;
$ret['message'] = t('Ignore status updated');
}
else {
$ret['success'] = false;
$ret['message'] = t('Ignore failed');
}
return $ret;
}
if ($cmd === 'archive') {
if (abook_toggle_flag($contact, ABOOK_FLAG_ARCHIVED)) {
$this->init(); // refresh data
$this->contactedit_clone();
$ret['success'] = true;
$ret['message'] = t('Archive status updated');
}
else {
$ret['success'] = false;
$ret['message'] = t('Archive failed');
}
return $ret;
}
if ($cmd === 'hide') {
if (abook_toggle_flag($contact, ABOOK_FLAG_HIDDEN)) {
$this->init(); // refresh data
$this->contactedit_clone();
$ret['success'] = true;
$ret['message'] = t('Hide status updated');
}
else {
$ret['success'] = false;
$ret['message'] = t('Hide failed');
}
return $ret;
}
// We'll prevent somebody from unapproving an already approved contact.
// Though maybe somebody will want this eventually (??)
//if ($cmd === 'approve') {
//if (intval($contact['abook_pending'])) {
//if (abook_toggle_flag($contact, ABOOK_FLAG_PENDING)) {
//$this->contactedit_clone();
//}
//else
//notice(t('Unable to set address book parameters.') . EOL);
//}
//goaway(z_root() . '/connedit/' . $contact_id);
//}
if ($cmd === 'drop') {
if (contact_remove(local_channel(), $contact['abook_id'])) {
Master::Summon(['Notifier', 'purge', local_channel(), $contact['xchan_hash']]);
Libsync::build_sync_packet(0 /* use the current local_channel */,
['abook' => [
[
'abook_xchan' => $contact['abook_xchan'],
'entry_deleted' => true
]
]
]);
$ret['success'] = true;
$ret['message'] = t('Contact removed');
}
else {
$ret['success'] = false;
$ret['message'] = t('Delete failed');
}
return $ret;
}
}
function get_tools($contact) {
return [
'refresh' => [
'label' => t('Refresh Permissions'),
'title' => t('Fetch updated permissions'),
],
'rephoto' => [
'label' => t('Refresh Photo'),
'title' => t('Fetch updated photo'),
],
'block' => [
'label' => (intval($contact['abook_blocked']) ? t('Unblock') : t('Block')),
'sel' => (intval($contact['abook_blocked']) ? 'active' : ''),
'title' => t('Block (or Unblock) all communications with this connection'),
'info' => (intval($contact['abook_blocked']) ? t('This connection is blocked!') : ''),
],
'ignore' => [
'label' => (intval($contact['abook_ignored']) ? t('Unignore') : t('Ignore')),
'sel' => (intval($contact['abook_ignored']) ? 'active' : ''),
'title' => t('Ignore (or Unignore) all inbound communications from this connection'),
'info' => (intval($contact['abook_ignored']) ? t('This connection is ignored!') : ''),
],
'archive' => [
'label' => (intval($contact['abook_archived']) ? t('Unarchive') : t('Archive')),
'sel' => (intval($contact['abook_archived']) ? 'active' : ''),
'title' => t('Archive (or Unarchive) this connection - mark channel dead but keep content'),
'info' => (intval($contact['abook_archived']) ? t('This connection is archived!') : ''),
],
'hide' => [
'label' => (intval($contact['abook_hidden']) ? t('Unhide') : t('Hide')),
'sel' => (intval($contact['abook_hidden']) ? 'active' : ''),
'title' => t('Hide or Unhide this connection from your other connections'),
'info' => (intval($contact['abook_hidden']) ? t('This connection is hidden!') : ''),
],
'delete' => [
'label' => t('Delete'),
'sel' => '',
'title' => t('Delete this connection'),
],
];
}
}

View File

@@ -1,17 +1,17 @@
<?php
namespace Zotlabs\Module;
require_once('include/group.php');
use Zotlabs\Lib\AccessList;
use Zotlabs\Web\Controller;
class Contactgroup extends \Zotlabs\Web\Controller {
class Contactgroup extends Controller {
function get() {
if(! local_channel()) {
killme();
}
if((argc() > 2) && (intval(argv(1))) && (argv(2))) {
$r = q("SELECT abook_xchan from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1",
dbesc(base64url_decode(argv(2))),
@@ -20,9 +20,9 @@ class Contactgroup extends \Zotlabs\Web\Controller {
if($r)
$change = $r[0]['abook_xchan'];
}
if((argc() > 1) && (intval(argv(1)))) {
$r = q("SELECT * FROM pgrp WHERE id = %d AND uid = %d AND deleted = 0 LIMIT 1",
intval(argv(1)),
intval(local_channel())
@@ -30,25 +30,25 @@ class Contactgroup extends \Zotlabs\Web\Controller {
if(! $r) {
killme();
}
$group = $r[0];
$members = group_get_members($group['id']);
$members = AccessList::members(local_channel(), $group['id']);
$preselected = array();
if(count($members)) {
foreach($members as $member)
$preselected[] = $member['xchan_hash'];
}
if($change) {
if(in_array($change,$preselected)) {
group_rmv_member(local_channel(),$group['gname'],$change);
AccessList::member_remove(local_channel(),$group['gname'],$change);
}
else {
group_add_member(local_channel(),$group['gname'],$change);
AccessList::member_add(local_channel(),$group['gname'],$change);
}
}
}
killme();
}
}

View File

@@ -8,7 +8,6 @@ use Zotlabs\Lib\Libsync;
require_once('include/socgraph.php');
require_once('include/selectors.php');
require_once('include/group.php');
require_once('include/photos.php');
class Defperms extends Controller {
@@ -23,8 +22,8 @@ class Defperms extends Controller {
if(! local_channel())
return;
if(! Apps::system_app_installed(local_channel(), 'Default Permissions'))
return;
//if(! Apps::system_app_installed(local_channel(), 'Default Permissions'))
// return;
$r = q("SELECT abook.*, xchan.*
FROM abook left join xchan on abook_xchan = xchan_hash
@@ -50,8 +49,8 @@ class Defperms extends Controller {
if(! local_channel())
return;
if(! Apps::system_app_installed(local_channel(), 'Default Permissions'))
return;
//if(! Apps::system_app_installed(local_channel(), 'Default Permissions'))
// return;
$contact_id = intval(argv(1));
if(! $contact_id)
@@ -183,12 +182,12 @@ class Defperms extends Controller {
return login();
}
if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) {
//Do not display any associated widgets at this point
App::$pdl = '';
$papp = Apps::get_papp('Default Permissions');
return Apps::app_render($papp, 'module');
}
//~ if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) {
//~ //Do not display any associated widgets at this point
//~ App::$pdl = '';
//~ $papp = Apps::get_papp('Default Permissions');
//~ return Apps::app_render($papp, 'module');
//~ }
$section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : '');
$channel = App::get_channel();

View File

@@ -99,6 +99,9 @@ class File_upload extends \Zotlabs\Web\Controller {
}
}
if(is_ajax())
killme();
goaway(z_root() . '/' . $_REQUEST['return_url']);
}

View File

@@ -108,7 +108,7 @@ class Follow extends Controller {
}
Libsync::build_sync_packet(0, [ 'abook' => [ $clone ] ], true);
$can_view_stream = their_perms_contains($channel['channel_id'],$clone['abook_xchan'],'view_stream');
$can_view_stream = intval(get_abconfig($channel['channel_id'], $clone['abook_xchan'], 'their_perms', 'view_stream'));
// If we can view their stream, pull in some posts
@@ -117,7 +117,7 @@ class Follow extends Controller {
}
if ($interactive) {
goaway(z_root() . '/connedit/' . $result['abook']['abook_id'] . '?follow=1');
goaway(z_root() . '/connections#' . $result['abook']['abook_id']);
}
else {
json_return_and_die([ 'success' => true ]);

View File

@@ -5,8 +5,7 @@ use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Apps;
use Zotlabs\Lib\Libsync;
require_once('include/group.php');
use Zotlabs\Lib\AccessList;
class Group extends Controller {
@@ -41,16 +40,17 @@ class Group extends Controller {
$name = notags(trim($_POST['groupname']));
$public = intval($_POST['public']);
$r = group_add(local_channel(),$name,$public);
$r = AccessList::add(local_channel(),$name,$public);
$group_hash = $r;
if($r) {
info( t('Privacy group created.') . EOL );
}
else {
notice( t('Could not create privacy group.') . EOL );
}
goaway(z_root() . '/group');
}
if((argc() == 2) && (intval(argv(1)))) {
check_form_security_token_redirectOnErr('/group', 'group_edit');
@@ -65,10 +65,11 @@ class Group extends Controller {
}
$group = $r[0];
$groupname = notags(trim($_POST['groupname']));
$group_hash = $group['hash'];
$public = intval($_POST['public']);
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ];
call_hooks ('privacygroup_extras_post',$hookinfo);
call_hooks('privacygroup_extras_post',$hookinfo);
if((strlen($groupname)) && (($groupname != $group['gname']) || ($public != $group['visible']))) {
$r = q("UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d",
@@ -79,13 +80,25 @@ class Group extends Controller {
);
if($r)
info( t('Privacy group updated.') . EOL );
Libsync::build_sync_packet(local_channel(),null,true);
}
goaway(z_root() . '/group/' . argv(1) . '/' . argv(2));
}
$channel = App::get_channel();
$default_group = ((isset($_POST['set_default_group'])) ? $group_hash : (($channel['channel_default_group'] === $group_hash) ? '' : $channel['channel_default_group']));
$default_acl = ((isset($_POST['set_default_acl'])) ? '<' . $group_hash . '>' : (($channel['channel_allow_gid'] === '<' . $group_hash . '>') ? '' : $channel['channel_allow_gid']));
q("update channel set channel_default_group = '%s', channel_allow_gid = '%s'
where channel_id = %d",
dbesc($default_group),
dbesc($default_acl),
intval(local_channel())
);
Libsync::build_sync_packet(local_channel(),null,true);
goaway(z_root() . '/group/' . argv(1) . ((argv(2)) ? '/' . argv(2) : ''));
return;
}
@@ -117,51 +130,32 @@ class Group extends Controller {
if((argc() == 1) || ((argc() == 2) && (argv(1) === 'new'))) {
$new = (((argc() == 2) && (argv(1) === 'new')) ? true : false);
$groups = q("SELECT id, gname FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
intval(local_channel())
);
$i = 0;
foreach($groups as $group) {
$entries[$i]['name'] = $group['gname'];
$entries[$i]['id'] = $group['id'];
$entries[$i]['count'] = count(group_get_members($group['id']));
$i++;
}
$hookinfo = [ 'pgrp_extras' => '', 'group'=>argv(1) ];
call_hooks ('privacygroup_extras',$hookinfo);
$pgrp_extras = $hookinfo['pgrp_extras'];
$is_default_acl = ['set_default_acl', t('Post to this group by default'), 0, '', [t('No'), t('Yes')]];
$is_default_group = ['set_default_group', t('Add new contacts to this group by default'), 0, '', [t('No'), t('Yes')]];
$tpl = get_markup_template('privacy_groups.tpl');
$o = replace_macros($tpl, [
'$title' => t('Privacy Groups'),
'$add_new_label' => t('Add Group'),
'$new' => $new,
// new group form
'$gname' => array('groupname',t('Privacy group name')),
'$public' => array('public',t('Members are visible to other channels'), false),
'$public' => array('public',t('Members are visible to other channels'), 0, '', [t('No'), t('Yes')]),
'$pgrp_extras' => $pgrp_extras,
'$form_security_token' => get_form_security_token("group_edit"),
'$submit' => t('Submit'),
// groups list
'$title' => t('Privacy Groups'),
'$name_label' => t('Name'),
'$count_label' => t('Members'),
'$entries' => $entries
'$is_default_acl' => $is_default_acl,
'$is_default_group' => $is_default_group,
]);
return $o;
}
$context = array('$submit' => t('Submit'));
$tpl = get_markup_template('group_edit.tpl');
@@ -174,7 +168,7 @@ class Group extends Controller {
intval(local_channel())
);
if($r)
$result = group_rmv(local_channel(),$r[0]['gname']);
$result = AccessList::remove(local_channel(),$r[0]['gname']);
if($result) {
$hookinfo = [ 'pgrp_extras' => '', 'group' => argv(2) ];
call_hooks ('privacygroup_extras_drop',$hookinfo);
@@ -215,7 +209,7 @@ class Group extends Controller {
$group = $r[0];
$members = group_get_members($group['id']);
$members = AccessList::members(local_channel(), $group['id']);
$preselected = array();
if(count($members)) {
@@ -227,13 +221,13 @@ class Group extends Controller {
if($change) {
if(in_array($change,$preselected)) {
group_rmv_member(local_channel(),$group['gname'],$change);
AccessList::member_remove(local_channel(),$group['gname'],$change);
}
else {
group_add_member(local_channel(),$group['gname'],$change);
AccessList::member_add(local_channel(),$group['gname'],$change);
}
$members = group_get_members($group['id']);
$members = AccessList::members(local_channel(), $group['id']);
$preselected = array();
if(count($members)) {
@@ -252,9 +246,9 @@ class Group extends Controller {
'$gname' => array('groupname',t('Privacy group name: '),$group['gname'], ''),
'$gid' => $group['id'],
'$drop' => $drop_txt,
'$public' => array('public',t('Members are visible to other channels'), $group['visible'], ''),
'$public' => array('public',t('Members are visible to other channels'), $group['visible'], '', [t('No'), t('Yes')]),
'$form_security_token_edit' => get_form_security_token('group_edit'),
'$delete' => t('Delete Group'),
'$delete' => t('Delete'),
'$form_security_token_drop' => get_form_security_token("group_drop"),
'$pgrp_extras' => $pgrp_extras,
);
@@ -280,7 +274,7 @@ class Group extends Controller {
$groupeditor['members'][] = micropro($member,true,'mpgroup', $textmode);
}
else
group_rmv_member(local_channel(),$group['gname'],$member['xchan_hash']);
AccessList::member_remove(local_channel(),$group['gname'],$member['xchan_hash']);
}
$r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d AND abook_self = 0 and abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 order by xchan_name asc",
@@ -302,6 +296,12 @@ class Group extends Controller {
$context['$desc'] = t('Click a channel to toggle membership');
$context['$pgrp_extras'] = $pgrp_extras;
$channel = App::get_channel();
$context['$is_default_acl'] = ['set_default_acl', t('Post to this group by default'), intval($group['hash'] === trim($channel['channel_allow_gid'], '<>')), '', [t('No'), t('Yes')]];
$context['$is_default_group'] = ['set_default_group', t('Add new contacts to this group by default'), intval($group['hash'] === $channel['channel_default_group']), '', [t('No'), t('Yes')]];
if($change) {
$tpl = get_markup_template('groupeditor.tpl');
echo replace_macros($tpl, $context);

View File

@@ -42,6 +42,9 @@ class Hq extends \Zotlabs\Web\Controller {
$item_normal = item_normal();
$item_normal_update = item_normal_update();
$sys = get_sys_channel();
$sys_item = false;
$sql_extra = '';
if(! $item_hash) {
$r = q("SELECT mid FROM item
@@ -77,11 +80,6 @@ class Hq extends \Zotlabs\Web\Controller {
if($update && $_SESSION['loadtime'])
$simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) ";
$sys = get_sys_channel();
$sql_extra = item_permissions_sql($sys['channel_id']);
$sys_item = false;
}
if(! $update) {
@@ -183,6 +181,7 @@ class Hq extends \Zotlabs\Web\Controller {
if(!$r) {
$sys_item = true;
$sql_extra = item_permissions_sql($sys['channel_id']);
$r = q("SELECT item.id AS item_id FROM item
LEFT JOIN abook ON item.author_xchan = abook.abook_xchan
@@ -209,6 +208,7 @@ class Hq extends \Zotlabs\Web\Controller {
if(!$r) {
$sys_item = true;
$sql_extra = item_permissions_sql($sys['channel_id']);
$r = q("SELECT item.parent AS item_id FROM item
LEFT JOIN abook ON item.author_xchan = abook.abook_xchan
@@ -227,7 +227,7 @@ class Hq extends \Zotlabs\Web\Controller {
if($r) {
$items = q("SELECT item.*, item.id AS item_id
FROM item
WHERE parent = '%s' $item_normal ",
WHERE parent = '%s' $item_normal $sql_extra",
dbesc($r[0]['item_id'])
);
@@ -254,7 +254,6 @@ class Hq extends \Zotlabs\Web\Controller {
return;
$options['offset'] = $_REQUEST['offset'];
$options['dm'] = $_REQUEST['dm'];
$options['type'] = $_REQUEST['type'];
$ret = Messages::get_messages_page($options);

View File

@@ -1,5 +1,9 @@
<?php
namespace Zotlabs\Module; /** @file */
namespace Zotlabs\Module;
use URLify;
/** @file */
// import page design element
@@ -9,33 +13,33 @@ require_once('include/menu.php');
class Impel extends \Zotlabs\Web\Controller {
function init() {
$ret = array('success' => false);
if(! local_channel())
json_return_and_die($ret);
logger('impel: ' . print_r($_REQUEST,true), LOGGER_DATA);
$elm = $_REQUEST['element'];
$x = base64url_decode($elm);
if(! $x)
json_return_and_die($ret);
$j = json_decode($x,true);
if(! $j)
json_return_and_die($ret);
// logger('element: ' . print_r($j,true));
$channel = \App::get_channel();
$arr = array();
$is_menu = false;
// a portable menu has its links rewritten with the local baseurl
$portable_menu = false;
switch($j['type']) {
case 'webpage':
$arr['item_type'] = ITEM_TYPE_WEBPAGE;
@@ -58,12 +62,12 @@ class Impel extends \Zotlabs\Web\Controller {
case 'menu':
$is_menu = true;
$installed_type = t('menu');
break;
break;
default:
logger('mod_impel: unrecognised element type' . print_r($j,true));
break;
}
if($is_menu) {
$m = array();
$m['menu_channel_id'] = local_channel();
@@ -73,23 +77,23 @@ class Impel extends \Zotlabs\Web\Controller {
$m['menu_created'] = datetime_convert($j['created']);
if($j['edited'])
$m['menu_edited'] = datetime_convert($j['edited']);
$m['menu_flags'] = 0;
if($j['flags']) {
if(in_array('bookmark',$j['flags']))
$m['menu_flags'] |= MENU_BOOKMARK;
if(in_array('system',$j['flags']))
$m['menu_flags'] |= MENU_SYSTEM;
}
$menu_id = menu_create($m);
if($menu_id) {
if(is_array($j['items'])) {
foreach($j['items'] as $it) {
$mitem = array();
$mitem['mitem_link'] = str_replace('[channelurl]',z_root() . '/channel/' . $channel['channel_address'],$it['link']);
$mitem['mitem_link'] = str_replace('[pageurl]',z_root() . '/page/' . $channel['channel_address'],$it['link']);
$mitem['mitem_link'] = str_replace('[cloudurl]',z_root() . '/cloud/' . $channel['channel_address'],$it['link']);
@@ -115,7 +119,7 @@ class Impel extends \Zotlabs\Web\Controller {
intval(local_channel())
);
}
}
}
$ret['success'] = true;
}
$x = $ret;
@@ -132,22 +136,21 @@ class Impel extends \Zotlabs\Web\Controller {
$arr['owner_xchan'] = get_observer_hash();
$arr['author_xchan'] = (($j['author_xchan']) ? $j['author_xchan'] : get_observer_hash());
$arr['mimetype'] = (($j['mimetype']) ? $j['mimetype'] : 'text/bbcode');
if(! $j['mid']) {
$j['uuid'] = item_message_id();
$j['mid'] = z_root() . '/item/' . $j['uuid'];
}
$arr['uuid'] = $j['uuid'];
$arr['mid'] = $arr['parent_mid'] = $j['mid'];
if($j['pagetitle']) {
require_once('library/urlify/URLify.php');
$pagetitle = strtolower(\URLify::transliterate($j['pagetitle']));
$pagetitle = strtolower(URLify::transliterate($j['pagetitle']));
}
// Verify ability to use html or php!!!
$execflag = ((intval($channel['channel_id']) == intval(local_channel()) && ($channel['channel_pageflags'] & PAGE_ALLOWCODE)) ? true : false);
$i = q("select id, edited, item_deleted from item where mid = '%s' and uid = %d limit 1",
@@ -156,7 +159,7 @@ class Impel extends \Zotlabs\Web\Controller {
);
\Zotlabs\Lib\IConfig::Set($arr,'system',$namespace,(($pagetitle) ? $pagetitle : substr($arr['mid'],0,16)),true);
if($i) {
$arr['id'] = $i[0]['id'];
// don't update if it has the same timestamp as the original
@@ -174,24 +177,24 @@ class Impel extends \Zotlabs\Web\Controller {
else
$x = item_store($arr,$execflag);
}
if($x && $x['success']) {
$item_id = $x['item_id'];
}
}
if($x['success']) {
$ret['success'] = true;
info( sprintf( t('%s element installed'), $installed_type));
info( sprintf( t('%s element installed'), $installed_type));
}
else {
notice( sprintf( t('%s element installation failed'), $installed_type));
notice( sprintf( t('%s element installation failed'), $installed_type));
}
//??? should perhaps return ret?
//??? should perhaps return ret?
json_return_and_die(true);
}
}

View File

@@ -5,10 +5,12 @@ namespace Zotlabs\Module;
require_once('include/channel.php');
require_once('include/import.php');
require_once('include/perm_upgrade.php');
require_once('library/urlify/URLify.php');
use Zotlabs\Lib\Crypto;
use App;
use URLify;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Libzot;
use Zotlabs\Web\Controller;
/**
@@ -17,7 +19,7 @@ use Zotlabs\Lib\Libzot;
* Import a channel, either by direct file upload or via
* connection to another server.
*/
class Import extends \Zotlabs\Web\Controller {
class Import extends Controller {
/**
* @brief Import channel into account.
@@ -26,95 +28,94 @@ class Import extends \Zotlabs\Web\Controller {
*/
function import_account($account_id) {
if(! $account_id){
if (!$account_id) {
logger('No account ID supplied');
return;
}
$max_friends = account_service_class_fetch($account_id,'total_channels');
$max_feeds = account_service_class_fetch($account_id,'total_feeds');
$data = null;
$seize = ((x($_REQUEST,'make_primary')) ? intval($_REQUEST['make_primary']) : 0);
$import_posts = ((x($_REQUEST,'import_posts')) ? intval($_REQUEST['import_posts']) : 0);
$moving = intval($_REQUEST['moving']);
$src = $_FILES['filename']['tmp_name'];
$filename = basename($_FILES['filename']['name']);
$filesize = intval($_FILES['filename']['size']);
$filetype = $_FILES['filename']['type'];
$newname = trim(strtolower($_REQUEST['newname']));
$max_friends = account_service_class_fetch($account_id, 'total_channels');
$max_feeds = account_service_class_fetch($account_id, 'total_feeds');
$data = null;
$seize = ((x($_REQUEST, 'make_primary')) ? intval($_REQUEST['make_primary']) : 0);
$import_posts = ((x($_REQUEST, 'import_posts')) ? intval($_REQUEST['import_posts']) : 0);
$moving = false; //intval($_REQUEST['moving']);
$src = $_FILES['filename']['tmp_name'];
$filename = basename($_FILES['filename']['name']);
$filesize = intval($_FILES['filename']['size']);
$filetype = $_FILES['filename']['type'];
$newname = trim(strtolower($_REQUEST['newname']));
// import channel from file
if($src) {
if ($src) {
// This is OS specific and could also fail if your tmpdir isn't very
// large mostly used for Diaspora which exports gzipped files.
if(strpos($filename,'.gz')){
@rename($src,$src . '.gz');
if (strpos($filename, '.gz')) {
@rename($src, $src . '.gz');
@system('gunzip ' . escapeshellarg($src . '.gz'));
}
if($filesize) {
if ($filesize) {
$data = @file_get_contents($src);
}
unlink($src);
}
// import channel from another server
if(! $src) {
$old_address = ((x($_REQUEST,'old_address')) ? $_REQUEST['old_address'] : '');
if(! $old_address) {
if (!$src) {
$old_address = ((x($_REQUEST, 'old_address')) ? $_REQUEST['old_address'] : '');
if (!$old_address) {
logger('Nothing to import.');
notice( t('Nothing to import.') . EOL);
notice(t('Nothing to import.') . EOL);
return;
} else if(strpos($old_address, '')) {
}
else if (strpos($old_address, '')) {
// if you copy the identity address from your profile page, make it work for convenience - WARNING: this is a utf-8 variant and NOT an ASCII ampersand. Please do not edit.
$old_address = str_replace('', '@', $old_address);
}
$email = ((x($_REQUEST,'email')) ? $_REQUEST['email'] : '');
$password = ((x($_REQUEST,'password')) ? $_REQUEST['password'] : '');
$email = ((x($_REQUEST, 'email')) ? $_REQUEST['email'] : '');
$password = ((x($_REQUEST, 'password')) ? $_REQUEST['password'] : '');
$channelname = substr($old_address,0,strpos($old_address,'@'));
$servername = substr($old_address,strpos($old_address,'@')+1);
$channelname = substr($old_address, 0, strpos($old_address, '@'));
$servername = substr($old_address, strpos($old_address, '@') + 1);
$api_path = probe_api_path($servername);
if(! $api_path) {
notice( t('Unable to download data from old server') . EOL);
if (!$api_path) {
notice(t('Unable to download data from old server') . EOL);
return;
}
$api_path .= 'channel/export/basic?f=&channel=' . $channelname;
if($import_posts)
$api_path .= '&posts=1';
$binary = false;
$binary = false;
$redirects = 0;
$opts = array('http_auth' => $email . ':' . $password);
$ret = z_fetch_url($api_path, $binary, $redirects, $opts);
if($ret['success']) {
$opts = ['http_auth' => $email . ':' . $password];
$ret = z_fetch_url($api_path, $binary, $redirects, $opts);
if ($ret['success']) {
$data = $ret['body'];
}
else {
notice( t('Unable to download data from old server') . EOL);
notice(t('Unable to download data from old server') . EOL);
return;
}
}
if(! $data) {
if (!$data) {
logger('Empty import file.');
notice( t('Imported file is empty.') . EOL);
notice(t('Imported file is empty.') . EOL);
return;
}
$data = json_decode($data,true);
$data = json_decode($data, true);
//logger('import: data: ' . print_r($data,true));
//print_r($data);
if(! array_key_exists('compatibility',$data)) {
call_hooks('import_foreign_channel_data',$data);
if($data['handled'])
if (!array_key_exists('compatibility', $data)) {
call_hooks('import_foreign_channel_data', $data);
if ($data['handled'])
return;
}
@@ -132,47 +133,47 @@ class Import extends \Zotlabs\Web\Controller {
// prevent incompatible osada or zap data from horking your database
if(array_path_exists('compatibility/codebase',$data)) {
if (array_path_exists('compatibility/codebase', $data)) {
notice('Data export format is not compatible with this software');
return;
}
if(version_compare($data['compatibility']['version'], '4.7.3', '<=')) {
if (version_compare($data['compatibility']['version'], '4.7.3', '<=')) {
// zot6 transition: cloning is not compatible with older versions
notice('Data export format is not compatible with this software (not a zot6 channel)');
return;
}
if($moving)
if ($moving)
$seize = 1;
// import channel
$relocate = ((array_key_exists('relocate',$data)) ? $data['relocate'] : null);
$relocate = ((array_key_exists('relocate', $data)) ? $data['relocate'] : null);
if(array_key_exists('channel',$data)) {
if (array_key_exists('channel', $data)) {
$max_identities = account_service_class_fetch($account_id,'total_identities');
$max_identities = account_service_class_fetch($account_id, 'total_identities');
if($max_identities !== false) {
$r = q("select channel_id from channel where channel_account_id = %d",
if ($max_identities !== false) {
$r = q("select channel_id from channel where channel_account_id = %d and channel_removed = 0",
intval($account_id)
);
if($r && count($r) > $max_identities) {
notice( sprintf( t('Your service plan only allows %d channels.'), $max_identities) . EOL);
if ($r && count($r) > $max_identities) {
notice(sprintf(t('Your service plan only allows %d channels.'), $max_identities) . EOL);
return;
}
}
if($newname) {
$x = false;
if ($newname) {
$x = false;
if(get_config('system','unicode_usernames')) {
$x = punify(mb_strtolower($newname));
}
if (get_config('system', 'unicode_usernames')) {
$x = punify(mb_strtolower($newname));
}
if((! $x) || strlen($x) > 64) {
$x = strtolower(\URLify::transliterate($newname));
if ((!$x) || strlen($x) > 64) {
$x = strtolower(URLify::transliterate($newname));
}
$newname = $x;
}
@@ -181,36 +182,36 @@ class Import extends \Zotlabs\Web\Controller {
}
else {
$moving = false;
$channel = \App::get_channel();
$channel = App::get_channel();
}
if(! $channel) {
logger('Channel not found. ', print_r($channel,true));
notice( t('No channel. Import failed.') . EOL);
if (!$channel) {
logger('Channel not found. ', print_r($channel, true));
notice(t('No channel. Import failed.') . EOL);
return;
}
if(is_array($data['config'])) {
import_config($channel,$data['config']);
if (is_array($data['config'])) {
import_config($channel, $data['config']);
}
logger('import step 2');
if(array_key_exists('channel',$data)) {
if($data['photo']) {
if (array_key_exists('channel', $data)) {
if ($data['photo']) {
require_once('include/photo/photo_driver.php');
import_channel_photo(base64url_decode($data['photo']['data']),$data['photo']['type'],$account_id,$channel['channel_id']);
import_channel_photo(base64url_decode($data['photo']['data']), $data['photo']['type'], $account_id, $channel['channel_id']);
}
if(is_array($data['profile']))
import_profiles($channel,$data['profile']);
if (is_array($data['profile']))
import_profiles($channel, $data['profile']);
}
logger('import step 3');
// create new hubloc for the new channel at this site
if(array_key_exists('channel',$data)) {
if (array_key_exists('channel', $data)) {
// create a new zot6 hubloc
@@ -223,18 +224,18 @@ class Import extends \Zotlabs\Web\Controller {
'hubloc_network' => 'zot6',
'hubloc_primary' => (($seize) ? 1 : 0),
'hubloc_url' => z_root(),
'hubloc_url_sig' => 'sha256.' . base64url_encode(Crypto::sign(z_root(),$channel['channel_prvkey'])),
'hubloc_host' => \App::get_hostname(),
'hubloc_url_sig' => Libzot::sign(z_root(), $channel['channel_prvkey']),
'hubloc_host' => App::get_hostname(),
'hubloc_callback' => z_root() . '/zot',
'hubloc_sitekey' => get_config('system','pubkey'),
'hubloc_sitekey' => get_config('system', 'pubkey'),
'hubloc_updated' => datetime_convert(),
'hubloc_id_url' => channel_url($channel),
'hubloc_site_id' => Libzot::make_xchan_hash(z_root(),get_config('system','pubkey'))
'hubloc_site_id' => Libzot::make_xchan_hash(z_root(), get_config('system', 'pubkey'))
]
);
// reset the original primary hubloc if it is being seized
if($seize) {
if ($seize) {
$r = q("update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ",
dbesc($channel['channel_hash']),
dbesc(z_root())
@@ -245,10 +246,9 @@ class Import extends \Zotlabs\Web\Controller {
logger('import step 4');
// import xchans and contact photos
if(array_key_exists('channel',$data) && $seize) {
if (array_key_exists('channel', $data) && $seize) {
// replace any existing xchan we may have on this site if we're seizing control
@@ -258,21 +258,21 @@ class Import extends \Zotlabs\Web\Controller {
$r = xchan_store_lowlevel(
[
'xchan_hash' => $channel['channel_hash'],
'xchan_guid' => $channel['channel_guid'],
'xchan_guid_sig' => $channel['channel_guid_sig'],
'xchan_pubkey' => $channel['channel_pubkey'],
'xchan_photo_l' => z_root() . "/photo/profile/l/" . $channel['channel_id'],
'xchan_photo_m' => z_root() . "/photo/profile/m/" . $channel['channel_id'],
'xchan_photo_s' => z_root() . "/photo/profile/s/" . $channel['channel_id'],
'xchan_addr' => channel_reddress($channel),
'xchan_url' => z_root() . '/channel/' . $channel['channel_address'],
'xchan_connurl' => z_root() . '/poco/' . $channel['channel_address'],
'xchan_follow' => z_root() . '/follow?f=&url=%s',
'xchan_name' => $channel['channel_name'],
'xchan_network' => 'zot6',
'xchan_photo_date' => datetime_convert(),
'xchan_name_date' => datetime_convert()
'xchan_hash' => $channel['channel_hash'],
'xchan_guid' => $channel['channel_guid'],
'xchan_guid_sig' => $channel['channel_guid_sig'],
'xchan_pubkey' => $channel['channel_pubkey'],
'xchan_photo_l' => z_root() . "/photo/profile/l/" . $channel['channel_id'],
'xchan_photo_m' => z_root() . "/photo/profile/m/" . $channel['channel_id'],
'xchan_photo_s' => z_root() . "/photo/profile/s/" . $channel['channel_id'],
'xchan_addr' => channel_reddress($channel),
'xchan_url' => z_root() . '/channel/' . $channel['channel_address'],
'xchan_connurl' => z_root() . '/poco/' . $channel['channel_address'],
'xchan_follow' => z_root() . '/follow?f=&url=%s',
'xchan_name' => $channel['channel_name'],
'xchan_network' => 'zot6',
'xchan_photo_date' => datetime_convert(),
'xchan_name_date' => datetime_convert()
]
);
@@ -282,18 +282,18 @@ class Import extends \Zotlabs\Web\Controller {
// import xchans
$xchans = $data['xchan'];
if($xchans) {
foreach($xchans as $xchan) {
if ($xchans) {
foreach ($xchans as $xchan) {
if($xchan['xchan_network'] === 'zot6') {
$zhash = Libzot::make_xchan_hash($xchan['xchan_guid'],$xchan['xchan_pubkey']);
if($zhash !== $xchan['xchan_hash']) {
logger('forged xchan: ' . print_r($xchan,true));
if ($xchan['xchan_network'] === 'zot6') {
$zhash = Libzot::make_xchan_hash($xchan['xchan_guid'], $xchan['xchan_pubkey']);
if ($zhash !== $xchan['xchan_hash']) {
logger('forged xchan: ' . print_r($xchan, true));
continue;
}
}
if(! array_key_exists('xchan_hidden',$xchan)) {
if (!array_key_exists('xchan_hidden', $xchan)) {
$xchan['xchan_hidden'] = (($xchan['xchan_flags'] & 0x0001) ? 1 : 0);
$xchan['xchan_orphan'] = (($xchan['xchan_flags'] & 0x0002) ? 1 : 0);
$xchan['xchan_censored'] = (($xchan['xchan_flags'] & 0x0004) ? 1 : 0);
@@ -306,14 +306,14 @@ class Import extends \Zotlabs\Web\Controller {
$r = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1",
dbesc($xchan['xchan_hash'])
);
if($r)
if ($r)
continue;
create_table_from_array('xchan',$xchan);
create_table_from_array('xchan', $xchan);
require_once('include/photo/photo_driver.php');
if($xchan['xchan_hash'] === $channel['channel_hash']) {
if ($xchan['xchan_hash'] === $channel['channel_hash']) {
$r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' where xchan_hash = '%s'",
dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']),
dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']),
@@ -322,13 +322,13 @@ class Import extends \Zotlabs\Web\Controller {
);
}
else {
$photos = import_xchan_photo($xchan['xchan_photo_l'],$xchan['xchan_hash']);
if($photos[4])
$photos = import_xchan_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']);
if ($photos[4])
$photodate = NULL_DATE;
else
$photodate = $xchan['xchan_photo_date'];
$r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s' where xchan_hash = '%s'",
q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s' where xchan_hash = '%s'",
dbesc($photos[0]),
dbesc($photos[1]),
dbesc($photos[2]),
@@ -345,22 +345,22 @@ class Import extends \Zotlabs\Web\Controller {
logger('import step 7');
// this must happen after xchans got imported!
if(is_array($data['hubloc'])) {
import_hublocs($channel,$data['hubloc'],$seize,$moving);
if (is_array($data['hubloc'])) {
import_hublocs($channel, $data['hubloc'], $seize, $moving);
}
$friends = 0;
$feeds = 0;
$feeds = 0;
// import contacts
$abooks = $data['abook'];
if($abooks) {
foreach($abooks as $abook) {
if ($abooks) {
foreach ($abooks as $abook) {
$abook_copy = $abook;
$abconfig = null;
if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig']))
if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && count($abook['abconfig']))
$abconfig = $abook['abconfig'];
unset($abook['abook_id']);
@@ -373,33 +373,33 @@ class Import extends \Zotlabs\Web\Controller {
$abook['abook_account'] = $account_id;
$abook['abook_channel'] = $channel['channel_id'];
if(! array_key_exists('abook_blocked',$abook)) {
$abook['abook_blocked'] = (($abook['abook_flags'] & 0x0001 ) ? 1 : 0);
$abook['abook_ignored'] = (($abook['abook_flags'] & 0x0002 ) ? 1 : 0);
$abook['abook_hidden'] = (($abook['abook_flags'] & 0x0004 ) ? 1 : 0);
$abook['abook_archived'] = (($abook['abook_flags'] & 0x0008 ) ? 1 : 0);
$abook['abook_pending'] = (($abook['abook_flags'] & 0x0010 ) ? 1 : 0);
$abook['abook_unconnected'] = (($abook['abook_flags'] & 0x0020 ) ? 1 : 0);
$abook['abook_self'] = (($abook['abook_flags'] & 0x0080 ) ? 1 : 0);
$abook['abook_feed'] = (($abook['abook_flags'] & 0x0100 ) ? 1 : 0);
if (!array_key_exists('abook_blocked', $abook)) {
$abook['abook_blocked'] = (($abook['abook_flags'] & 0x0001) ? 1 : 0);
$abook['abook_ignored'] = (($abook['abook_flags'] & 0x0002) ? 1 : 0);
$abook['abook_hidden'] = (($abook['abook_flags'] & 0x0004) ? 1 : 0);
$abook['abook_archived'] = (($abook['abook_flags'] & 0x0008) ? 1 : 0);
$abook['abook_pending'] = (($abook['abook_flags'] & 0x0010) ? 1 : 0);
$abook['abook_unconnected'] = (($abook['abook_flags'] & 0x0020) ? 1 : 0);
$abook['abook_self'] = (($abook['abook_flags'] & 0x0080) ? 1 : 0);
$abook['abook_feed'] = (($abook['abook_flags'] & 0x0100) ? 1 : 0);
}
if(array_key_exists('abook_instance',$abook) && $abook['abook_instance'] && strpos($abook['abook_instance'],z_root()) === false) {
if (array_key_exists('abook_instance', $abook) && $abook['abook_instance'] && strpos($abook['abook_instance'], z_root()) === false) {
$abook['abook_not_here'] = 1;
}
if($abook['abook_self']) {
$role = get_pconfig($channel['channel_id'],'system','permissions_role');
if(($role === 'forum') || ($abook['abook_my_perms'] & PERMS_W_TAGWALL)) {
if ($abook['abook_self']) {
$role = get_pconfig($channel['channel_id'], 'system', 'permissions_role');
if (($role === 'forum') || ($abook['abook_my_perms'] & PERMS_W_TAGWALL)) {
q("update xchan set xchan_pubforum = 1 where xchan_hash = '%s' ",
dbesc($abook['abook_xchan'])
);
}
}
else {
if($max_friends !== false && $friends > $max_friends)
if ($max_friends !== false && $friends > $max_friends)
continue;
if($max_feeds !== false && intval($abook['abook_feed']) && ($feeds > $max_feeds))
if ($max_feeds !== false && intval($abook['abook_feed']) && ($feeds > $max_feeds))
continue;
}
@@ -407,9 +407,9 @@ class Import extends \Zotlabs\Web\Controller {
dbesc($abook['abook_xchan']),
intval($channel['channel_id'])
);
if($r) {
foreach($abook as $k => $v) {
$r = q("UPDATE abook SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE abook_xchan = '%s' AND abook_channel = %d",
if ($r) {
foreach ($abook as $k => $v) {
q("UPDATE abook SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE abook_xchan = '%s' AND abook_channel = %d",
dbesc($k),
dbesc($v),
dbesc($abook['abook_xchan']),
@@ -420,17 +420,17 @@ class Import extends \Zotlabs\Web\Controller {
else {
abook_store_lowlevel($abook);
$friends ++;
if(intval($abook['abook_feed']))
$feeds ++;
$friends++;
if (intval($abook['abook_feed']))
$feeds++;
}
translate_abook_perms_inbound($channel,$abook_copy);
translate_abook_perms_inbound($channel, $abook_copy);
if($abconfig) {
if ($abconfig) {
/// @FIXME does not handle sync of del_abconfig
foreach($abconfig as $abc) {
set_abconfig($channel['channel_id'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']);
foreach ($abconfig as $abc) {
set_abconfig($channel['channel_id'], $abc['xchan'], $abc['cat'], $abc['k'], $abc['v']);
}
}
}
@@ -438,13 +438,14 @@ class Import extends \Zotlabs\Web\Controller {
logger('import step 8');
}
// import groups
$groups = $data['group'];
if($groups) {
$saved = array();
foreach($groups as $group) {
$saved[$group['hash']] = array('old' => $group['id']);
if(array_key_exists('name', $group)) {
if ($groups) {
$saved = [];
foreach ($groups as $group) {
$saved[$group['hash']] = ['old' => $group['id']];
if (array_key_exists('name', $group)) {
$group['gname'] = $group['name'];
unset($group['name']);
}
@@ -456,8 +457,8 @@ class Import extends \Zotlabs\Web\Controller {
$r = q("select * from pgrp where uid = %d",
intval($channel['channel_id'])
);
if($r) {
foreach($r as $rr) {
if ($r) {
foreach ($r as $rr) {
$saved[$rr['hash']]['new'] = $rr['id'];
}
}
@@ -465,12 +466,12 @@ class Import extends \Zotlabs\Web\Controller {
// import group members
$group_members = $data['group_member'];
if($group_members) {
foreach($group_members as $group_member) {
if ($group_members) {
foreach ($group_members as $group_member) {
unset($group_member['id']);
$group_member['uid'] = $channel['channel_id'];
foreach($saved as $x) {
if($x['old'] == $group_member['gid'])
foreach ($saved as $x) {
if ($x['old'] == $group_member['gid'])
$group_member['gid'] = $x['new'];
}
create_table_from_array('pgrp_member', $group_member);
@@ -479,59 +480,85 @@ class Import extends \Zotlabs\Web\Controller {
logger('import step 9');
if(is_array($data['obj']))
import_objs($channel,$data['obj']);
if(is_array($data['likes']))
import_likes($channel,$data['likes']);
if (is_array($data['obj']))
import_objs($channel, $data['obj']);
if(is_array($data['app']))
import_apps($channel,$data['app']);
if (is_array($data['likes']))
import_likes($channel, $data['likes']);
if(is_array($data['sysapp']))
import_sysapps($channel,$data['sysapp']);
if (is_array($data['app']))
import_apps($channel, $data['app']);
if(is_array($data['chatroom']))
import_chatrooms($channel,$data['chatroom']);
if (is_array($data['sysapp']))
import_sysapps($channel, $data['sysapp']);
if(is_array($data['event']))
import_events($channel,$data['event']);
if (is_array($data['chatroom']))
import_chatrooms($channel, $data['chatroom']);
if(is_array($data['event_item']))
import_items($channel,$data['event_item'],false,$relocate);
if (is_array($data['event']))
import_events($channel, $data['event']);
if(is_array($data['menu']))
import_menus($channel,$data['menu']);
if (is_array($data['event_item']))
import_items($channel, $data['event_item'], false, $relocate);
if(is_array($data['wiki']))
import_items($channel,$data['wiki'],false,$relocate);
if (is_array($data['menu']))
import_menus($channel, $data['menu']);
if(is_array($data['webpages']))
import_items($channel,$data['webpages'],false,$relocate);
if (is_array($data['wiki']))
import_items($channel, $data['wiki'], false, $relocate);
$addon = array('channel' => $channel,'data' => $data);
call_hooks('import_channel',$addon);
if (is_array($data['webpages']))
import_items($channel, $data['webpages'], false, $relocate);
$saved_notification_flags = notifications_off($channel['channel_id']);
$addon = ['channel' => $channel, 'data' => $data];
call_hooks('import_channel', $addon);
if($import_posts && array_key_exists('item',$data) && $data['item'])
import_items($channel,$data['item'],false,$relocate);
if ($import_posts && array_key_exists('item', $data) && $data['item']) {
import_items($channel, $data['item'], false, $relocate);
}
notifications_on($channel['channel_id'],$saved_notification_flags);
if(array_key_exists('item_id',$data) && $data['item_id'])
import_item_ids($channel,$data['item_id']);
// Immediately notify old server about the new clone
Master::Summon(['Notifier', 'refresh_all', $channel['channel_id']]);
// This will indirectly perform a refresh_all *and* update the directory
Master::Summon(['Directory', $channel['channel_id']]);
\Zotlabs\Daemon\Master::Summon(array('Directory', $channel['channel_id']));
$cf_api_compat = true;
if ($api_path && $import_posts) { // we are importing from a server and not a file
if (version_compare($data['compatibility']['version'], '6.3.4', '>=')) {
notice( t('Import completed.') . EOL);
$m = parse_url($api_path);
$hz_server = $m['scheme'] . '://' . $m['host'];
$since = datetime_convert(date_default_timezone_get(), date_default_timezone_get(), '0001-01-01 00:00');
$until = datetime_convert(date_default_timezone_get(), date_default_timezone_get(), 'now + 1 day');
$poll_interval = get_config('system', 'poll_interval', 3);
$page = 0;
Master::Summon(['Content_importer', sprintf('%d', $page), $since, $until, $channel['channel_address'], urlencode($hz_server)]);
Master::Summon(['File_importer', sprintf('%d', $page), $channel['channel_address'], urlencode($hz_server)]);
}
else {
$cf_api_compat = false;
}
}
change_channel($channel['channel_id']);
goaway(z_root() . '/network' );
if ($api_path && $import_posts && $cf_api_compat) {
goaway(z_root() . '/import_progress');
}
if (!$cf_api_compat) {
notice(t('Automatic content and files import was not possible due to API version incompatiblity. Please import content and files manually!') . EOL);
}
goaway(z_root());
}
/**
@@ -539,7 +566,7 @@ class Import extends \Zotlabs\Web\Controller {
*/
function post() {
$account_id = get_account_id();
if(! $account_id)
if (!$account_id)
return;
check_form_security_token_redirectOnErr('/import', 'channel_import');
@@ -554,33 +581,35 @@ class Import extends \Zotlabs\Web\Controller {
*/
function get() {
if(! get_account_id()) {
notice( t('You must be logged in to use this feature.') . EOL);
if (!get_account_id()) {
notice(t('You must be logged in to use this feature.') . EOL);
return '';
}
$o = replace_macros(get_markup_template('channel_import.tpl'),array(
'$title' => t('Import Channel'),
'$desc' => t('Use this form to import an existing channel from a different server/hub. You may retrieve the channel identity from the old server/hub via the network or provide an export file.'),
'$label_filename' => t('File to Upload'),
'$choice' => t('Or provide the old server/hub details'),
nav_set_selected('Channel Import');
'$old_address' => [ 'old_address', t('Your old identity address (xyz@example.com)'), '', ''],
'$email' => [ 'email', t('Your old login email address'), '', '' ],
'$password' => [ 'password', t('Your old login password'), '', '' ],
'$import_posts' => [ 'import_posts', t('Import a few months of posts if possible (limited by available memory'), false, '', [ t('No'), t('Yes') ]],
$o = replace_macros(get_markup_template('channel_import.tpl'), [
'$title' => t('Channel Import'),
'$desc' => t('Use this form to import an existing channel from a different server/hub. You may retrieve the channel identity from the old server/hub via the network or provide an export file.'),
'$label_filename' => t('File to Upload'),
'$choice' => t('Or provide the old server/hub details'),
'$old_address' => ['old_address', t('Your old identity address (xyz@example.com)'), '', ''],
'$email' => ['email', t('Your old login email address'), '', ''],
'$password' => ['password', t('Your old login password'), '', ''],
'$import_posts' => ['import_posts', t('Import your items and files (limited by available memory)'), false, '', [t('No'), t('Yes')]],
'$common' => t('For either option, please choose whether to make this hub your new primary address, or whether your old location should continue this role. You will be able to post from either location, but only one can be marked as the primary location for files, photos, and media.'),
'$make_primary' => [ 'make_primary', t('Make this hub my primary location'), false, '', [ t('No'), t('Yes') ] ],
'$moving' => [ 'moving', t('Move this channel (disable all previous locations)'), false, '', [ t('No'), t('Yes') ] ],
'$newname' => [ 'newname', t('Use this channel nickname instead of the one provided'), '', t('Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site.')],
'$make_primary' => ['make_primary', t('Make this hub my primary location'), false, '', [t('No'), t('Yes')]],
'$moving' => ['moving', t('Move this channel (disable all previous locations)'), false, '', [t('No'), t('Yes')]],
'$newname' => ['newname', t('Use this channel nickname instead of the one provided'), '', t('Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site.')],
'$pleasewait' => t('This process may take several minutes to complete. Please submit the form only once and leave this page open until finished.'),
'$form_security_token' => get_form_security_token('channel_import'),
'$submit' => t('Submit')
));
'$submit' => t('Submit')
]);
return $o;
}

View File

@@ -1,6 +1,11 @@
<?php
namespace Zotlabs\Module;
use App;
use ZipArchive;
use Zotlabs\Web\Controller;
require_once('include/import.php');
/**
@@ -8,104 +13,95 @@ require_once('include/import.php');
*
* Import existing posts and content from an export file.
*/
class Import_items extends \Zotlabs\Web\Controller {
class Import_items extends Controller {
function post() {
if(! local_channel())
if (!local_channel())
return;
check_form_security_token_redirectOnErr('/import_items', 'import_items');
$data = null;
$data = null;
$src = $_FILES['filename']['tmp_name'];
$filename = basename($_FILES['filename']['name']);
$filesize = intval($_FILES['filename']['size']);
$filetype = $_FILES['filename']['type'];
if($src) {
// This is OS specific and could also fail if your tmpdir isn't very large
// mostly used for Diaspora which exports gzipped files.
$channel = App::get_channel();
if(strpos($filename,'.gz')){
@rename($src,$src . '.gz');
@system('gunzip ' . escapeshellarg($src . '.gz'));
}
if ($src) {
if($filesize) {
$data = @file_get_contents($src);
}
unlink($src);
}
if ($filetype === 'application/zip') {
$zip = new ZipArchive;
if(! $src) {
$r = $zip->open($src);
if ($r === true) {
for ($i = 0; $i < $zip->count(); $i++) {
$data = $zip->getFromIndex($i);
self::import($channel, $data);
}
$zip->close();
unlink($src);
return;
}
$old_address = ((x($_REQUEST,'old_address')) ? $_REQUEST['old_address'] : '');
if(! $old_address) {
logger('Nothing to import.');
notice( t('Nothing to import.') . EOL);
notice(t('Not a zip file or zip file corrupted.') . EOL);
unlink($src);
return;
}
$email = ((x($_REQUEST,'email')) ? $_REQUEST['email'] : '');
$password = ((x($_REQUEST,'password')) ? $_REQUEST['password'] : '');
// This is OS specific and could also fail if your tmpdir isn't very large
// mostly used for Diaspora which exports gzipped files.
$year = ((x($_REQUEST,'year')) ? $_REQUEST['year'] : '');
//if(strpos($filename,'.gz')){
//@rename($src,$src . '.gz');
//@system('gunzip ' . escapeshellarg($src . '.gz'));
//}
$channelname = substr($old_address,0,strpos($old_address,'@'));
$servername = substr($old_address,strpos($old_address,'@')+1);
$scheme = 'https://';
$api_path = '/api/red/channel/export/items?f=&channel=' . $channelname . '&year=' . intval($year);
$binary = false;
$redirects = 0;
$opts = array('http_auth' => $email . ':' . $password);
$url = $scheme . $servername . $api_path;
$ret = z_fetch_url($url, $binary, $redirects, $opts);
if(! $ret['success'])
$ret = z_fetch_url('http://' . $servername . $api_path, $binary, $redirects, $opts);
if($ret['success'])
$data = $ret['body'];
else
notice( t('Unable to download data from old server') . EOL);
}
if(! $data) {
logger('Empty file.');
notice( t('Imported file is empty.') . EOL);
return;
}
$data = json_decode($data, true);
//logger('import: data: ' . print_r($data,true));
//print_r($data);
if(! is_array($data))
return;
if(array_key_exists('compatibility',$data) && array_key_exists('database',$data['compatibility'])) {
$v1 = substr($data['compatibility']['database'],-4);
$v2 = substr(DB_UPDATE_VERSION,-4);
if($v2 > $v1) {
$t = sprintf( t('Warning: Database versions differ by %1$d updates.'), $v2 - $v1 );
notice($t . EOL);
if ($filesize) {
$data = @file_get_contents($src);
self::import($channel, $data);
}
unlink($src);
return;
}
/*
if(! $src) {
$channel = \App::get_channel();
$old_address = ((x($_REQUEST,'old_address')) ? $_REQUEST['old_address'] : '');
if(array_key_exists('item',$data) && $data['item']) {
import_items($channel,$data['item'],false,((array_key_exists('relocate',$data)) ? $data['relocate'] : null));
}
if(! $old_address) {
logger('Nothing to import.');
notice( t('Nothing to import.') . EOL);
return;
}
if(array_key_exists('item_id',$data) && $data['item_id']) {
import_item_ids($channel,$data['item_id']);
}
$email = ((x($_REQUEST,'email')) ? $_REQUEST['email'] : '');
$password = ((x($_REQUEST,'password')) ? $_REQUEST['password'] : '');
$year = ((x($_REQUEST,'year')) ? $_REQUEST['year'] : '');
$channelname = substr($old_address,0,strpos($old_address,'@'));
$servername = substr($old_address,strpos($old_address,'@')+1);
$scheme = 'https://';
$api_path = '/api/red/channel/export/items?f=&channel=' . $channelname . '&year=' . intval($year);
$binary = false;
$redirects = 0;
$opts = array('http_auth' => $email . ':' . $password);
$url = $scheme . $servername . $api_path;
$ret = z_fetch_url($url, $binary, $redirects, $opts);
if(! $ret['success'])
$ret = z_fetch_url('http://' . $servername . $api_path, $binary, $redirects, $opts);
if($ret['success'])
$data = $ret['body'];
else
notice( t('Unable to download data from old server') . EOL);
}
*/
info( t('Import completed') . EOL);
}
@@ -116,20 +112,85 @@ class Import_items extends \Zotlabs\Web\Controller {
*/
function get() {
if(! local_channel()) {
notice( t('Permission denied') . EOL);
if (!local_channel()) {
notice(t('Permission denied') . EOL);
return login();
}
$o = replace_macros(get_markup_template('item_import.tpl'), array(
'$title' => t('Import Items'),
'$desc' => t('Use this form to import existing posts and content from an export file.'),
'$label_filename' => t('File to Upload'),
$o = replace_macros(get_markup_template('item_import.tpl'), [
'$title' => t('Import Items'),
'$desc' => t('Use this form to import existing posts and content from an export file.'),
'$label_filename' => t('File to Upload'),
'$form_security_token' => get_form_security_token('import_items'),
'$submit' => t('Submit')
));
'$submit' => t('Submit')
]);
return $o;
}
public static function import($channel, $data) {
if (!$data) {
logger('Empty file.');
notice(t('Imported file is empty.') . EOL);
return;
}
$data = json_decode($data, true);
//logger('import: data: ' . print_r($data,true));
//print_r($data);
if (!is_array($data)) {
return;
}
//if (array_key_exists('compatibility', $data) && array_key_exists('database', $data['compatibility'])) {
//$v1 = substr($data['compatibility']['database'], -4);
//$v2 = substr(DB_UPDATE_VERSION, -4);
//if ($v2 > $v1) {
//$t = sprintf(t('Warning: Database versions differ by %1$d updates.'), $v2 - $v1);
//notice($t . EOL);
//}
//}
if (array_key_exists('item', $data) && is_array($data['item'])) {
import_items($channel, $data['item'], false, ((array_key_exists('relocate', $data)) ? $data['relocate'] : null));
info(t('Content import completed') . EOL);
}
if (array_key_exists('chatroom', $data) && is_array($data['chatroom'])) {
import_chatrooms($channel, $data['chatroom']);
info(t('Chatroom import completed') . EOL);
}
if (array_key_exists('event', $data) && is_array($data['event'])) {
import_events($channel, $data['event']);
info(t('Channel calendar import 1/2 completed') . EOL);
}
if (array_key_exists('event_item', $data) && is_array($data['event_item'])) {
import_items($channel, $data['event_item'], false, ((array_key_exists('relocate', $data)) ? $data['relocate'] : null));
info(t('Channel calendar import 2/2 completed') . EOL);
}
if (array_key_exists('menu', $data) && is_array($data['menu'])) {
import_menus($channel, $data['menu']);
info(t('Menu import completed') . EOL);
}
if (array_key_exists('wiki', $data) && is_array($data['wiki'])) {
import_items($channel, $data['wiki'], false, ((array_key_exists('relocate', $data)) ? $data['relocate'] : null));
info(t('Wiki import completed') . EOL);
}
if (array_key_exists('webpages', $data) && is_array($data['webpages'])) {
import_items($channel, $data['webpages'], false, ((array_key_exists('relocate', $data)) ? $data['relocate'] : null));
info(t('Webpages import completed') . EOL);
}
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace Zotlabs\Module;
use Zotlabs\Lib\PConfig;
use Zotlabs\Daemon\Master;
class Import_progress extends \Zotlabs\Web\Controller {
function post() {
if(! local_channel())
return;
}
function get() {
if(! local_channel()) {
return;
}
nav_set_selected('Channel Import');
// items
$c = PConfig::Get(local_channel(), 'import', 'content_progress');
if ($c) {
$total_cpages = floor(intval($c['items_total']) / intval($c['items_page']));
if(!$total_cpages) {
$total_cpages = 1; // because of floor
}
$cpage = $c['last_page'] + 1; // because page count start at 0
$cprogress = intval(floor((intval($cpage) * 100) / $total_cpages));
$ccompleted_str = t('Item sync completed!');
if(argv(1) === 'resume_itemsync' && $cprogress < 100) {
Master::Summon($c['next_cmd']);
goaway('/import_progress');
}
}
else {
$cprogress = 'waiting to start...';
if (PConfig::Get(local_channel(), 'import', 'content_completed')) {
// There was nothing todo. Fake 100% and mention that there were no files found
$cprogress = 100;
}
$ccompleted_str = t('Item sync completed but no items were found!');
if(argv(1) === 'resume_itemsync') {
Master::Summon(["Content_importer","0","0001-01-01 00:00:00","2021-10-02 19:49:14","ct5","https%3A%2F%2Fhub.somaton.com"]);
goaway('/import_progress');
}
}
$cprogress_str = ((intval($cprogress)) ? $cprogress . '%' : $cprogress);
// files
$f = PConfig::Get(local_channel(), 'import', 'files_progress');
if ($f) {
$total_fpages = floor(intval($f['files_total']) / intval($f['files_page']));
if(!$total_fpages) {
$total_fpages = 1;
}
$fpage = $f['last_page'] + 1;
$fprogress = intval(floor((intval($fpage) * 100) / $total_fpages));
$fcompleted_str = t('File sync completed!');
if(argv(1) === 'resume_filesync' && $fprogress < 100) {
Master::Summon($f['next_cmd']);
goaway('/import_progress');
}
}
else {
$fprogress = 'waiting to start...';
if (PConfig::Get(local_channel(), 'import', 'files_completed')) {
// There was nothing todo. Fake 100% and mention that there were no files found
$fprogress = 100;
}
$fcompleted_str = t('File sync completed but no files were found!');
}
$fprogress_str = ((intval($fprogress)) ? $fprogress . '%' : $fprogress);
if(is_ajax()) {
$ret = [
'cprogress' => $cprogress,
'fprogress' => $fprogress
];
json_return_and_die($ret);
}
$o = replace_macros(get_markup_template("import_progress.tpl"), [
'$chtitle_str' => t('Channel clone status'),
'$ctitle_str' => t('Item sync status'),
'$ftitle_str' => t('File sync status'),
'$cprogress_str' => $cprogress_str,
'$cprogress' => intval($cprogress),
'$fprogress_str' => $fprogress_str,
'$fprogress' => intval($fprogress),
'$fcompleted_str' => $fcompleted_str,
'$ccompleted_str' => $ccompleted_str,
'$chcompleted_str' => t('Channel cloning completed!'),
'$resume_str' => t('Resume'),
'$resume_helper_str' => t('Only resume if sync stalled!')
]);
return $o;
}
}

View File

@@ -129,11 +129,11 @@ class Invite extends Controller {
if(! $recip) continue;
// see if we have an email address who@domain.tld
if (!preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $recip)) {
$feedbk .= 'ZAI0203E ' . ($n+1) . ': ' . sprintf( t('(%s) : Not a valid email address'), $recip) . $eol;
$ko++;
continue;
}
//if (!preg_match('/^.{2,64}\@[a-z0-9.-]{2,32}\.[a-z]{2,12}$/', $recip)) {
//$feedbk .= 'ZAI0203E ' . ($n+1) . ': ' . sprintf( t('(%s) : Not a valid email address'), $recip) . $eol;
//$ko++;
//continue;
//}
if(! validate_email($recip)) {
$feedbk .= 'ZAI0204E ' . ($n+1) . ': ' . sprintf( t('(%s) : Not a real email address'), $recip) . $eol;
$ko++;
@@ -225,7 +225,7 @@ class Invite extends Controller {
'$projectname' => t('$Projectname'),
'$invite_code' => $invite_code,
'$invite_where' => z_root() . '/register',
'$invite_whereami' => str_replace('@', '@+', $reonar['whereami']),
'$invite_whereami' => $reonar['whereami'],
'$invite_whoami' => z_root() . '/channel/' . $reonar['whoami'],
'$invite_anywhere' => z_root() . '/pubsites'
)
@@ -422,8 +422,6 @@ class Invite extends Controller {
// let take one descriptive for template (as said is never used)
$invite_code = 'INVITATE2020';
// what languages we use now
$lccmy = ((isset(App::$config['system']['language'])) ? App::$config['system']['language'] : 'en');
// and all the localized templates belonging to invite
$tpls = glob('view/*/invite.*.tpl');
@@ -444,6 +442,9 @@ class Invite extends Controller {
$langs = array_keys($tpla);
asort($langs);
// Use the current language if we have a template for it. Otherwise fall back to 'en'.
$lccmy = ((in_array(App::$language, $langs)) ? App::$language : 'en');
$tplx = array_unique($tplx);
asort($tplx);

File diff suppressed because it is too large Load Diff

View File

@@ -91,6 +91,12 @@ class Like extends Controller {
'id' => $arr['item']['id'],
'html' => conversation($items, $conv_mode, true, $page_mode),
];
// mod photos
if (isset($_REQUEST['reload']) && $_REQUEST['reload']) {
$ret['reload'] = 1;
}
return $ret;
}

View File

@@ -29,6 +29,9 @@ class Linkinfo extends \Zotlabs\Web\Controller {
if((substr($url,0,1) != '/') && (substr($url,0,4) != 'http'))
$url = 'http://' . $url;
$x = parse_url($url);
if ($x)
$url = str_replace($x['host'], punify($x['host']), $url);
if($_GET['title'])
$title = strip_tags(trim($_GET['title']));

View File

@@ -1,21 +1,30 @@
<?php
namespace Zotlabs\Module;
use Zotlabs\Lib\AccessList;
use Zotlabs\Web\Controller;
require_once('include/security.php');
class Lockview extends \Zotlabs\Web\Controller {
class Lockview extends Controller {
function get() {
$atokens = array();
$atokens = [];
$atoken_xchans = [];
$access_list = [];
$guest_access_list = [];
if(local_channel()) {
if (local_channel()) {
$at = q("select * from atoken where atoken_uid = %d",
intval(local_channel())
);
if($at) {
foreach($at as $t) {
$atokens[] = atoken_xchan($t);
if ($at) {
foreach ($at as $t) {
$atoken_xchan = atoken_xchan($t);
$atokens[] = array_merge($t, $atoken_xchan);
$atoken_xchans[] = $atoken_xchan['xchan_hash'];
}
}
}
@@ -23,20 +32,20 @@ class Lockview extends \Zotlabs\Web\Controller {
$type = ((argc() > 1) ? argv(1) : 0);
if (is_numeric($type)) {
$item_id = intval($type);
$type='item';
$type = 'item';
}
else {
$item_id = ((argc() > 2) ? intval(argv(2)) : 0);
}
if(! $item_id)
if (!$item_id)
killme();
if (! in_array($type, array('item', 'photo', 'attach', 'event', 'menu_item', 'chatroom')))
if (!in_array($type, ['item', 'photo', 'attach', 'menu_item', 'chatroom']))
killme();
// we have different naming in in menu_item table and chatroom table
switch($type) {
switch ($type) {
case 'menu_item':
$id = 'mitem_id';
break;
@@ -53,134 +62,177 @@ class Lockview extends \Zotlabs\Web\Controller {
intval($item_id)
);
if(! $r)
if (!$r)
killme();
$item = $r[0];
$uid = null;
$url = '';
//we have different naming in in menu_item table and chatroom table
switch($type) {
switch ($type) {
case 'menu_item':
$uid = $item['mitem_channel_id'];
break;
case 'chatroom':
$uid = $item['cr_uid'];
$uid = $item['cr_uid'];
$channel = channelx_by_n($uid);
$url = z_root() . '/chat/' . $channel['channel_address'] . '/' . $item['cr_id'];
break;
case 'item':
$uid = $item['uid'];
$url = $item['plink'];
break;
case 'photo':
$uid = $item['uid'];
$channel = channelx_by_n($uid);
$url = z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $item['resource_id'];
break;
case 'attach':
$uid = $item['uid'];
$channel = channelx_by_n($uid);
$url = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $item['display_path'];
break;
default:
$uid = $item['uid'];
break;
}
if($uid != local_channel()) {
echo '<div class="dropdown-item">' . t('Remote privacy information not available.') . '</div>';
if (intval($uid) !== local_channel()) {
echo '<div class="dropdown-item-text">' . t('Remote privacy information not available') . '</div>';
killme();
}
if(intval($item['item_private']) && (! strlen($item['allow_cid'])) && (! strlen($item['allow_gid']))
&& (! strlen($item['deny_cid'])) && (! strlen($item['deny_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>';
echo '<div class="dropdown-item-text">' . translate_scope((!$item['public_policy']) ? 'specific' : $item['public_policy']) . '</div>';
killme();
}
$allowed_users = expand_acl($item['allow_cid']);
$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']);
$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);
stringify_array_elms($allowed_groups, true);
stringify_array_elms($allowed_users, true);
stringify_array_elms($deny_groups, true);
stringify_array_elms($deny_users, true);
$allowed_xchans = [];
$profile_groups = [];
if($allowed_groups) {
foreach($allowed_groups as $g) {
if(substr($g,0,4) === '\'vp.') {
$profile_groups[] = '\'' . substr($g,4);
if ($allowed_groups) {
foreach ($allowed_groups as $g) {
if (substr($g, 0, 4) === '\'vp.') {
$profile_groups[] = '\'' . substr($g, 4);
}
}
}
if(count($profile_groups)) {
$r = q("SELECT profile_name FROM profile WHERE profile_guid IN ( " . implode(', ', $profile_groups) . " )");
if($r)
foreach($r as $rr)
$l[] = '<div class="dropdown-item"><b>' . t('Profile','acl') . ' ' . $rr['profile_name'] . '</b></div>';
if ($profile_groups) {
$r = q("SELECT id, profile_name FROM profile WHERE profile_guid IN ( " . implode(', ', $profile_groups) . " )");
if ($r) {
foreach ($r as $rr) {
$pgrp_members = AccessList::profile_members_xchan($uid, $rr['id']);
$allowed_xchans = array_merge($allowed_xchans, $pgrp_members);
$access_list[] = '<div class="dropdown-item-text" title="' . t('Profile', 'acl') . '">' . $rr['profile_name'] . '</div>';
}
}
}
if(count($allowed_groups)) {
$r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $allowed_groups) . " )");
if($r)
foreach($r as $rr)
$l[] = '<div class="dropdown-item"><b>' . $rr['gname'] . '</b></div>';
if ($allowed_groups) {
$r = q("SELECT id, gname FROM pgrp WHERE hash IN ( " . implode(', ', $allowed_groups) . " )");
if ($r) {
foreach ($r as $rr) {
$pgrp_members = AccessList::members_xchan($uid, $rr['id']);
$allowed_xchans = array_merge($allowed_xchans, $pgrp_members);
$access_list[] = '<div class="dropdown-item-text" title="' . t('Privacy group') . '">' . $rr['gname'] . '</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)
$l[] = '<div class="dropdown-item">' . $rr['xchan_name'] . '</div>';
if($atokens) {
foreach($atokens as $at) {
if(in_array("'" . $at['xchan_hash'] . "'",$allowed_users)) {
$l[] = '<div class="dropdown-item">' . $at['xchan_name'] . '</div>';
if ($allowed_users) {
$r = q("SELECT xchan_name, xchan_hash FROM xchan WHERE xchan_hash IN ( " . implode(', ', $allowed_users) . " )");
if ($r) {
foreach ($r as $rr) {
$allowed_xchans[] = $rr['xchan_hash'];
if (!in_array($rr['xchan_hash'], $atoken_xchans)) {
$access_list[] = '<div class="dropdown-item-text">' . $rr['xchan_name'] . '</div>';
}
}
}
}
$profile_groups = [];
if($deny_groups) {
foreach($deny_groups as $g) {
if(substr($g,0,4) === '\'vp.') {
$profile_groups[] = '\'' . substr($g,4);
if ($deny_groups) {
foreach ($deny_groups as $g) {
if (substr($g, 0, 4) === '\'vp.') {
$profile_groups[] = '\'' . substr($g, 4);
}
}
}
if(count($profile_groups)) {
if ($profile_groups) {
$r = q("SELECT profile_name FROM profile WHERE profile_guid IN ( " . implode(', ', $profile_groups) . " )");
if($r)
foreach($r as $rr)
$l[] = '<div class="dropdown-item"><b><strike>' . t('Profile','acl') . ' ' . $rr['profile_name'] . '</strike></b></div>';
if ($r) {
foreach ($r as $rr) {
$access_list[] = '<div class="dropdown-item-text" title="' . t('Profile', 'acl') . '"><strike>' . $rr['profile_name'] . '</strike></b></div>';
}
}
}
if(count($deny_groups)) {
if ($deny_groups) {
$r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $deny_groups) . " )");
if($r)
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)
$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)) {
$l[] = '<div class="dropdown-item"><strike>' . $at['xchan_name'] . '</strike></div>';
}
if ($r) {
foreach ($r as $rr) {
$access_list[] = '<div class="dropdown-item-text" title="' . t('Privacy group') . '"><strike>' . $rr['gname'] . '</strike></b></div>';
}
}
}
echo $o . implode($l);
killme();
if ($deny_users) {
$r = q("SELECT xchan_name FROM xchan WHERE xchan_hash IN ( " . implode(', ', $deny_users) . " )");
if ($r) {
foreach ($r as $rr) {
$access_list[] = '<div class="dropdown-item-text"><strike>' . $rr['xchan_name'] . '</strike></div>';
}
}
}
if ($atokens && $allowed_xchans && $url) {
$guest_access_list = [];
$allowed_xchans = array_unique($allowed_xchans);
foreach ($atokens as $atoken) {
if (in_array($atoken['xchan_hash'], $allowed_xchans)) {
$guest_access_list[] = '<div class="dropdown-item d-flex justify-content-between cursor-pointer" title="' . sprintf(t('Click to copy link to this ressource for guest %s to clipboard'), $atoken['xchan_name']) . '" data-token="' . $url . '?zat=' . $atoken['atoken_token'] . '" onclick="navigator.clipboard.writeText(this.dataset.token); $.jGrowl(\'' . t('Link copied') . '\', { sticky: false, theme: \'info\', life: 1000 });"><span>' . $atoken['xchan_name'] . '</span><i class="fa fa-copy p-1"></i></div>';
}
}
}
$access_list_header = '';
if ($access_list) {
$access_list_header = '<div class="dropdown-header text-uppercase h6">' . t('Access') . '</div>';
}
$guest_access_list_header = '';
if ($guest_access_list) {
$guest_access_list_header = '<div class="dropdown-header text-uppercase h6">' . t('Guest access') . '</div>';
}
$divider = '';
if ($access_list && $guest_access_list) {
$divider = '<div class="dropdown-divider"></div>';
}
echo $access_list_header . implode($access_list) . $divider . $guest_access_list_header . implode($guest_access_list);
killme();
}

View File

@@ -116,11 +116,6 @@ class Locs extends Controller {
return;
}
for($x = 0; $x < count($r); $x ++) {
$r[$x]['primary'] = (intval($r[$x]['hubloc_primary']) ? true : false);
$r[$x]['deleted'] = (intval($r[$x]['hubloc_deleted']) ? true : false);
}
$o = replace_macros(get_markup_template('locmanage.tpl'), array(
'$header' => t('Manage Channel Locations'),
'$loc' => t('Location'),
@@ -132,7 +127,8 @@ class Locs extends Controller {
'$sync_text' => t('Please wait several minutes between consecutive operations.'),
'$drop_text' => t('When possible, drop a location by logging into that website/hub and removing your channel.'),
'$last_resort' => t('Use this form to drop the location if the hub is no longer operating.'),
'$hubs' => $r
'$hubs' => $r,
'$base_url' => z_root()
));
return $o;

View File

@@ -61,7 +61,7 @@ class Manage extends \Zotlabs\Web\Controller {
$channels[$x]['default'] = (($channels[$x]['channel_id'] == $account['account_default_channel']) ? "1" : '');
$channels[$x]['default_links'] = '1';
/* this is not currently implemented in the UI and probably should not (performance)
$c = q("SELECT id, item_wall FROM item
WHERE item_unseen = 1 and uid = %d " . item_normal(),
intval($channels[$x]['channel_id'])
@@ -75,7 +75,7 @@ class Manage extends \Zotlabs\Web\Controller {
$channels[$x]['network'] ++;
}
}
*/
$intr = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ",
intval($channels[$x]['channel_id'])
@@ -84,6 +84,7 @@ class Manage extends \Zotlabs\Web\Controller {
if($intr)
$channels[$x]['intros'] = intval($intr[0]['total']);
/* this is not currently implemented in the UI and probably should not (performance)
$events = q("SELECT etype, dtstart, adjust FROM event
WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0
ORDER BY dtstart ASC ",
@@ -116,6 +117,7 @@ class Manage extends \Zotlabs\Web\Controller {
}
}
}
*/
}
}

View File

@@ -4,11 +4,15 @@ namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\System;
use Zotlabs\Render\Theme;
class Manifest extends Controller {
function init() {
// populate App::$theme_info
Theme::current();
$ret = [
'name' => ucfirst(System::get_platform_name()),
'short_name' => ucfirst(System::get_platform_name()),
@@ -18,15 +22,16 @@ class Manifest extends Controller {
[ 'src' => '/images/app/hz-128.png', 'sizes' => '128x128', 'type' => 'image/png' ],
[ 'src' => '/images/app/hz-144.png', 'sizes' => '144x144', 'type' => 'image/png' ],
[ 'src' => '/images/app/hz-152.png', 'sizes' => '152x152', 'type' => 'image/png' ],
[ 'src' => '/images/app/hz-192.png', 'sizes' => '192x192', 'type' => 'image/png' ],
[ 'src' => '/images/app/hz-192.png', 'sizes' => '192x192', 'type' => 'image/png', 'purpose' => 'any maskable' ],
[ 'src' => '/images/app/hz-348.png', 'sizes' => '384x384', 'type' => 'image/png' ],
[ 'src' => '/images/app/hz-512.png', 'sizes' => '512x512', 'type' => 'image/png' ],
[ 'src' => '/images/app/hz.svg', 'sizes' => '64x64', 'type' => 'image/xml+svg' ]
],
'theme_color' => App::$theme_info['theme_color'],
'background_color' => App::$theme_info['background_color'],
'scope' => '/',
'start_url' => z_root(),
'display' => 'standalone',
'orientation' => 'any',
'share_target' => [
'action' => '/rpost',
'method' => 'POST',

View File

@@ -1,12 +1,11 @@
<?php
namespace Zotlabs\Module;
use Zotlabs\Lib\Group;
use Zotlabs\Lib\AccessList;
use Zotlabs\Lib\Apps;
use App;
require_once('include/items.php');
require_once('include/group.php');
require_once('include/contact_widgets.php');
require_once('include/conversation.php');
require_once('include/acl_selectors.php');
@@ -233,7 +232,7 @@ class Network extends \Zotlabs\Web\Controller {
if($group) {
$contact_str = '';
$contacts = group_get_members($group);
$contacts = AccessList::members(local_channel(), $group);
if($contacts) {
$contact_str = ids_to_querystr($contacts, 'xchan', true);
}
@@ -246,7 +245,7 @@ class Network extends \Zotlabs\Web\Controller {
$item_thread_top = '';
$sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND (( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str )) or allow_gid like '" . protect_sprintf('%<' . dbesc($group_hash) . '>%') . "' ) and id = parent $item_normal ) ";
$x = group_rec_byhash(local_channel(), $group_hash);
$x = AccessList::by_hash(local_channel(), $group_hash);
if($x) {
$title = replace_macros(get_markup_template('section_title.tpl'), array(

View File

@@ -1,6 +1,8 @@
<?php
namespace Zotlabs\Module;
use URLify;
require_once('include/channel.php');
require_once('include/permissions.php');
@@ -13,7 +15,6 @@ class New_channel extends \Zotlabs\Web\Controller {
$cmd = ((argc() > 1) ? argv(1) : '');
if($cmd === 'autofill.json') {
require_once('library/urlify/URLify.php');
$result = array('error' => false, 'message' => '');
$n = trim($_REQUEST['name']);
@@ -24,7 +25,7 @@ class New_channel extends \Zotlabs\Web\Controller {
}
if((! $x) || strlen($x) > 64)
$x = strtolower(\URLify::transliterate($n));
$x = strtolower(URLify::transliterate($n));
$test = array();
@@ -46,7 +47,6 @@ class New_channel extends \Zotlabs\Web\Controller {
}
if($cmd === 'checkaddr.json') {
require_once('library/urlify/URLify.php');
$result = array('error' => false, 'message' => '');
$n = trim($_REQUEST['nick']);
if(! $n) {
@@ -60,7 +60,7 @@ class New_channel extends \Zotlabs\Web\Controller {
}
if((! $x) || strlen($x) > 64)
$x = strtolower(\URLify::transliterate($n));
$x = strtolower(URLify::transliterate($n));
$test = array();
@@ -138,7 +138,7 @@ class New_channel extends \Zotlabs\Web\Controller {
intval($aid)
);
if($r && (! intval($r[0]['total']))) {
$default_role = get_config('system','default_permissions_role','social');
$default_role = get_config('system','default_permissions_role','personal');
}
$limit = account_service_class_fetch(get_account_id(),'total_identities');
@@ -170,12 +170,12 @@ class New_channel extends \Zotlabs\Web\Controller {
$privacy_role = ((x($_REQUEST,'permissions_role')) ? $_REQUEST['permissions_role'] : "" );
$perm_roles = \Zotlabs\Access\PermissionRoles::roles();
$perm_roles = \Zotlabs\Access\PermissionRoles::channel_roles();
$name = array('name', t('Channel name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), $name_help, "*");
$nickhub = '@' . \App::get_hostname();
$nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), $nick_help, "*");
$role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role compatible with your usage needs and privacy requirements.') . '<br>' . '<a href="help/member/member_guide#Channel_Permission_Roles" target="_blank">' . t('Read more about channel permission roles') . '</a>',$perm_roles);
$role = array('permissions_role' , t('Channel role'), ($privacy_role) ? $privacy_role : 'personal', '', $perm_roles);
$o = replace_macros(get_markup_template('new_channel.tpl'), array(
'$title' => t('Create a Channel'),

View File

@@ -19,7 +19,12 @@ class Notes extends Controller {
if(! Apps::system_app_installed(local_channel(), 'Notes'))
return EMPTY_STR;
$ret = array('success' => true);
$ret = [
'success' => false,
'html' => ''
];
if(array_key_exists('note_text',$_REQUEST)) {
$body = escape_tags($_REQUEST['note_text']);
@@ -33,6 +38,10 @@ class Notes extends Controller {
set_pconfig(local_channel(),'notes','text.bak',$old_text);
}
set_pconfig(local_channel(),'notes','text',$body);
$ret['html'] = bbcode($body);
$ret['success'] = true;
}
// push updates to channel clones

View File

@@ -1,19 +1,35 @@
<?php
namespace Zotlabs\Module;
use \Zotlabs\Lib\PConfig;
use \Zotlabs\Web\Controller;
class Notify extends \Zotlabs\Web\Controller {
class Notify extends Controller {
function init() {
if(! local_channel())
return;
if($_REQUEST['notify_id']) {
q("update notify set seen = 1 where id = %d and uid = %d",
intval($_REQUEST['notify_id']),
intval(local_channel())
);
$update_notices_per_parent = PConfig::Get(local_channel(), 'system', 'update_notices_per_parent', 1);
if($update_notices_per_parent) {
$r = q("SELECT parent FROM notify WHERE id = %d AND uid = %d",
intval($_REQUEST['notify_id']),
intval(local_channel())
);
q("update notify set seen = 1 where parent = '%s' and uid = %d",
dbesc($r[0]['parent']),
intval(local_channel())
);
}
else {
q("update notify set seen = 1 where id = %d and uid = %d",
intval($_REQUEST['notify_id']),
intval(local_channel())
);
}
killme();
}

124
Zotlabs/Module/Outbox.php Normal file
View File

@@ -0,0 +1,124 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\Config;
use Zotlabs\Lib\ThreadListener;
use Zotlabs\Web\Controller;
use Zotlabs\Web\HTTPSig;
class Outbox extends Controller {
function init() {
if (ActivityStreams::is_as_request()) {
if (observer_prohibited(true)) {
killme();
}
$channel = channelx_by_nick(argv(1));
if (!$channel) {
killme();
}
if (intval($channel['channel_system'])) {
killme();
}
$sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR);
if ($sigdata['portable_id'] && $sigdata['header_valid']) {
$portable_id = $sigdata['portable_id'];
if (!check_channelallowed($portable_id)) {
http_status_exit(403, 'Permission denied');
}
if (!check_siteallowed($sigdata['signer'])) {
http_status_exit(403, 'Permission denied');
}
observer_auth($portable_id);
}
elseif (Config::get('system', 'require_authenticated_fetch', false)) {
http_status_exit(403, 'Permission denied');
}
$observer_hash = get_observer_hash();
$params = [];
$params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
$params['end'] = ((x($_REQUEST, 'date_end')) ? $_REQUEST['date_end'] : '');
$params['type'] = 'json';
$params['pages'] = ((x($_REQUEST, 'pages')) ? intval($_REQUEST['pages']) : 0);
$params['top'] = ((x($_REQUEST, 'top')) ? intval($_REQUEST['top']) : 0);
$params['direction'] = ((x($_REQUEST, 'direction')) ? dbesc($_REQUEST['direction']) : 'desc'); // unimplemented
$params['cat'] = ((x($_REQUEST, 'cat')) ? escape_tags($_REQUEST['cat']) : '');
$params['compat'] = 1;
$total = items_fetch(
[
'total' => true,
'wall' => 1,
'datequery' => $params['end'],
'datequery2' => $params['begin'],
'direction' => dbesc($params['direction']),
'pages' => $params['pages'],
'order' => dbesc('post'),
'top' => $params['top'],
'cat' => $params['cat'],
'compat' => $params['compat']
], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module
);
if ($total) {
App::set_pager_total($total);
App::set_pager_itemspage(30);
}
if (App::$pager['unset'] && $total > 30) {
$ret = Activity::paged_collection_init($total, App::$query_string);
}
else {
$items = items_fetch(
[
'wall' => 1,
'datequery' => $params['end'],
'datequery2' => $params['begin'],
'records' => intval(App::$pager['itemspage']),
'start' => intval(App::$pager['start']),
'direction' => dbesc($params['direction']),
'pages' => $params['pages'],
'order' => dbesc('post'),
'top' => $params['top'],
'cat' => $params['cat'],
'compat' => $params['compat']
], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module
);
if ($items && $observer_hash) {
// check to see if this observer is a connection. If not, register any items
// belonging to this channel for notification of deletion/expiration
$x = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'",
intval($channel['channel_id']),
dbesc($observer_hash)
);
if (!$x) {
foreach ($items as $item) {
if (strpos($item['mid'], z_root()) === 0) {
ThreadListener::store($item['mid'], $observer_hash);
}
}
}
}
$ret = Activity::encode_item_collection($items, App::$query_string, 'OrderedCollection', $total);
}
as_return_and_die($ret, $channel);
}
}
}

View File

@@ -3,49 +3,156 @@
namespace Zotlabs\Module;
use App;
use Zotlabs\Access\PermissionLimits;
use Zotlabs\Access\Permissions;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Apps;
use Zotlabs\Lib\Libsync;
use Zotlabs\Lib\AccessList;
use Zotlabs\Lib\Permcat;
class Permcats extends Controller {
function post() {
if(! local_channel())
return;
if(! Apps::system_app_installed(local_channel(), 'Permission Categories'))
if (!local_channel())
return;
$channel = App::get_channel();
check_form_security_token_redirectOnErr('/permcats', 'permcats');
$name = escape_tags(trim($_REQUEST['name']));
$is_system_role = isset($_REQUEST['is_system_role']);
$return_path = z_root() . '/permcats/' . $_REQUEST['return_path'];
$group_hash = $_REQUEST['group_select'] ?? '';
$deleted_role = $_REQUEST['deleted_role'] ?? '';
$new_role = $_REQUEST['new_role'] ?? '';
$contacts = [];
$all_perms = \Zotlabs\Access\Permissions::Perms();
$name = escape_tags(trim($_POST['name']));
if(! $name) {
notice( t('Permission category name is required.') . EOL);
if (argv(1) && hex2bin(argv(1)) !== $name) {
$return_path = z_root() . '/permcats/' . bin2hex($name);
}
if ($deleted_role && $new_role) {
$r = q("SELECT abook_xchan FROM abook WHERE abook_channel = %d AND abook_role = '%s' AND abook_self = 0 AND abook_pending = 0",
intval(local_channel()),
dbesc($deleted_role)
);
if ($r) {
$contacts = ids_to_array($r, 'abook_xchan');
}
if ($contacts) {
Permcat::assign($channel, $new_role, $contacts);
}
Permcat::delete(local_channel(), $deleted_role);
$default_role = get_pconfig(local_channel(), 'system', 'default_permcat', 'default');
if ($deleted_role === $default_role) {
set_pconfig(local_channel(), 'system', 'default_permcat', $new_role);
}
Libsync::build_sync_packet();
info(t('Contact role deleted.') . EOL);
goaway(z_root() . '/permcats/' . bin2hex($new_role));
return;
}
if ($group_hash === 'all_contacts') {
$r = q("SELECT abook_xchan FROM abook WHERE abook_channel = %d and abook_self = 0 and abook_pending = 0",
intval(local_channel())
);
$pcarr = [];
if ($r) {
$contacts = ids_to_array($r, 'abook_xchan');
}
}
if($all_perms) {
foreach($all_perms as $perm => $desc) {
if(array_key_exists('perms_' . $perm, $_POST)) {
$group = null;
if (!$contacts && $group_hash) {
$group = AccessList::by_hash(local_channel(), $group_hash);
}
if ($group) {
$contacts = AccessList::members_xchan(local_channel(), $group['id']);
}
if (!$name) {
notice(t('Permission category name is required.') . EOL);
return;
}
set_pconfig(local_channel(), 'system', 'default_permcat', 'default');
if (isset($_REQUEST['default_role'])) {
set_pconfig(local_channel(), 'system', 'default_permcat', $name);
}
if ($is_system_role) {
// if we have a system role just set the default and assign if aplicable and be done with it
if ($contacts) {
Permcat::assign($channel, $name, $contacts);
}
info(t('Contact role saved.') . EOL);
Libsync::build_sync_packet();
goaway($return_path);
return;
}
$pcarr = [];
$all_perms = Permissions::Perms();
if ($all_perms) {
foreach ($all_perms as $perm => $desc) {
if (array_key_exists('perms_' . $perm, $_POST)) {
$pcarr[] = $perm;
}
}
}
\Zotlabs\Lib\Permcat::update(local_channel(),$name,$pcarr);
$pcat = new Permcat(local_channel());
$pcatlist = $pcat->listing();
$existing_raw_perms = [];
if ($pcatlist) {
foreach ($pcatlist as $pc) {
if ($pc['name'] && ($pc['name'] === $name)) {
$existing_raw_perms = $pc['raw_perms'];
}
}
}
if (!$contacts && array_diff_assoc($existing_raw_perms, Permissions::FilledPerms($pcarr))) {
// If we don't have anyone to assign the role to and an existing role has changed,
// we will re-assign the changed role to all its members if there are any.
$r = q("SELECT abook_xchan FROM abook WHERE abook_channel = %d AND abook_role = '%s' AND abook_self = 0 AND abook_pending = 0",
intval(local_channel()),
dbesc($name)
);
if ($r) {
$contacts = ids_to_array($r, 'abook_xchan');
}
}
Permcat::update(local_channel(), $name, $pcarr);
if ($contacts) {
Permcat::assign($channel, $name, $contacts);
}
Libsync::build_sync_packet();
info( t('Permission category saved.') . EOL);
info(t('Contact role saved.') . EOL);
goaway($return_path);
return;
}
@@ -53,79 +160,107 @@ class Permcats extends Controller {
function get() {
if(! local_channel())
return;
if (!local_channel())
return EMPTY_STR;
if(! Apps::system_app_installed(local_channel(), 'Permission Categories')) {
//Do not display any associated widgets at this point
App::$pdl = '';
$papp = Apps::get_papp('Permission Categories');
return Apps::app_render($papp, 'module');
}
nav_set_selected('Contact Roles');
$channel = App::get_channel();
if(argc() > 1)
$name = '';
if (argc() > 1) {
$name = hex2bin(argv(1));
if(argc() > 2 && argv(2) === 'drop') {
\Zotlabs\Lib\Permcat::delete(local_channel(),$name);
Libsync::build_sync_packet();
json_return_and_die([ 'success' => true ]);
}
$perms = [];
$existing = [];
$pcat = new Permcat(local_channel());
$pcatlist = $pcat->listing();
$is_system_role = false;
$delete_role_select_options = [];
$is_default_role = (get_pconfig(local_channel(), 'system', 'default_permcat', 'default') === $name);
$localname = '';
$desc = t('Use this form to create permission rules for various classes of people or connections.');
$existing = [];
$pcat = new \Zotlabs\Lib\Permcat(local_channel());
$pcatlist = $pcat->listing();
$permcats = [];
if($pcatlist) {
foreach($pcatlist as $pc) {
if(($pc['name']) && ($name) && ($pc['name'] == $name))
if ($pcatlist) {
foreach ($pcatlist as $pc) {
if ($pc['name'] && $name && ($pc['name'] === $name)) {
$existing = $pc['perms'];
if(! $pc['system'])
$permcats[bin2hex($pc['name'])] = $pc['localname'];
if (isset($pc['system']) && intval($pc['system']))
$is_system_role = $pc['name'];
}
if ($pc['name'] == $name) {
$localname = $pc['localname'];
}
if ($pc['name'] !== $name) {
$delete_role_select_options[$pc['name']] = $pc['localname'];
}
}
}
$global_perms = \Zotlabs\Access\Permissions::Perms();
// select for delete action
$delete_role_select = [
'new_role',
(($is_default_role) ? t('Role to assign affected contacts and default role to') : t('Role to assign affected contacts to')),
'',
'',
$delete_role_select_options
];
foreach($global_perms as $k => $v) {
$thisperm = \Zotlabs\Lib\Permcat::find_permcat($existing,$k);
$checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$k);
$global_perms = Permissions::Perms();
if($existing[$k])
$thisperm = "1";
foreach ($global_perms as $k => $v) {
$thisperm = Permcat::find_permcat($existing, $k);
$checkinherited = PermissionLimits::Get(local_channel(), $k);
$perms[] = array('perms_' . $k, $v, '',$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited);
if ($existing[$k])
$thisperm = 1;
$perms[] = [
'perms_' . $k,
$v,
'',
$thisperm,
1,
(($checkinherited & PERMS_SPECIFIC) ? '' : '1'),
'',
$checkinherited
];
}
$group_select_options = [
'selected' => '',
'form_id' => 'group_select',
'label' => t('Assign this role to'),
'after' => [
'name' => t('All my contacts'),
'id' => 'all_contacts',
'selected' => false
]
];
$group_select = AccessList::select(local_channel(), $group_select_options);
$tpl = get_markup_template("permcats.tpl");
$o .= replace_macros($tpl, array(
$o = replace_macros($tpl, [
'$form_security_token' => get_form_security_token("permcats"),
'$title' => t('Permission Categories'),
'$desc' => $desc,
'$desc2' => $desc2,
'$tokens' => $t,
'$permcats' => $permcats,
'$atoken' => $atoken,
'$url1' => z_root() . '/channel/' . $channel['channel_address'],
'$url2' => z_root() . '/photos/' . $channel['channel_address'],
'$name' => array('name', t('Permission category name') . ' <span class="required">*</span>', (($name) ? $name : ''), ''),
'$me' => t('My Settings'),
'$perms' => $perms,
'$inherited' => t('inherited'),
'$notself' => 0,
'$self' => 1,
'$permlbl' => t('Individual Permissions'),
'$permnote' => t('Some permissions may be inherited from your channel\'s <a href="settings"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can <strong>not</strong> change those settings here.'),
'$submit' => t('Submit')
));
'$default_role' => ['default_role', t('Automatically assign this role to new contacts'), intval($is_default_role), '', [t('No'), t('Yes')]],
'$title' => t('Contact Roles'),
'$name' => ['name', t('Role name') . ' <span class="required">*</span>', (($localname) ? $localname : ''), (($is_system_role) ? t('System role - not editable') : ''), '', (($is_system_role) ? 'disabled' : '')],
'$delete_label' => t('Deleting') . ' ' . $localname,
'$current_role' => $name,
'$perms' => $perms,
'$inherited' => t('inherited'),
'$is_system_role' => $is_system_role,
'$permlbl' => t('Role Permissions'),
'$permnote' => t('Some permissions may be inherited from your <a href="settings">channel role</a>, which have higher priority than contact role settings.'),
'$submit' => t('Submit'),
'$return_path' => argv(1),
'$group_select' => $group_select,
'$delete_role_select' => $delete_role_select,
'$delet_role_button' => t('Delete')
]);
return $o;
}

View File

@@ -3,6 +3,12 @@
namespace Zotlabs\Module;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Web\HTTPSig;
use Zotlabs\Lib\Config;
require_once('include/security.php');
require_once('include/attach.php');
require_once('include/photo/photo_driver.php');
@@ -11,6 +17,48 @@ class Photo extends \Zotlabs\Web\Controller {
function init() {
if (ActivityStreams::is_as_request()) {
$sigdata = HTTPSig::verify(EMPTY_STR);
if ($sigdata['portable_id'] && $sigdata['header_valid']) {
$portable_id = $sigdata['portable_id'];
if (! check_channelallowed($portable_id)) {
http_status_exit(403, 'Permission denied');
}
if (! check_siteallowed($sigdata['signer'])) {
http_status_exit(403, 'Permission denied');
}
observer_auth($portable_id);
}
elseif (Config::get('system','require_authenticated_fetch',false)) {
http_status_exit(403,'Permission denied');
}
$observer_xchan = get_observer_hash();
$allowed = false;
$bear = Activity::token_from_request();
if ($bear) {
logger('bear: ' . $bear, LOGGER_DEBUG);
}
$r = q("select * from item where resource_type = 'photo' and resource_id = '%s' limit 1",
dbesc(argv(1))
);
if ($r) {
$allowed = attach_can_view($r[0]['uid'],$observer_xchan,argv(1)/*,$bear*/);
}
if (! $allowed) {
http_status_exit(404,'Permission denied.');
}
$channel = channelx_by_n($r[0]['uid']);
$obj = json_decode($r[0]['obj'],true);
as_return_and_die($obj,$channel);
}
$streaming = null;
$channel = null;
$person = 0;
@@ -33,19 +81,19 @@ class Photo extends \Zotlabs\Web\Controller {
$cache_mode = [ 'on' => false, 'age' => 86400, 'exp' => true, 'leak' => false ];
call_hooks('cache_mode_hook', $cache_mode);
$observer_xchan = get_observer_hash();
$cachecontrol = ', no-cache';
if(isset($type)) {
/**
* Profile photos - Access controls on default profile photos are not honoured since they need to be exchanged with remote sites.
*
*
*/
$default = get_default_profile_photo();
if($type === 'profile') {
switch($res) {
case 'm':
@@ -62,9 +110,9 @@ class Photo extends \Zotlabs\Web\Controller {
break;
}
}
$uid = $person;
$data = '';
if ($uid > 0) {
@@ -81,13 +129,13 @@ 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'];
$uid = $d['channel_id'];
$default = $d['default'];
$data = $d['data'];
$mimetype = $d['mimetype'];
@@ -105,11 +153,11 @@ class Photo extends \Zotlabs\Web\Controller {
$cachecontrol .= ', must-revalidate';
}
else {
/**
* Other photos
*/
/* Check for a cookie to indicate display pixel density, in order to detect high-resolution
displays. This procedure was derived from the "Retina Images" by Jeremey Worboys,
used in accordance with the Creative Commons Attribution 3.0 Unported License.
@@ -127,12 +175,12 @@ class Photo extends \Zotlabs\Web\Controller {
// $prvcachecontrol = 'no-cache';
$status = 'no cookie';
}
$resolution = 0;
if(strpos($photo,'.') !== false)
$photo = substr($photo,0,strpos($photo,'.'));
if(substr($photo,-2,1) == '-') {
$resolution = intval(substr($photo,-1,1));
$photo = substr($photo,0,-2);
@@ -140,7 +188,7 @@ class Photo extends \Zotlabs\Web\Controller {
if ($resolution == 2 && ($cookie_value > 1))
$resolution = 1;
}
$r = q("SELECT * FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
dbesc($photo),
intval($resolution)
@@ -151,7 +199,7 @@ class Photo extends \Zotlabs\Web\Controller {
$u = intval($r[0]['photo_usage']);
if($u) {
$allowed = 1;
if($u === PHOTO_COVER)
if($u === PHOTO_COVER)
if($resolution < PHOTO_RES_COVER_1200)
$allowed = (-1);
if($u === PHOTO_PROFILE)
@@ -184,9 +232,9 @@ class Photo extends \Zotlabs\Web\Controller {
dbesc($photo),
intval($resolution)
);
$exists = (($e) ? true : false);
if($exists && $allowed) {
$expires = strtotime($e[0]['expires'] . 'Z');
$data = dbunescbin($e[0]['content']);
@@ -209,16 +257,16 @@ class Photo extends \Zotlabs\Web\Controller {
}
}
}
}
else
http_status_exit(404,'not found');
}
if(! $data)
killme();
$etag = '"' . md5($data . $modified) . '"';
if($modified == 0)
$modified = time();
@@ -241,39 +289,39 @@ class Photo extends \Zotlabs\Web\Controller {
}
if(isset($prvcachecontrol)) {
// it is a private photo that they have no permission to view.
// tell the browser not to cache it, in case they authenticate
// and subsequently have permission to see it
header("Cache-Control: " . $prvcachecontrol);
}
else {
// The photo cache default is 1 day to provide a privacy trade-off,
// as somebody reducing photo permissions on a photo that is already
// as somebody reducing photo permissions on a photo that is already
// "in the wild" won't be able to stop the photo from being viewed
// for this amount amount of time once it is in the browser cache.
// The privacy expectations of your site members and their perception
// The privacy expectations of your site members and their perception
// of privacy where it affects the entire project may be affected.
// This has performance considerations but we highly recommend you
// leave it alone.
// This has performance considerations but we highly recommend you
// leave it alone.
$maxage = $cache_mode['age'];
if($cache_mode['exp'] || (! isset($expires)) || (isset($expires) && $expires - 60 < time()))
$expires = time() + $maxage;
else
$maxage = $expires - time();
header("Expires: " . gmdate("D, d M Y H:i:s", $expires) . " GMT");
// set CDN/Infrastructure caching much lower than maxage
// set CDN/Infrastructure caching much lower than maxage
// in the event that infrastructure caching is present.
$smaxage = intval($maxage/12);
header("Cache-Control: s-maxage=" . $smaxage . ", max-age=" . $maxage . $cachecontrol);
}
header("Content-type: " . $mimetype);
@@ -281,7 +329,7 @@ class Photo extends \Zotlabs\Web\Controller {
header("ETag: " . $etag);
header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data)));
// If it's a file resource, stream it.
// If it's a file resource, stream it.
if($streaming) {
if(strpos($streaming,'store') !== false)
$istream = fopen($streaming,'rb');
@@ -300,5 +348,5 @@ class Photo extends \Zotlabs\Web\Controller {
killme();
}
}

View File

@@ -171,6 +171,7 @@ class Photos extends \Zotlabs\Web\Controller {
}
goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address']);
}
if((argc() > 2) && (x($_REQUEST,'delete')) && ($_REQUEST['delete'] === t('Delete Photo'))) {
@@ -501,6 +502,9 @@ class Photos extends \Zotlabs\Web\Controller {
goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address']);
}
if(is_ajax())
killme();
goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/album/' . $r['data']['folder']);
}
@@ -709,13 +713,15 @@ class Photos extends \Zotlabs\Web\Controller {
]);
if($x = photos_album_exists($owner_uid, get_observer_hash(), $datum)) {
\App::set_pager_itemspage(30);
$album = $x['display_path'];
}
else {
goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address']);
$album = '/';
//goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address']);
}
\App::set_pager_itemspage(30);
if($_GET['order'] === 'posted')
$order = 'ASC';
else

View File

@@ -1,7 +1,11 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Libsync;
use Zotlabs\Web\Controller;
/*
* @file Profile_photo.php
@@ -15,109 +19,123 @@ require_once('include/photos.php');
require_once('include/channel.php');
/* @brief Function for sync'ing permissions of profile-photos and their profile
*
* @param $profileid The id number of the profile to sync
* @return void
*/
*
*/
class Profile_photo extends Controller {
class Profile_photo extends \Zotlabs\Web\Controller {
/* @brief Initalize the profile-photo edit view
*
* @return void
*
*/
function init() {
if(! local_channel()) {
if (!local_channel()) {
return;
}
$channel = \App::get_channel();
profile_load($channel['channel_address']);
$channel = App::get_channel();
$profile = App::$argv[1];
profile_load($channel['channel_address'], $profile);
}
/* @brief Evaluate posted values
*
* @param $a Current application
* @return void
*
*/
function post() {
if(! local_channel()) {
if (!local_channel()) {
return;
}
$channel = \App::get_channel();
$channel = App::get_channel();
check_form_security_token_redirectOnErr('/profile_photo', 'profile_photo');
// Remove cover photo
if(isset($_POST['remove'])) {
$r = q("SELECT resource_id FROM photo WHERE photo_usage = %d AND uid = %d LIMIT 1",
intval(PHOTO_PROFILE),
intval(local_channel())
);
if($r) {
q("update photo set photo_usage = %d where photo_usage = %d and uid = %d",
intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE),
intval(local_channel())
);
$sync = attach_export_data($channel,$r[0]['resource_id']);
if($sync)
Libsync:: build_sync_packet($channel['channel_id'],array('file' => array($sync)));
$r = q("select id, profile_guid, is_default, gender from profile where uid = %d",
intval(local_channel())
);
$profile_id = intval($_POST['profile']);
$default_profile_id = null;
$profile = [];
foreach ($r as $rr) {
if ($rr['is_default']) {
$default_profile_id = intval($rr['id']);
}
if ($profile_id === intval($rr['id'])) {
$profile = $rr;
}
$_SESSION['reload_avatar'] = true;
goaway(z_root() . '/profiles');
}
if((array_key_exists('cropfinal',$_POST)) && (intval($_POST['cropfinal']) == 1)) {
$is_default_profile = ($profile_id === $default_profile_id);
// Remove profile photo
if (isset($_POST['remove'])) {
if ($is_default_profile) {
$r = q("SELECT resource_id FROM photo WHERE photo_usage = %d AND uid = %d LIMIT 1",
intval(PHOTO_PROFILE),
intval(local_channel())
);
if ($r) {
q("update photo set photo_usage = %d where photo_usage = %d and uid = %d",
intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE),
intval(local_channel())
);
q("update profile set photo = '%s', thumb = '%s' where is_default = 1 and uid = %d",
dbesc(z_root() . '/photo/profile/l/' . local_channel()),
dbesc(z_root() . '/photo/profile/m/' . local_channel()),
intval(local_channel())
);
}
}
else {
q("update profile set photo = '%s', thumb = '%s' where id = %d and uid = %d",
dbesc(z_root() . '/' . get_default_profile_photo(300)),
dbesc(z_root() . '/' . get_default_profile_photo(80)),
intval($profile_id),
intval(local_channel())
);
}
$sync = attach_export_data($channel, $r[0]['resource_id']);
if ($sync)
Libsync:: build_sync_packet($channel['channel_id'], ['file' => [$sync]]);
$_SESSION['reload_avatar'] = true;
goaway(z_root() . '/profiles/' . $profile_id);
}
if ((array_key_exists('cropfinal', $_POST)) && (intval($_POST['cropfinal']) == 1)) {
// logger('crop: ' . print_r($_POST,true));
// phase 2 - we have finished cropping
if(argc() != 2) {
notice( t('Image uploaded but image cropping failed.') . EOL );
if (argc() != 2) {
notice(t('Image uploaded but image cropping failed.') . EOL);
return;
}
$image_id = argv(1);
if(substr($image_id,-2,1) == '-') {
$scale = substr($image_id,-1,1);
$image_id = substr($image_id,0,-2);
if (substr($image_id, -2, 1) == '-') {
$scale = substr($image_id, -1, 1);
$image_id = substr($image_id, 0, -2);
}
// unless proven otherwise
$is_default_profile = 1;
if($_REQUEST['profile']) {
$r = q("select id, profile_guid, is_default, gender from profile where id = %d and uid = %d limit 1",
intval($_REQUEST['profile']),
intval(local_channel())
);
if($r) {
$profile = $r[0];
if(! intval($profile['is_default']))
$is_default_profile = 0;
}
}
$srcX = intval($_POST['xstart']);
$srcY = intval($_POST['ystart']);
$srcW = intval($_POST['xfinal']) - $srcX;
@@ -126,45 +144,45 @@ class Profile_photo extends \Zotlabs\Web\Controller {
$r = q("SELECT * FROM photo WHERE resource_id = '%s' AND uid = %d AND imgscale = %d LIMIT 1",
dbesc($image_id),
dbesc(local_channel()),
intval($scale));
if($r) {
$base_image = $r[0];
intval($scale)
);
if ($r) {
$base_image = $r[0];
$base_image['content'] = (($r[0]['os_storage']) ? @file_get_contents(dbunescbin($base_image['content'])) : dbunescbin($base_image['content']));
$im = photo_factory($base_image['content'], $base_image['mimetype']);
if($im->is_valid()) {
$im->cropImage(300,$srcX,$srcY,$srcW,$srcH);
if ($im->is_valid()) {
$im->cropImage(300, $srcX, $srcY, $srcW, $srcH);
$aid = get_account_id();
$p = [
'aid' => $aid,
'uid' => local_channel(),
$p = [
'aid' => $aid,
'uid' => local_channel(),
'resource_id' => $base_image['resource_id'],
'filename' => $base_image['filename'],
'filename' => $base_image['filename'],
'album' => t('Profile Photos'),
'os_path' => $base_image['os_path'],
'display_path' => $base_image['display_path'],
'photo_usage' => PHOTO_PROFILE,
'edited' => dbescdate($base_image['edited'])
'photo_usage' => (($is_default_profile) ? PHOTO_PROFILE : PHOTO_NORMAL),
'edited' => dbescdate($base_image['edited'])
];
$p['photo_usage'] = (($is_default_profile) ? PHOTO_PROFILE : PHOTO_NORMAL);
$r1 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_300);
$im->scaleImage(80);
$r2 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_80);
$im->scaleImage(48);
$r3 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_48);
if($r1 === false || $r2 === false || $r3 === false) {
if ($r1 === false || $r2 === false || $r3 === false) {
// if one failed, delete them all so we can start over.
notice( t('Image resize failed.') . EOL );
$x = q("delete from photo where resource_id = '%s' and uid = %d and imgscale in ( %d, %d, %d )",
notice(t('Image resize failed.') . EOL);
q("delete from photo where resource_id = '%s' and uid = %d and imgscale in ( %d, %d, %d )",
dbesc($base_image['resource_id']),
local_channel(),
intval(PHOTO_RES_PROFILE_300),
@@ -179,59 +197,55 @@ class Profile_photo extends \Zotlabs\Web\Controller {
intval(PHOTO_RES_PROFILE_80),
intval(PHOTO_RES_PROFILE_48)
);
if($x) {
foreach($x as $xx) {
if ($x) {
foreach ($x as $xx) {
@unlink(dbunescbin($xx['content']));
}
}
return;
}
// If setting for the default profile, unset the profile photo flag from any other photos I own
if($is_default_profile) {
$r = q("update profile set photo = '%s', thumb = '%s' where is_default = 1 and uid = %d",
// If setting for the default profile, unset the profile photo flag from any other photos I own
if ($is_default_profile) {
q("update profile set photo = '%s', thumb = '%s' where is_default = 1 and uid = %d",
dbesc(z_root() . '/photo/profile/l/' . local_channel()),
dbesc(z_root() . '/photo/profile/m/' . local_channel()),
intval(local_channel())
);
$r = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d
q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d
AND resource_id != '%s' AND uid = %d",
intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE),
dbesc($base_image['resource_id']),
intval(local_channel())
);
send_profile_photo_activity($channel,$base_image,$profile);
send_profile_photo_activity($channel, $base_image, $profile);
}
else {
$r = q("update profile set photo = '%s', thumb = '%s' where id = %d and uid = %d",
q("update profile set photo = '%s', thumb = '%s' where id = %d and uid = %d",
dbesc(z_root() . '/photo/' . $base_image['resource_id'] . '-4'),
dbesc(z_root() . '/photo/' . $base_image['resource_id'] . '-5'),
intval($_REQUEST['profile']),
intval($profile_id),
intval(local_channel())
);
}
// set $send to false in profiles_build_sync() to return the data
// so that we only send one sync packet.
$sync_profiles = profiles_build_sync(local_channel(),false);
// set $send to false in profiles_build_sync() to return the data
// so that we only send one sync packet.
$sync_profiles = profiles_build_sync(local_channel(), false);
// We'll set the updated profile-photo timestamp even if it isn't the default profile,
// so that browsers will do a cache update unconditionally
// Also set links back to site-specific profile photo url in case it was
// changed to a generic URL by a clone operation. Otherwise the new photo may
// changed to a generic URL by a clone operation. Otherwise the new photo may
// not get pushed to other sites correctly.
$r = q("UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s'
q("UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s'
where xchan_hash = '%s'",
dbesc($im->getType()),
dbescdate($base_image['edited']),
@@ -241,341 +255,372 @@ class Profile_photo extends \Zotlabs\Web\Controller {
dbesc($channel['xchan_hash'])
);
photo_profile_setperms(local_channel(),$base_image['resource_id'],$_REQUEST['profile']);
photo_profile_setperms(local_channel(), $base_image['resource_id'], $profile_id);
$sync = attach_export_data($channel,$base_image['resource_id']);
if($sync)
Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync), 'profile' => $sync_profiles));
$sync = attach_export_data($channel, $base_image['resource_id']);
if ($sync)
Libsync::build_sync_packet($channel['channel_id'], ['file' => [$sync], 'profile' => $sync_profiles]);
// Similarly, tell the nav bar to bypass the cache and update the avatar image.
$_SESSION['reload_avatar'] = true;
info( t('Shift-reload the page or clear browser cache if the new photo does not display immediately.') . EOL);
info(t('Shift-reload the page or clear browser cache if the new photo does not display immediately.') . EOL);
// Update directory in background
\Zotlabs\Daemon\Master::Summon(array('Directory',$channel['channel_id']));
Master::Summon(['Directory', $channel['channel_id']]);
}
else
notice( t('Unable to process image') . EOL);
notice(t('Unable to process image') . EOL);
}
goaway(z_root() . '/profiles');
goaway(z_root() . '/profiles/' . $profile_id);
return; // NOTREACHED
}
// A new photo was uploaded. Store it and save some important details
// in App::$data for use in the cropping function
$hash = photo_new_resource();
$importing = false;
$smallest = 0;
if($_REQUEST['importfile']) {
$hash = $_REQUEST['importfile'];
$hash = photo_new_resource();
$importing = false;
$smallest = 0;
if ($_REQUEST['importfile']) {
$hash = $_REQUEST['importfile'];
$importing = true;
}
else {
require_once('include/attach.php');
$res = attach_store(\App::get_channel(), get_observer_hash(), '', array('album' => t('Profile Photos'), 'hash' => $hash, 'nosync' => true));
logger('attach_store: ' . print_r($res,true));
$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) {
logger('Content-Range: ' . print_r($matches, true));
$partial = true;
}
}
if ($partial) {
$x = save_chunk($channel, $matches[1], $matches[2], $matches[3]);
if ($x['partial']) {
header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
json_return_and_die($x);
}
else {
header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0));
$_FILES['userfile'] = [
'name' => $x['name'],
'type' => $x['type'],
'tmp_name' => $x['tmp_name'],
'error' => $x['error'],
'size' => $x['size']
];
}
}
else {
if (!array_key_exists('userfile', $_FILES)) {
$_FILES['userfile'] = [
'name' => $_FILES['files']['name'],
'type' => $_FILES['files']['type'],
'tmp_name' => $_FILES['files']['tmp_name'],
'error' => $_FILES['files']['error'],
'size' => $_FILES['files']['size']
];
}
}
$res = attach_store(App::get_channel(), get_observer_hash(), '', ['album' => t('Profile Photos'), 'hash' => $hash, 'nosync' => true, 'source' => 'photos']);
json_return_and_die(['message' => $hash]);
}
if(($res && intval($res['data']['is_photo'])) || $importing) {
if (($res && intval($res['data']['is_photo'])) || $importing) {
$i = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale",
dbesc($hash),
intval(local_channel())
);
if(! $i) {
notice( t('Image upload failed.') . EOL );
if (!$i) {
notice(t('Image upload failed.') . EOL);
return;
}
$os_storage = false;
foreach($i as $ii) {
if(intval($ii['imgscale']) < PHOTO_RES_640) {
$smallest = intval($ii['imgscale']);
foreach ($i as $ii) {
if (intval($ii['imgscale']) < PHOTO_RES_640) {
$smallest = intval($ii['imgscale']);
$os_storage = intval($ii['os_storage']);
$imagedata = $ii['content'];
$filetype = $ii['mimetype'];
$imagedata = $ii['content'];
$filetype = $ii['mimetype'];
}
}
}
$imagedata = (($os_storage) ? @file_get_contents(dbunescbin($imagedata)) : dbunescbin($imagedata));
$ph = photo_factory($imagedata, $filetype);
if(! $ph->is_valid()) {
notice( t('Unable to process image.') . EOL );
$ph = photo_factory($imagedata, $filetype);
if (!$ph->is_valid()) {
notice(t('Unable to process image.') . EOL);
return;
}
return $this->profile_photo_crop_ui_head($a, $ph, $hash, $smallest);
return $this->profile_photo_crop_ui_head($ph, $hash, $smallest);
// This will "fall through" to the get() method, and since
// App::$data['imagecrop'] is set, it will proceed to cropping
// rather than present the upload form
// App::$data['imagecrop'] is set, it will proceed to cropping
// rather than present the upload form
}
/* @brief Generate content of profile-photo view
*
* @param $a Current application
* @return void
*
*/
function get() {
if(! local_channel()) {
notice( t('Permission denied.') . EOL );
if (!local_channel()) {
notice(t('Permission denied.') . EOL);
return;
}
$channel = \App::get_channel();
$pf = 0;
$newuser = false;
if(argc() == 2 && argv(1) === 'new')
$newuser = true;
if(argv(1) === 'use') {
$channel = App::get_channel();
$profile_id = (($_REQUEST['profile']) ? intval($_REQUEST['profile']) : intval(argv(1)));
$default_profile_id = null;
$r = q("select id, profile_name as name, is_default from profile where uid = %d order by id asc",
intval(local_channel())
);
foreach ($r as $rr) {
if ($rr['is_default']) {
$default_profile_id = intval($rr['id']);
}
if ($profile_id === intval($rr['id'])) {
$profile = $rr;
}
}
$is_default_profile = ($profile_id === $default_profile_id);
if (argv(1) === 'use') {
if (argc() < 3) {
notice( t('Permission denied.') . EOL );
notice(t('Permission denied.') . EOL);
return;
};
$resource_id = argv(2);
$pf = (($_REQUEST['pf']) ? intval($_REQUEST['pf']) : 0);
$c = q("select id, is_default from profile where uid = %d",
intval(local_channel())
);
$multi_profiles = true;
if(($c) && (count($c) === 1) && (intval($c[0]['is_default']))) {
$_REQUEST['profile'] = $c[0]['id'];
$multi_profiles = false;
}
else {
$_REQUEST['profile'] = $pf;
}
$r = q("SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' ORDER BY imgscale ASC",
intval(local_channel()),
dbesc($resource_id)
);
if(! $r) {
notice( t('Photo not available.') . EOL );
if (!$r) {
notice(t('Photo not available.') . EOL);
return;
}
$havescale = false;
foreach($r as $rr) {
if($rr['imgscale'] == PHOTO_RES_PROFILE_80)
foreach ($r as $rr) {
if ($rr['imgscale'] == PHOTO_RES_PROFILE_80)
$havescale = true;
}
// set an already loaded and cropped photo as profile photo
if($havescale) {
// unset any existing profile photos
$x = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND uid = %d",
intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE),
if ($havescale) {
if ($is_default_profile) {
// unset any existing profile photos
q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND uid = %d",
intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE),
intval(local_channel())
);
$edited = datetime_convert();
q("UPDATE photo SET photo_usage = %d, edited = '%s' WHERE uid = %d AND resource_id = '%s' AND imgscale > 0",
intval(PHOTO_PROFILE),
dbescdate($edited),
intval(local_channel()),
dbesc($resource_id)
);
q("UPDATE xchan SET xchan_photo_date = '%s' WHERE xchan_hash = '%s'",
dbescdate($edited),
dbesc($channel['xchan_hash'])
);
}
q("update profile set photo = '%s', thumb = '%s' where id = %d and uid = %d",
dbesc(z_root() . '/photo/' . $resource_id . '-4'),
dbesc(z_root() . '/photo/' . $resource_id . '-5'),
intval($profile_id),
intval(local_channel())
);
$edited = datetime_convert();
$x = q("UPDATE photo SET photo_usage = %d, edited = '%s' WHERE uid = %d AND resource_id = '%s' AND imgscale > 0",
intval(PHOTO_PROFILE),
dbescdate($edited),
intval(local_channel()),
dbesc($resource_id)
);
$x = q("UPDATE xchan SET xchan_photo_date = '%s' WHERE xchan_hash = '%s'",
dbescdate($edited),
dbesc($channel['xchan_hash'])
);
photo_profile_setperms(local_channel(),$resource_id,$_REQUEST['profile']);
photo_profile_setperms(local_channel(), $resource_id, $profile_id);
$sync = attach_export_data($channel,$resource_id);
if($sync)
Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync)));
$sync = attach_export_data($channel, $resource_id);
if ($sync)
Libsync::build_sync_packet($channel['channel_id'], ['file' => [$sync]]);
$_SESSION['reload_avatar'] = true;
\Zotlabs\Daemon\Master::Summon(array('Directory',local_channel()));
goaway(z_root() . '/profiles');
Master::Summon(['Directory', local_channel()]);
goaway(z_root() . '/profiles/' . $profile_id);
}
$r = q("SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1",
intval($r[0]['id']),
intval(local_channel())
);
if(! $r) {
notice( t('Photo not available.') . EOL );
if (!$r) {
notice(t('Photo not available.') . EOL);
return;
}
if(intval($r[0]['os_storage']))
if (intval($r[0]['os_storage'])) {
$data = @file_get_contents(dbunescbin($r[0]['content']));
else
$data = dbunescbin($r[0]['content']);
$ph = photo_factory($data, $r[0]['mimetype']);
}
else {
$data = dbunescbin($r[0]['content']);
}
$ph = photo_factory($data, $r[0]['mimetype']);
$smallest = 0;
if($ph->is_valid()) {
if ($ph->is_valid()) {
// go ahead as if we have just uploaded a new photo to crop
$i = q("select resource_id, imgscale from photo where resource_id = '%s' and uid = %d order by imgscale",
dbesc($r[0]['resource_id']),
intval(local_channel())
);
if($i) {
if ($i) {
$hash = $i[0]['resource_id'];
foreach($i as $ii) {
if(intval($ii['imgscale']) < PHOTO_RES_640) {
foreach ($i as $ii) {
if (intval($ii['imgscale']) < PHOTO_RES_640) {
$smallest = intval($ii['imgscale']);
}
}
}
}
if($multi_profiles) {
\App::$data['importfile'] = $resource_id;
}
else {
$this->profile_photo_crop_ui_head($a, $ph, $hash, $smallest);
}
}
$this->profile_photo_crop_ui_head($ph, $hash, $smallest);
// falls through with App::$data['imagecrop'] set so we go straight to the cropping section
}
// present an upload form
$profiles = q("select id, profile_name as name, is_default from profile where uid = %d order by id asc",
intval(local_channel())
);
$importing = ((array_key_exists('importfile', App::$data)) ? true : false);
if($profiles) {
for($x = 0; $x < count($profiles); $x ++) {
$profiles[$x]['selected'] = false;
if($pf && $profiles[$x]['id'] == $pf)
$profiles[$x]['selected'] = true;
if((! $pf) && $profiles[$x]['is_default'])
$profiles[$x]['selected'] = true;
}
}
if (!x(App::$data, 'imagecrop')) {
$importing = ((array_key_exists('importfile',\App::$data)) ? true : false);
if(! x(\App::$data,'imagecrop')) {
$tpl = get_markup_template('profile_photo.tpl');
$o .= replace_macros($tpl,array(
'$user' => \App::$channel['channel_address'],
'$info' => ((count($profiles) > 1) ? t('Your default profile photo is visible to anybody on the internet. Profile photos for alternate profiles will inherit the permissions of the profile') : t('Your profile photo is visible to anybody on the internet and may be distributed to other websites.')),
'$importfile' => (($importing) ? \App::$data['importfile'] : ''),
'$lbl_upfile' => t('Upload File:'),
'$lbl_profiles' => t('Select a profile:'),
'$title' => (($importing) ? t('Use Photo for Profile') : t('Change Profile Photo')),
'$submit' => (($importing) ? t('Use') : t('Upload')),
'$remove' => t('Remove'),
'$profiles' => $profiles,
'$single' => ((count($profiles) == 1) ? true : false),
'$profile0' => $profiles[0],
'$embedPhotos' => t('Use a photo from your albums'),
'$embedPhotosModalTitle' => t('Use a photo from your albums'),
$o = replace_macros($tpl, [
'$user' => App::$channel['channel_address'],
'$info' => (($is_default_profile) ? t('This profile photo will be visible to anybody on the internet and may be distributed to other websites.') : t('This profile photo will be visible only to channels with permission to view this profile.')),
'$importfile' => (($importing) ? App::$data['importfile'] : ''),
'$title' => (($importing) ? t('Use Photo for Profile') : t('Change Profile Photo')),
'$submit' => t('Upload'),
'$remove' => t('Reset to default'),
'$profile_id' => $profile_id,
'$profile' => $profile,
'$embedPhotos' => t('Use a photo from your albums'),
'$embedPhotosModalTitle' => t('Use a photo from your albums'),
'$embedPhotosModalCancel' => t('Cancel'),
'$embedPhotosModalOK' => t('OK'),
'$modalchooseimages' => t('Choose images to embed'),
'$modalchoosealbum' => t('Choose an album'),
'$modaldiffalbum' => t('Choose a different album'),
'$modalerrorlist' => t('Error getting album list'),
'$modalerrorlink' => t('Error getting photo link'),
'$modalerroralbum' => t('Error getting album'),
'$form_security_token' => get_form_security_token("profile_photo"),
'$select' => t('Select existing photo'),
));
'$embedPhotosModalOK' => t('OK'),
'$modalchooseimages' => t('Choose images to embed'),
'$modalchoosealbum' => t('Choose an album'),
'$modaldiffalbum' => t('Choose a different album'),
'$modalerrorlist' => t('Error getting album list'),
'$modalerrorlink' => t('Error getting photo link'),
'$modalerroralbum' => t('Error getting album'),
'$form_security_token' => get_form_security_token("profile_photo"),
'$select' => t('Select existing'),
]);
call_hooks('profile_photo_content_end', $o);
return $o;
}
else {
// present a cropping form
$filename = \App::$data['imagecrop'] . '-' . \App::$data['imagecrop_resolution'];
$resolution = \App::$data['imagecrop_resolution'];
$tpl = get_markup_template("cropbody.tpl");
$o .= replace_macros($tpl,array(
'$filename' => $filename,
'$profile' => intval($_REQUEST['profile']),
'$resource' => \App::$data['imagecrop'] . '-' . \App::$data['imagecrop_resolution'],
'$image_url' => z_root() . '/photo/' . $filename,
'$title' => t('Crop Image'),
'$desc' => t('Please adjust the image cropping for optimum viewing.'),
$filename = App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution'];
$tpl = get_markup_template("cropbody.tpl");
$o = replace_macros($tpl, [
'$filename' => $filename,
'$profile' => $profile_id,
'$resource' => App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution'],
'$image_url' => z_root() . '/photo/' . $filename,
'$title' => t('Crop Image'),
'$desc' => t('Please adjust the image cropping for optimum viewing.'),
'$form_security_token' => get_form_security_token("profile_photo"),
'$done' => t('Done Editing')
));
'$done' => t('Done editing')
]);
return $o;
}
return; // NOTREACHED
}
/* @brief Generate the UI for photo-cropping
*
* @param $a Current application
* @param $ph Photo-Factory
* @return void
* @param $ph
* @param $hash
* @param $smallest
*
*/
function profile_photo_crop_ui_head(&$a, $ph, $hash, $smallest){
$max_length = get_config('system','max_image_length');
if(! $max_length)
function profile_photo_crop_ui_head($ph, $hash, $smallest) {
$max_length = get_config('system', 'max_image_length');
if (!$max_length) {
$max_length = MAX_IMAGE_LENGTH;
if($max_length > 0)
$ph->scaleImage($max_length);
\App::$data['width'] = $ph->getWidth();
\App::$data['height'] = $ph->getHeight();
if(\App::$data['width'] < 500 || \App::$data['height'] < 500) {
$ph->scaleImageUp(400);
\App::$data['width'] = $ph->getWidth();
\App::$data['height'] = $ph->getHeight();
}
\App::$data['imagecrop'] = $hash;
\App::$data['imagecrop_resolution'] = $smallest;
\App::$page['htmlhead'] .= replace_macros(get_markup_template("crophead.tpl"), array());
if ($max_length > 0) {
$ph->scaleImage($max_length);
}
App::$data['width'] = $ph->getWidth();
App::$data['height'] = $ph->getHeight();
if (App::$data['width'] < 500 || App::$data['height'] < 500) {
$ph->scaleImageUp(400);
App::$data['width'] = $ph->getWidth();
App::$data['height'] = $ph->getHeight();
}
App::$data['imagecrop'] = $hash;
App::$data['imagecrop_resolution'] = $smallest;
App::$page['htmlhead'] .= replace_macros(get_markup_template("crophead.tpl"), []);
return;
}
}

View File

@@ -163,35 +163,6 @@ class Profiles extends \Zotlabs\Web\Controller {
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')) {
if(feature_enabled(local_channel(),'multi_profiles'))
$id = \App::$argv[1];
else {
$x = q("select id from profile where uid = %d and is_default = 1",
intval(local_channel())
);
if($x)
$id = $x[0]['id'];
}
$r = q("SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1",
intval($id),
intval(local_channel())
);
if(! count($r)) {
notice( t('Profile not found.') . EOL);
\App::$error = 404;
return;
}
$chan = \App::get_channel();
profile_load($chan['channel_address'],$r[0]['id']);
}
}
function post() {
@@ -317,8 +288,6 @@ class Profiles extends \Zotlabs\Web\Controller {
$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);
@@ -514,6 +483,16 @@ class Profiles extends \Zotlabs\Web\Controller {
$value = $locality . $comma1 . $region . $comma2 . $country_name;
}
$hide_friends = ((intval($_POST['hide_friends'])) ? 1: 0);
$suggestme = ((x($_POST, 'suggestme')) ? intval($_POST['suggestme']) : 0);
set_pconfig(local_channel(), 'system', 'suggestme', $suggestme);
$show_presence = (((x($_POST, 'show_presence')) && (intval($_POST['show_presence']) == 1)) ? 1 : 0);
set_pconfig(local_channel(), 'system', 'show_online_status', $show_presence);
$publish = ((x($_POST, 'profile_in_directory') && (intval($_POST['profile_in_directory']) == 1)) ? 1 : 0);
profile_activity($changes,$value);
}
@@ -552,7 +531,8 @@ class Profiles extends \Zotlabs\Web\Controller {
employment = '%s',
education = '%s',
hide_friends = %d,
profile_vcard = '%s'
profile_vcard = '%s',
publish = %d
WHERE id = %d AND uid = %d",
dbesc($profile_name),
dbesc($name),
@@ -588,6 +568,7 @@ class Profiles extends \Zotlabs\Web\Controller {
dbesc($education),
intval($hide_friends),
dbesc($profile_vcard),
intval($publish),
intval(argv(1)),
intval(local_channel())
);
@@ -595,31 +576,31 @@ class Profiles extends \Zotlabs\Web\Controller {
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())
);
if($r) {
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'",
// change name on all associated xchans by matching the url
q("UPDATE xchan SET xchan_name = '%s', xchan_name_date = '%s' WHERE xchan_url = '%s'",
dbesc($name),
dbesc(datetime_convert()),
dbesc(z_root() . '/channel/' . $channel['channel_address'])
);
$r = q("UPDATE channel SET channel_name = '%s' WHERE channel_hash = '%s'",
q("UPDATE channel SET channel_name = '%s' WHERE channel_hash = '%s'",
dbesc($name),
dbesc($channel['xchan_hash'])
);
}
$r = q("select * from profile where id = %d and uid = %d limit 1",
intval(argv(1)),
intval(local_channel())
);
if($r) {
Libsync::build_sync_packet(local_channel(), ['profile' => $r]);
}
if($is_default) {
// reload the info for the sidebar widget - why does this not work?
profile_load($channel['channel_address']);
\Zotlabs\Daemon\Master::Summon(array('Directory',local_channel()));
}
}
@@ -630,13 +611,13 @@ class Profiles extends \Zotlabs\Web\Controller {
$o = '';
$channel = \App::get_channel();
if(! local_channel()) {
notice( t('Permission denied.') . EOL);
return;
}
$channel = \App::get_channel();
require_once('include/channel.php');
$profile_fields_basic = get_profile_fields_basic();
@@ -652,15 +633,20 @@ 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())
);
if(! $r) {
notice( t('Profile not found.') . EOL);
return;
}
// make sure we got uptodate data
profile_load($channel['channel_address'], $id);
$editselect = 'none';
\App::$page['htmlhead'] .= replace_macros(get_markup_template('profed_head.tpl'), array(
@@ -674,13 +660,43 @@ class Profiles extends \Zotlabs\Web\Controller {
else
$fields = $profile_fields_basic;
$hide_friends = array(
'hide_friends',
t('Hide your connections list from viewers of this profile'),
$r[0]['hide_friends'],
'',
array(t('No'),t('Yes'))
);
$show_presence = [];
$profile_in_dir = '';
$suggestme = '';
$hide_friends = [];
$is_default = (($r[0]['is_default']) ? 1 : 0);
if ($is_default) {
$hide_friends = array(
'hide_friends',
t('Hide my connections from viewers of this profile'),
$r[0]['hide_friends'],
'',
[t('No'), t('Yes')]
);
$opt_tpl = get_markup_template("field_checkbox.tpl");
if (get_config('system', 'publish_all')) {
$profile_in_dir = '<input type="hidden" name="profile_in_directory" value="1" />';
}
else {
$profile_in_dir = replace_macros($opt_tpl, [
'$field' => ['profile_in_directory', t('Publish my default profile in the network directory'), $r[0]['publish'], '', [t('No'), t('Yes')]],
]);
}
$suggestme = get_pconfig(local_channel(), 'system', 'suggestme');
$suggestme = (($suggestme === false) ? '0' : $suggestme); // default if not set: 0
$suggestme = replace_macros($opt_tpl, [
'$field' => ['suggestme', t('Suggest me as a potential contact to new members'), $suggestme, '', [t('No'), t('Yes')]],
]);
$show_presence_val = intval(get_pconfig(local_channel(), 'system', 'show_online_status'));
$show_presence = ['show_presence', t('Reveal my online status'), $show_presence_val, '', [t('No'), t('Yes')]];
}
$q = q("select * from profdef where true");
if($q) {
@@ -701,15 +717,15 @@ class Profiles extends \Zotlabs\Web\Controller {
//logger('extra_fields: ' . print_r($extra_fields,true));
$vc = $r[0]['profile_vcard'];
$vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null);
$vcard = (($vctmp) ? get_vcard_array($vctmp,$r[0]['id']) : [] );
//$vc = $r[0]['profile_vcard'];
//$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(
@@ -718,12 +734,12 @@ class Profiles extends \Zotlabs\Web\Controller {
'$profile_clone_link' => 'profiles/clone/' . $r[0]['id'] . '?t=' . get_form_security_token("profile_clone"),
'$profile_drop_link' => 'profiles/drop/' . $r[0]['id'] . '?t=' . get_form_security_token("profile_drop"),
'$fields' => $fields,
'$vcard' => $vcard,
//'$vcard' => $vcard,
'$guid' => $r[0]['profile_guid'],
'$banner' => t('Edit Profile Details'),
'$submit' => t('Submit'),
'$viewprof' => t('View this profile'),
'$editvis' => t('Edit visibility'),
'$editvis' => t('Edit visibility'),
'$tools_label' => t('Profile Tools'),
'$coverpic' => t('Change cover photo'),
'$profpic' => t('Change profile photo'),
@@ -731,7 +747,7 @@ class Profiles extends \Zotlabs\Web\Controller {
'$cl_prof' => t('Clone this profile'),
'$del_prof' => t('Delete this profile'),
'$addthing' => t('Add profile things'),
'$personal' => t('Personal'),
'$basic' => t('Basic'),
'$location' => t('Location'),
'$relation' => t('Relationship'),
'$miscellaneous'=> t('Miscellaneous'),
@@ -783,23 +799,28 @@ class Profiles extends \Zotlabs\Web\Controller {
'$contact' => array('contact', t('Contact information and social networks'), $r[0]['contact']),
'$channels' => array('channels', t('My other channels'), $r[0]['channels']),
'$extra_fields' => $extra_fields,
'$comms' => t('Communications'),
'$tel_label' => t('Phone'),
'$email_label' => t('Email'),
'$impp_label' => t('Instant messenger'),
'$url_label' => t('Website'),
'$adr_label' => t('Address'),
'$note_label' => t('Note'),
'$mobile' => t('Mobile'),
'$home' => t('Home'),
'$work' => t('Work'),
'$other' => t('Other'),
'$add_card' => t('Add Contact'),
'$add_field' => t('Add Field'),
'$create' => t('Create'),
'$update' => t('Update'),
'$delete' => t('Delete'),
'$cancel' => t('Cancel'),
//'$comms' => t('Communications'),
//'$tel_label' => t('Phone'),
//'$email_label' => t('Email'),
//'$impp_label' => t('Instant messenger'),
//'$url_label' => t('Website'),
//'$adr_label' => t('Address'),
//'$note_label' => t('Note'),
//'$mobile' => t('Mobile'),
//'$home' => t('Home'),
//'$work' => t('Work'),
//'$other' => t('Other'),
//'$add_card' => t('Add Contact'),
//'$add_field' => t('Add Field'),
//'$create' => t('Create'),
//'$update' => t('Update'),
//'$delete' => t('Delete'),
//'$cancel' => t('Cancel'),
'$show_presence' => $show_presence,
'$suggestme' => $suggestme,
'$profile_in_dir' => $profile_in_dir,
));
$arr = array('profile' => $r[0], 'entry' => $o);

View File

@@ -2,6 +2,9 @@
namespace Zotlabs\Module;
use Zotlabs\Lib\Connect;
use Zotlabs\Daemon\Master;
require_once('include/security.php');
/**
@@ -184,7 +187,24 @@ class Regate extends \Zotlabs\Web\Controller {
$new_channel = auto_channel_create($cra['account']['account_id']);
if($new_channel['success']) {
$channel_id = $new_channel['channel']['channel_id'];
// If we have an inviter, connect.
if ($didx === 'i' && intval($r['reg_byc'])) {
$invite_channel = channelx_by_n($r['reg_byc']);
if ($invite_channel) {
$f = Connect::connect($new_channel['channel'], $invite_channel['xchan_addr']);
if ($f['success']) {
$can_view_stream = intval(get_abconfig($channel_id, $f['abook']['abook_xchan'], 'their_perms', 'view_stream'));
// If we can view their stream, pull in some posts
if ($can_view_stream) {
Master::Summon(['Onepoll', $f['abook']['abook_id']]);
}
}
}
}
change_channel($channel_id);
$nextpage = 'profiles/' . $channel_id;
$msg_code = 'ZAR1239I';

View File

@@ -57,8 +57,8 @@ class Removeme extends \Zotlabs\Web\Controller {
$o .= replace_macros($tpl, array(
'$basedir' => z_root(),
'$hash' => $hash,
'$title' => t('Remove This Channel'),
'$desc' => [ t('WARNING: '), t('This channel will be completely removed from the network. '), t('This action is permanent and can not be undone!') ],
'$title' => t('Remove Channel'),
'$desc' => [ t('WARNING: '), t('This channel will be permanently removed. '), t('This action can not be undone!') ],
'$passwd' => t('Please enter your password for verification:'),
// '$global' => [ 'global', t('Remove this channel and all its clones from the network'), false, t('By default only the instance of the channel located on this hub will be removed from the network'), [ t('No'),t('Yes') ] ],
'$submit' => t('Remove Channel')

View File

@@ -3,6 +3,7 @@
namespace Zotlabs\Module;
use App;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Web\Controller;
@@ -57,26 +58,15 @@ class Search extends Controller {
$o .= search($search, 'search-box', '/search', ((local_channel()) ? true : false));
if (local_channel() && strpos($search, 'https://') === 0 && !$update && !$load) {
$j = Activity::fetch(punify($search), App::get_channel());
if ($j) {
$AS = new ActivityStreams($j);
if ($AS->is_valid()) {
// check if is_an_actor, otherwise import activity
if (is_array($AS->obj) && !ActivityStreams::is_an_actor($AS->obj)) {
$item = Activity::decode_note($AS);
if ($item) {
logger('parsed_item: ' . print_r($item, true), LOGGER_DATA);
Activity::store(App::get_channel(), $observer_hash, $AS, $item, true, true);
goaway(z_root() . '/display/' . gen_link_id($item['mid']));
}
}
}
$f = Libzot::fetch_conversation(App::get_channel(), punify($search), true);
if ($f) {
goaway(z_root() . '/hq/' . gen_link_id($f['message_id']));
}
else {
// try other fetch providers (e.g. diaspora)
// try other fetch providers (e.g. diaspora, pubcrawl)
$hookdata = [
'channel' => App::get_channel(),
'data' => $search
'url' => punify($search)
];
call_hooks('fetch_provider', $hookdata);
}

View File

@@ -2,6 +2,10 @@
namespace Zotlabs\Module\Settings;
use App;
use Zotlabs\Access\PermissionLimits;
use Zotlabs\Access\PermissionRoles;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Apps;
use Zotlabs\Lib\Libsync;
@@ -10,597 +14,272 @@ require_once('include/selectors.php');
class Channel {
function post() {
$channel = \App::get_channel();
check_form_security_token_redirectOnErr('/settings', 'settings');
call_hooks('settings_post', $_POST);
$set_perms = '';
$channel = App::get_channel();
$role = ((x($_POST, 'permissions_role')) ? notags(trim($_POST['permissions_role'])) : '');
$timezone = ((x($_POST, 'timezone_select')) ? notags(trim($_POST['timezone_select'])) : '');
$defloc = ((x($_POST, 'defloc')) ? notags(trim($_POST['defloc'])) : '');
$evdays = ((x($_POST, 'evdays')) ? intval($_POST['evdays']) : 3);
$photo_path = ((x($_POST, 'photo_path')) ? escape_tags(trim($_POST['photo_path'])) : '');
$attach_path = ((x($_POST, 'attach_path')) ? escape_tags(trim($_POST['attach_path'])) : '');
$allow_location = (((x($_POST, 'allow_location')) && (intval($_POST['allow_location']) == 1)) ? 1 : 0);
$post_newfriend = (($_POST['post_newfriend'] == 1) ? 1 : 0);
$post_joingroup = (($_POST['post_joingroup'] == 1) ? 1 : 0);
$post_profilechange = (($_POST['post_profilechange'] == 1) ? 1 : 0);
$adult = (($_POST['adult'] == 1) ? 1 : 0);
$mailhost = ((array_key_exists('mailhost', $_POST)) ? notags(trim($_POST['mailhost'])) : '');
$pageflags = $channel['channel_pageflags'];
$existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0);
$expire = ((x($_POST, 'expire')) ? intval($_POST['expire']) : 0);
$role = ((x($_POST,'permissions_role')) ? notags(trim($_POST['permissions_role'])) : '');
$oldrole = get_pconfig(local_channel(),'system','permissions_role');
// This mapping can be removed after 3.4 release
if($oldrole === 'social_party') {
$oldrole = 'social_federation';
}
if(($role != $oldrole) || ($role === 'custom')) {
if($role === 'custom') {
$hide_presence = (((x($_POST,'hide_presence')) && (intval($_POST['hide_presence']) == 1)) ? 1: 0);
$publish = (((x($_POST,'profile_in_directory')) && (intval($_POST['profile_in_directory']) == 1)) ? 1: 0);
$def_group = ((x($_POST,'group-selection')) ? notags(trim($_POST['group-selection'])) : '');
$r = q("update channel set channel_default_group = '%s' where channel_id = %d",
dbesc($def_group),
intval(local_channel())
);
$global_perms = \Zotlabs\Access\Permissions::Perms();
foreach($global_perms as $k => $v) {
\Zotlabs\Access\PermissionLimits::Set(local_channel(),$k,intval($_POST[$k]));
}
$acl = new \Zotlabs\Access\AccessList($channel);
$acl->set_from_array($_POST);
$x = $acl->get();
$r = q("update channel set channel_allow_cid = '%s', channel_allow_gid = '%s',
channel_deny_cid = '%s', channel_deny_gid = '%s' where channel_id = %d",
dbesc($x['allow_cid']),
dbesc($x['allow_gid']),
dbesc($x['deny_cid']),
dbesc($x['deny_gid']),
intval(local_channel())
);
}
else {
$role_permissions = \Zotlabs\Access\PermissionRoles::role_perms($_POST['permissions_role']);
if(! $role_permissions) {
notice('Permissions category could not be found.');
return;
}
$hide_presence = 1 - (intval($role_permissions['online']));
if($role_permissions['default_collection']) {
$r = q("select hash from pgrp where uid = %d and gname = '%s' limit 1",
intval(local_channel()),
dbesc( t('Friends') )
);
if(! $r) {
require_once('include/group.php');
group_add(local_channel(), t('Friends'));
group_add_member(local_channel(),t('Friends'),$channel['channel_hash']);
$r = q("select hash from pgrp where uid = %d and gname = '%s' limit 1",
intval(local_channel()),
dbesc( t('Friends') )
);
}
if($r) {
q("update channel set channel_default_group = '%s', channel_allow_gid = '%s', channel_allow_cid = '', channel_deny_gid = '', channel_deny_cid = '' where channel_id = %d",
dbesc($r[0]['hash']),
dbesc('<' . $r[0]['hash'] . '>'),
intval(local_channel())
);
}
else {
notice( sprintf('Default privacy group \'%s\' not found. Please create and re-submit permission change.', t('Friends')) . EOL);
return;
}
}
// no default collection
else {
q("update channel set channel_default_group = '', channel_allow_gid = '', channel_allow_cid = '', channel_deny_gid = '',
channel_deny_cid = '' where channel_id = %d",
intval(local_channel())
);
}
if($role_permissions['perms_connect']) {
$x = \Zotlabs\Access\Permissions::FilledPerms($role_permissions['perms_connect']);
foreach($x as $k => $v) {
set_abconfig(local_channel(),$channel['channel_hash'],'my_perms',$k, $v);
if($role_permissions['perms_auto']) {
set_pconfig(local_channel(),'autoperms',$k,$v);
}
else {
del_pconfig(local_channel(),'autoperms',$k);
}
}
}
if($role_permissions['limits']) {
foreach($role_permissions['limits'] as $k => $v) {
\Zotlabs\Access\PermissionLimits::Set(local_channel(),$k,$v);
}
}
if(array_key_exists('directory_publish',$role_permissions)) {
$publish = intval($role_permissions['directory_publish']);
}
}
set_pconfig(local_channel(),'system','hide_online_status',$hide_presence);
set_pconfig(local_channel(),'system','permissions_role',$role);
}
$username = ((x($_POST,'username')) ? notags(trim($_POST['username'])) : '');
$timezone = ((x($_POST,'timezone_select')) ? notags(trim($_POST['timezone_select'])) : '');
$defloc = ((x($_POST,'defloc')) ? notags(trim($_POST['defloc'])) : '');
$openid = ((x($_POST,'openid_url')) ? notags(trim($_POST['openid_url'])) : '');
$maxreq = ((x($_POST,'maxreq')) ? intval($_POST['maxreq']) : 0);
$expire = ((x($_POST,'expire')) ? intval($_POST['expire']) : 0);
$evdays = ((x($_POST,'evdays')) ? intval($_POST['evdays']) : 3);
$photo_path = ((x($_POST,'photo_path')) ? escape_tags(trim($_POST['photo_path'])) : '');
$attach_path = ((x($_POST,'attach_path')) ? escape_tags(trim($_POST['attach_path'])) : '');
$expire_items = ((x($_POST,'expire_items')) ? intval($_POST['expire_items']) : 0);
$expire_starred = ((x($_POST,'expire_starred')) ? intval($_POST['expire_starred']) : 0);
$expire_photos = ((x($_POST,'expire_photos'))? intval($_POST['expire_photos']) : 0);
$expire_network_only = ((x($_POST,'expire_network_only'))? intval($_POST['expire_network_only']) : 0);
$allow_location = (((x($_POST,'allow_location')) && (intval($_POST['allow_location']) == 1)) ? 1: 0);
$blocktags = (((x($_POST,'blocktags')) && (intval($_POST['blocktags']) == 1)) ? 0: 1); // this setting is inverted!
$unkmail = (((x($_POST,'unkmail')) && (intval($_POST['unkmail']) == 1)) ? 1: 0);
$cntunkmail = ((x($_POST,'cntunkmail')) ? intval($_POST['cntunkmail']) : 0);
$suggestme = ((x($_POST,'suggestme')) ? intval($_POST['suggestme']) : 0);
$autoperms = ((x($_POST,'autoperms')) ? intval($_POST['autoperms']) : 0);
$post_newfriend = (($_POST['post_newfriend'] == 1) ? 1: 0);
$post_joingroup = (($_POST['post_joingroup'] == 1) ? 1: 0);
$post_profilechange = (($_POST['post_profilechange'] == 1) ? 1: 0);
$adult = (($_POST['adult'] == 1) ? 1 : 0);
$defpermcat = ((x($_POST,'defpermcat')) ? notags(trim($_POST['defpermcat'])) : 'default');
$mailhost = ((array_key_exists('mailhost',$_POST)) ? notags(trim($_POST['mailhost'])) : '');
$pageflags = $channel['channel_pageflags'];
$existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0);
if($adult != $existing_adult)
if ($adult != $existing_adult) {
$pageflags = ($pageflags ^ PAGE_ADULT);
}
$notify = 0;
if(x($_POST,'notify1'))
if (x($_POST, 'notify1'))
$notify += intval($_POST['notify1']);
if(x($_POST,'notify2'))
if (x($_POST, 'notify2'))
$notify += intval($_POST['notify2']);
if(x($_POST,'notify3'))
if (x($_POST, 'notify3'))
$notify += intval($_POST['notify3']);
if(x($_POST,'notify4'))
if (x($_POST, 'notify4'))
$notify += intval($_POST['notify4']);
if(x($_POST,'notify5'))
if (x($_POST, 'notify5'))
$notify += intval($_POST['notify5']);
if(x($_POST,'notify6'))
if (x($_POST, 'notify6'))
$notify += intval($_POST['notify6']);
if(x($_POST,'notify7'))
if (x($_POST, 'notify7'))
$notify += intval($_POST['notify7']);
if(x($_POST,'notify8'))
if (x($_POST, 'notify8'))
$notify += intval($_POST['notify8']);
$vnotify = 0;
if(x($_POST,'vnotify1'))
if (x($_POST, 'vnotify1'))
$vnotify += intval($_POST['vnotify1']);
if(x($_POST,'vnotify2'))
if (x($_POST, 'vnotify2'))
$vnotify += intval($_POST['vnotify2']);
if(x($_POST,'vnotify3'))
if (x($_POST, 'vnotify3'))
$vnotify += intval($_POST['vnotify3']);
if(x($_POST,'vnotify4'))
if (x($_POST, 'vnotify4'))
$vnotify += intval($_POST['vnotify4']);
if(x($_POST,'vnotify5'))
if (x($_POST, 'vnotify5'))
$vnotify += intval($_POST['vnotify5']);
if(x($_POST,'vnotify6'))
if (x($_POST, 'vnotify6'))
$vnotify += intval($_POST['vnotify6']);
if(x($_POST,'vnotify7'))
if (x($_POST, 'vnotify7'))
$vnotify += intval($_POST['vnotify7']);
if(x($_POST,'vnotify8'))
if (x($_POST, 'vnotify8'))
$vnotify += intval($_POST['vnotify8']);
if(x($_POST,'vnotify9'))
if (x($_POST, 'vnotify9'))
$vnotify += intval($_POST['vnotify9']);
if(x($_POST,'vnotify10'))
if (x($_POST, 'vnotify10'))
$vnotify += intval($_POST['vnotify10']);
if(x($_POST,'vnotify11') && is_site_admin())
if (x($_POST, 'vnotify11') && is_site_admin())
$vnotify += intval($_POST['vnotify11']);
if(x($_POST,'vnotify12'))
if (x($_POST, 'vnotify12'))
$vnotify += intval($_POST['vnotify12']);
if(x($_POST,'vnotify13'))
if (x($_POST, 'vnotify13'))
$vnotify += intval($_POST['vnotify13']);
if(x($_POST,'vnotify14'))
if (x($_POST, 'vnotify14'))
$vnotify += intval($_POST['vnotify14']);
if(x($_POST,'vnotify15'))
if (x($_POST, 'vnotify15'))
$vnotify += intval($_POST['vnotify15']);
$always_show_in_notices = x($_POST,'always_show_in_notices') ? 1 : 0;
$always_show_in_notices = ((x($_POST, 'always_show_in_notices')) ? 1 : 0);
$update_notices_per_parent = ((x($_POST, 'update_notices_per_parent')) ? 1 : 0);
$err = '';
$name_change = false;
if($username != $channel['channel_name']) {
$name_change = true;
require_once('include/channel.php');
$err = validate_channelname($username);
if($err) {
notice($err);
return;
}
}
if($timezone != $channel['channel_timezone']) {
if(strlen($timezone))
if ($timezone !== $channel['channel_timezone']) {
if (strlen($timezone))
date_default_timezone_set($timezone);
}
set_pconfig(local_channel(),'system','use_browser_location',$allow_location);
set_pconfig(local_channel(),'system','suggestme', $suggestme);
set_pconfig(local_channel(),'system','post_newfriend', $post_newfriend);
set_pconfig(local_channel(),'system','post_joingroup', $post_joingroup);
set_pconfig(local_channel(),'system','post_profilechange', $post_profilechange);
set_pconfig(local_channel(),'system','blocktags',$blocktags);
set_pconfig(local_channel(),'system','vnotify',$vnotify);
set_pconfig(local_channel(),'system','always_show_in_notices',$always_show_in_notices);
set_pconfig(local_channel(),'system','evdays',$evdays);
set_pconfig(local_channel(),'system','photo_path',$photo_path);
set_pconfig(local_channel(),'system','attach_path',$attach_path);
set_pconfig(local_channel(),'system','default_permcat',$defpermcat);
set_pconfig(local_channel(),'system','email_notify_host',$mailhost);
set_pconfig(local_channel(),'system','autoperms',$autoperms);
if (!$role) {
notice(t('Please select a channel role') . EOL);
return;
}
$r = q("update channel set channel_name = '%s', channel_pageflags = %d, channel_timezone = '%s', channel_location = '%s', channel_notifyflags = %d, channel_max_anon_mail = %d, channel_max_friend_req = %d, channel_expire_days = %d $set_perms where channel_id = %d",
dbesc($username),
if ($role !== get_pconfig(local_channel(), 'system', 'permissions_role')) {
$role_permissions = PermissionRoles::role_perms($_POST['permissions_role']);
if (isset($role_permissions['limits'])) {
foreach ($role_permissions['limits'] as $k => $v) {
PermissionLimits::Set(local_channel(), $k, $v);
}
}
set_pconfig(local_channel(), 'system', 'group_actor', 0);
if (isset($role_permissions['channel_type']) && $role_permissions['channel_type'] === 'group') {
set_pconfig(local_channel(), 'system', 'group_actor', 1);
}
}
set_pconfig(local_channel(), 'system', 'permissions_role', $role);
set_pconfig(local_channel(), 'system', 'use_browser_location', $allow_location);
set_pconfig(local_channel(), 'system', 'post_newfriend', $post_newfriend);
set_pconfig(local_channel(), 'system', 'post_joingroup', $post_joingroup);
set_pconfig(local_channel(), 'system', 'post_profilechange', $post_profilechange);
set_pconfig(local_channel(), 'system', 'vnotify', $vnotify);
set_pconfig(local_channel(), 'system', 'always_show_in_notices', $always_show_in_notices);
set_pconfig(local_channel(), 'system', 'update_notices_per_parent', $update_notices_per_parent);
set_pconfig(local_channel(), 'system', 'evdays', $evdays);
set_pconfig(local_channel(), 'system', 'photo_path', $photo_path);
set_pconfig(local_channel(), 'system', 'attach_path', $attach_path);
set_pconfig(local_channel(), 'system', 'email_notify_host', $mailhost);
$r = q("update channel set channel_pageflags = %d, channel_timezone = '%s',
channel_location = '%s', channel_notifyflags = %d, channel_expire_days = %d
where channel_id = %d",
intval($pageflags),
dbesc($timezone),
dbesc($defloc),
intval($notify),
intval($unkmail),
intval($maxreq),
intval($expire),
intval(local_channel())
);
if($r)
info( t('Settings updated.') . EOL);
if(! is_null($publish)) {
$r = q("UPDATE profile SET publish = %d WHERE is_default = 1 AND uid = %d",
intval($publish),
intval(local_channel())
);
}
if($name_change) {
// change name on all associated xchans by matching the url
$r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s' where xchan_url = '%s'",
dbesc($username),
dbesc(datetime_convert()),
dbesc(z_root() . '/channel/' . $channel['channel_address'])
);
$r = q("update profile set fullname = '%s' where uid = %d and is_default = 1",
dbesc($username),
intval($channel['channel_id'])
);
}
\Zotlabs\Daemon\Master::Summon(array('Directory',local_channel()));
if ($r)
info(t('Settings updated.') . EOL);
Master::Summon(['Directory', local_channel()]);
Libsync::build_sync_packet();
if($email_changed && \App::$config['system']['register_policy'] == REGISTER_VERIFY) {
if ($email_changed && App::$config['system']['register_policy'] == REGISTER_VERIFY) {
// FIXME - set to un-verified, blocked and redirect to logout
// Q: Why? Are we verifying people or email addresses?
// A: the policy is to verify email addresses
}
goaway(z_root() . '/settings' );
goaway(z_root() . '/settings');
return; // NOTREACHED
}
function get() {
require_once('include/acl_selectors.php');
require_once('include/permissions.php');
load_pconfig(local_channel());
$channel = App::get_channel();
$nickname = $channel['channel_address'];
$timezone = $channel['channel_timezone'];
$notify = $channel['channel_notifyflags'];
$defloc = $channel['channel_location'];
$adult_flag = intval($channel['channel_pageflags'] & PAGE_ADULT);
$post_newfriend = get_pconfig(local_channel(), 'system', 'post_newfriend');
$post_newfriend = (($post_newfriend === false) ? '0' : $post_newfriend); // default if not set: 0
$post_joingroup = get_pconfig(local_channel(), 'system', 'post_joingroup');
$post_joingroup = (($post_joingroup === false) ? '0' : $post_joingroup); // default if not set: 0
$post_profilechange = get_pconfig(local_channel(), 'system', 'post_profilechange');
$post_profilechange = (($post_profilechange === false) ? '0' : $post_profilechange); // default if not set: 0
$subdir = ((strlen(App::get_path())) ? '<br />' . t('or') . ' ' . z_root() . '/channel/' . $nickname : '');
$webbie = $nickname . '@' . App::get_hostname();
$intl_nickname = unpunify($nickname) . '@' . unpunify(App::get_hostname());
$disable_discover_tab = intval(get_config('system', 'disable_discover_tab', 1)) == 1;
$site_firehose = intval(get_config('system', 'site_firehose', 0)) == 1;
$yes_no = array(t('No'),t('Yes'));
$expire = $channel['channel_expire_days'];
$sys_expire = get_config('system', 'default_expire_days');
$p = q("SELECT * FROM profile WHERE is_default = 1 AND uid = %d LIMIT 1",
intval(local_channel())
);
if(count($p))
$profile = $p[0];
load_pconfig(local_channel(),'expire');
$channel = \App::get_channel();
$global_perms = \Zotlabs\Access\Permissions::Perms();
$permiss = array();
$perm_opts = array(
array( t('Nobody except yourself'), 0),
array( t('Only those you specifically allow'), PERMS_SPECIFIC),
array( t('Approved connections'), PERMS_CONTACTS),
array( t('Any connections'), PERMS_PENDING),
array( t('Anybody on this website'), PERMS_SITE),
array( t('Anybody in this network'), PERMS_NETWORK),
array( t('Anybody authenticated'), PERMS_AUTHED),
array( t('Anybody on the internet'), PERMS_PUBLIC)
);
$limits = \Zotlabs\Access\PermissionLimits::Get(local_channel());
$anon_comments = get_config('system','anonymous_comments',true);
foreach($global_perms as $k => $perm) {
$options = array();
$can_be_public = ((strstr($k,'view') || ($k === 'post_comments' && $anon_comments)) ? true : false);
foreach($perm_opts as $opt) {
if($opt[1] == PERMS_PUBLIC && (! $can_be_public))
continue;
$options[$opt[1]] = $opt[0];
}
$permiss[] = array($k,$perm,$limits[$k],'',$options);
}
// logger('permiss: ' . print_r($permiss,true));
$username = $channel['channel_name'];
$nickname = $channel['channel_address'];
$timezone = $channel['channel_timezone'];
$notify = $channel['channel_notifyflags'];
$defloc = $channel['channel_location'];
$maxreq = $channel['channel_max_friend_req'];
$expire = $channel['channel_expire_days'];
$adult_flag = intval($channel['channel_pageflags'] & PAGE_ADULT);
$sys_expire = get_config('system','default_expire_days');
// $unkmail = \App::$user['unkmail'];
// $cntunkmail = \App::$user['cntunkmail'];
$hide_presence = intval(get_pconfig(local_channel(), 'system','hide_online_status'));
$expire_items = get_pconfig(local_channel(), 'expire','items');
$expire_items = (($expire_items===false)? '1' : $expire_items); // default if not set: 1
$expire_notes = get_pconfig(local_channel(), 'expire','notes');
$expire_notes = (($expire_notes===false)? '1' : $expire_notes); // default if not set: 1
$expire_starred = get_pconfig(local_channel(), 'expire','starred');
$expire_starred = (($expire_starred===false)? '1' : $expire_starred); // default if not set: 1
$expire_photos = get_pconfig(local_channel(), 'expire','photos');
$expire_photos = (($expire_photos===false)? '0' : $expire_photos); // default if not set: 0
$expire_network_only = get_pconfig(local_channel(), 'expire','network_only');
$expire_network_only = (($expire_network_only===false)? '0' : $expire_network_only); // default if not set: 0
$suggestme = get_pconfig(local_channel(), 'system','suggestme');
$suggestme = (($suggestme===false)? '0': $suggestme); // default if not set: 0
$post_newfriend = get_pconfig(local_channel(), 'system','post_newfriend');
$post_newfriend = (($post_newfriend===false)? '0': $post_newfriend); // default if not set: 0
$post_joingroup = get_pconfig(local_channel(), 'system','post_joingroup');
$post_joingroup = (($post_joingroup===false)? '0': $post_joingroup); // default if not set: 0
$post_profilechange = get_pconfig(local_channel(), 'system','post_profilechange');
$post_profilechange = (($post_profilechange===false)? '0': $post_profilechange); // default if not set: 0
$blocktags = get_pconfig(local_channel(),'system','blocktags');
$blocktags = (($blocktags===false) ? '0' : $blocktags);
$timezone = date_default_timezone_get();
$opt_tpl = get_markup_template("field_checkbox.tpl");
if(get_config('system','publish_all')) {
$profile_in_dir = '<input type="hidden" name="profile_in_directory" value="1" />';
}
else {
$profile_in_dir = replace_macros($opt_tpl,array(
'$field' => array('profile_in_directory', t('Publish your default profile in the network directory'), $profile['publish'], '', $yes_no),
));
}
$suggestme = replace_macros($opt_tpl,array(
'$field' => array('suggestme', t('Allow us to suggest you as a potential friend to new members?'), $suggestme, '', $yes_no),
));
$subdir = ((strlen(\App::get_path())) ? '<br />' . t('or') . ' ' . z_root() . '/channel/' . $nickname : '');
$webbie = $nickname . '@' . \App::get_hostname();
$intl_nickname = unpunify($nickname) . '@' . unpunify(\App::get_hostname());
$tpl_addr = get_markup_template("settings_nick_set.tpl");
$prof_addr = replace_macros($tpl_addr,array(
'$desc' => t('Your channel address is'),
$tpl_addr = get_markup_template("settings_nick_set.tpl");
$prof_addr = replace_macros($tpl_addr, [
'$desc' => t('Your channel address is'),
'$nickname' => (($intl_nickname === $webbie) ? $webbie : $intl_nickname . '&nbsp;(' . $webbie . ')'),
'$subdir' => $subdir,
'$davdesc' => t('Your files/photos are accessible via WebDAV at'),
'$davpath' => z_root() . '/dav/' . $nickname,
'$basepath' => \App::get_hostname()
));
'$subdir' => $subdir,
'$davdesc' => t('Your files/photos are accessible via WebDAV at'),
'$davpath' => z_root() . '/dav/' . $nickname,
'$basepath' => App::get_hostname()
]);
$pcat = new \Zotlabs\Lib\Permcat(local_channel());
$pcatlist = $pcat->listing();
$permcats = [];
if($pcatlist) {
foreach($pcatlist as $pc) {
$permcats[$pc['name']] = $pc['localname'];
}
}
$default_permcat = get_pconfig(local_channel(),'system','default_permcat','default');
$stpl = get_markup_template('settings.tpl');
$acl = new \Zotlabs\Access\AccessList($channel);
$perm_defaults = $acl->get();
require_once('include/group.php');
$group_select = mini_group_select(local_channel(),$channel['channel_default_group']);
$evdays = get_pconfig(local_channel(),'system','evdays');
if(! $evdays)
$evdays = get_pconfig(local_channel(), 'system', 'evdays');
if (!$evdays)
$evdays = 3;
$permissions_role = get_pconfig(local_channel(),'system','permissions_role');
if(! $permissions_role)
$permissions_role = 'custom';
// compatibility mapping - can be removed after 3.4 release
if($permissions_role === 'social_party')
$permissions_role = 'social_federation';
$always_show_in_notices = get_pconfig(local_channel(), 'system', 'always_show_in_notices');
$update_notices_per_parent = get_pconfig(local_channel(), 'system', 'update_notices_per_parent', 1);
if(in_array($permissions_role,['forum','repository']))
$autoperms = replace_macros(get_markup_template('field_checkbox.tpl'), [
'$field' => [ 'autoperms',t('Automatic membership approval'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('If enabled, connection requests will be approved without your interaction'), $yes_no ]]);
else
$autoperms = '<input type="hidden" name="autoperms" value="' . intval(get_pconfig(local_channel(),'system','autoperms')) . '" />';
$permissions_set = (($permissions_role != 'custom') ? true : false);
$perm_roles = \Zotlabs\Access\PermissionRoles::roles();
$vnotify = get_pconfig(local_channel(),'system','vnotify');
$always_show_in_notices = get_pconfig(local_channel(),'system','always_show_in_notices');
if($vnotify === false)
$vnotify = get_pconfig(local_channel(), 'system', 'vnotify');
if ($vnotify === false)
$vnotify = (-1);
$plugin = [ 'basic' => '', 'security' => '', 'notify' => '' ];
call_hooks('channel_settings',$plugin);
$perm_roles = PermissionRoles::channel_roles();
$permissions_role = get_pconfig(local_channel(), 'system', 'permissions_role');
$disable_discover_tab = intval(get_config('system','disable_discover_tab',1)) == 1;
$site_firehose = intval(get_config('system','site_firehose',0)) == 1;
if (!in_array($permissions_role, ['public', 'personal', 'group', 'custom'])) {
notice(t('Please select a channel role') . EOL);
array_unshift($perm_roles , '');
}
$plugin = ['basic' => '', 'notify' => ''];
call_hooks('channel_settings', $plugin);
$o .= replace_macros($stpl,array(
'$ptitle' => t('Channel Settings'),
$yes_no = [t('No'), t('Yes')];
'$submit' => t('Submit'),
'$baseurl' => z_root(),
'$uid' => local_channel(),
'$form_security_token' => get_form_security_token("settings"),
'$nickname_block' => $prof_addr,
'$h_basic' => t('Basic Settings'),
'$username' => array('username', t('Full Name:'), $username,''),
'$email' => array('email', t('Email Address:'), $email, ''),
'$timezone' => array('timezone_select' , t('Your Timezone:'), $timezone, '', get_timezones()),
'$defloc' => array('defloc', t('Default Post Location:'), $defloc, t('Geographical location to display on your posts')),
'$allowloc' => array('allow_location', t('Use Browser Location:'), ((get_pconfig(local_channel(),'system','use_browser_location')) ? 1 : ''), '', $yes_no),
'$adult' => array('adult', t('Adult Content'), $adult_flag, t('This channel frequently or regularly publishes adult content. (Please tag any adult material and/or nudity with #NSFW)'), $yes_no),
'$h_prv' => t('Security and Privacy Settings'),
'$permissions_set' => $permissions_set,
'$perms_set_msg' => t('Your permissions are already configured. Click to view/adjust'),
'$hide_presence' => array('hide_presence', t('Hide my online presence'),$hide_presence, t('Prevents displaying in your profile that you are online'), $yes_no),
'$lbl_pmacro' => t('Simple Privacy Settings:'),
'$pmacro3' => t('Very Public - <em>extremely permissive (should be used with caution)</em>'),
'$pmacro2' => t('Typical - <em>default public, privacy when desired (similar to social network permissions but with improved privacy)</em>'),
'$pmacro1' => t('Private - <em>default private, never open or public</em>'),
'$pmacro0' => t('Blocked - <em>default blocked to/from everybody</em>'),
'$permiss_arr' => $permiss,
'$blocktags' => array('blocktags',t('Allow others to tag your posts'), 1-$blocktags, t('Often used by the community to retro-actively flag inappropriate content'), $yes_no),
'$lbl_p2macro' => t('Channel Permission Limits'),
'$expire' => array('expire',t('Expire other channel content after this many days'),$expire, t('0 or blank to use the website limit.') . ' ' . ((intval($sys_expire)) ? sprintf( t('This website expires after %d days.'),intval($sys_expire)) : t('This website does not expire imported content.')) . ' ' . t('The website limit takes precedence if lower than your limit.')),
'$maxreq' => array('maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']) , t('May reduce spam activity')),
'$permissions' => t('Default Privacy Group'),
'$permdesc' => t("\x28click to open/close\x29"),
'$aclselect' => populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromDescription(t('Use my default audience setting for the type of object published'))),
'$allow_cid' => acl2json($perm_defaults['allow_cid']),
'$allow_gid' => acl2json($perm_defaults['allow_gid']),
'$deny_cid' => acl2json($perm_defaults['deny_cid']),
'$deny_gid' => acl2json($perm_defaults['deny_gid']),
'$suggestme' => $suggestme,
'$group_select' => $group_select,
'$role' => array('permissions_role' , t('Channel role and privacy'), $permissions_role, '', $perm_roles),
'$defpermcat' => [ 'defpermcat', t('Default permissions category'), $default_permcat, '', $permcats ],
'$permcat_enable' => Apps::system_app_installed(local_channel(), 'Permission Categories'),
'$profile_in_dir' => $profile_in_dir,
'$hide_friends' => $hide_friends,
'$hide_wall' => $hide_wall,
'$unkmail' => $unkmail,
'$cntunkmail' => array('cntunkmail', t('Maximum private messages per day from unknown people:'), intval($channel['channel_max_anon_mail']) ,t("Useful to reduce spamming")),
'$autoperms' => $autoperms,
'$h_not' => t('Notification Settings'),
'$activity_options' => t('By default post a status message when:'),
'$post_newfriend' => array('post_newfriend', t('accepting a friend request'), $post_newfriend, '', $yes_no),
'$post_joingroup' => array('post_joingroup', t('joining a forum/community'), $post_joingroup, '', $yes_no),
'$post_profilechange' => array('post_profilechange', t('making an <em>interesting</em> profile change'), $post_profilechange, '', $yes_no),
'$lbl_not' => t('Send a notification email when:'),
'$notify1' => array('notify1', t('You receive a connection request'), ($notify & NOTIFY_INTRO), NOTIFY_INTRO, '', $yes_no),
'$notify2' => array('notify2', t('Your connections are confirmed'), ($notify & NOTIFY_CONFIRM), NOTIFY_CONFIRM, '', $yes_no),
'$notify3' => array('notify3', t('Someone writes on your profile wall'), ($notify & NOTIFY_WALL), NOTIFY_WALL, '', $yes_no),
'$notify4' => array('notify4', t('Someone writes a followup comment'), ($notify & NOTIFY_COMMENT), NOTIFY_COMMENT, '', $yes_no),
'$notify5' => array('notify5', t('You receive a private message'), ($notify & NOTIFY_MAIL), NOTIFY_MAIL, '', $yes_no),
'$notify6' => array('notify6', t('You receive a friend suggestion'), ($notify & NOTIFY_SUGGEST), NOTIFY_SUGGEST, '', $yes_no),
'$notify7' => array('notify7', t('You are tagged in a post'), ($notify & NOTIFY_TAGSELF), NOTIFY_TAGSELF, '', $yes_no),
'$notify8' => array('notify8', t('You are poked/prodded/etc. in a post'), ($notify & NOTIFY_POKE), NOTIFY_POKE, '', $yes_no),
'$notify9' => array('notify9', t('Someone likes your post/comment'), ($notify & NOTIFY_LIKE), NOTIFY_LIKE, '', $yes_no),
'$lbl_vnot' => t('Show visual notifications including:'),
'$vnotify1' => array('vnotify1', t('Unseen stream activity'), ($vnotify & VNOTIFY_NETWORK), VNOTIFY_NETWORK, '', $yes_no),
'$vnotify2' => array('vnotify2', t('Unseen channel activity'), ($vnotify & VNOTIFY_CHANNEL), VNOTIFY_CHANNEL, '', $yes_no),
'$vnotify3' => array('vnotify3', t('Unseen private messages'), ($vnotify & VNOTIFY_MAIL), VNOTIFY_MAIL, t('Recommended'), $yes_no),
'$vnotify4' => array('vnotify4', t('Upcoming events'), ($vnotify & VNOTIFY_EVENT), VNOTIFY_EVENT, '', $yes_no),
'$vnotify5' => array('vnotify5', t('Events today'), ($vnotify & VNOTIFY_EVENTTODAY), VNOTIFY_EVENTTODAY, '', $yes_no),
'$vnotify6' => array('vnotify6', t('Upcoming birthdays'), ($vnotify & VNOTIFY_BIRTHDAY), VNOTIFY_BIRTHDAY, t('Not available in all themes'), $yes_no),
'$vnotify7' => array('vnotify7', t('System (personal) notifications'), ($vnotify & VNOTIFY_SYSTEM), VNOTIFY_SYSTEM, '', $yes_no),
'$vnotify8' => array('vnotify8', t('System info messages'), ($vnotify & VNOTIFY_INFO), VNOTIFY_INFO, t('Recommended'), $yes_no),
'$vnotify9' => array('vnotify9', t('System critical alerts'), ($vnotify & VNOTIFY_ALERT), VNOTIFY_ALERT, t('Recommended'), $yes_no),
'$vnotify10' => array('vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no),
'$vnotify11' => ((is_site_admin()) ? array('vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no) : array()),
'$vnotify12' => array('vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no),
'$vnotify13' => ((($disable_discover_tab && !$site_firehose) || !Apps::system_app_installed(local_channel(), 'Public Stream')) ? array() : array('vnotify13', t('Unseen public stream activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no)),
'$vnotify14' => array('vnotify14', t('Unseen likes and dislikes'), ($vnotify & VNOTIFY_LIKE), VNOTIFY_LIKE, '', $yes_no),
'$vnotify15' => array('vnotify15', t('Unseen forum posts'), ($vnotify & VNOTIFY_FORUMS), VNOTIFY_FORUMS, '', $yes_no),
'$mailhost' => [ 'mailhost', t('Email notification hub (hostname)'), get_pconfig(local_channel(),'system','email_notify_host',\App::get_hostname()), sprintf( t('If your channel is mirrored to multiple hubs, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'),\App::get_hostname()) ],
'$always_show_in_notices' => array('always_show_in_notices', t('Show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no),
'$desktop_notifications_info' => t('Desktop notifications are unavailable because the required browser permission has not been granted'),
$stpl = get_markup_template('settings.tpl');
$o = replace_macros($stpl, [
'$ptitle' => t('Channel Settings'),
'$submit' => t('Submit'),
'$baseurl' => z_root(),
'$uid' => local_channel(),
'$form_security_token' => get_form_security_token("settings"),
'$role' => ['permissions_role', t('Channel role'), $permissions_role, '', $perm_roles],
'$nickname_block' => $prof_addr,
'$h_basic' => t('Basic Settings'),
'$timezone' => ['timezone_select', t('Channel timezone:'), $timezone, '', get_timezones()],
'$defloc' => ['defloc', t('Default post location:'), $defloc, t('Geographical location to display on your posts')],
'$allowloc' => ['allow_location', t('Use browser location'), ((get_pconfig(local_channel(), 'system', 'use_browser_location')) ? 1 : ''), '', $yes_no],
'$adult' => ['adult', t('Adult content'), $adult_flag, t('This channel frequently or regularly publishes adult content'), $yes_no],
'$maxreq' => ['maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']), t('May reduce spam activity')],
'$h_not' => t('Notification Settings'),
'$activity_options' => t('By default post a status message when:'),
'$post_newfriend' => ['post_newfriend', t('accepting a friend request'), $post_newfriend, '', $yes_no],
'$post_joingroup' => ['post_joingroup', t('joining a forum/community'), $post_joingroup, '', $yes_no],
'$post_profilechange' => ['post_profilechange', t('making an <em>interesting</em> profile change'), $post_profilechange, '', $yes_no],
'$lbl_not' => t('Send a notification email when:'),
'$notify1' => ['notify1', t('You receive a connection request'), ($notify & NOTIFY_INTRO), NOTIFY_INTRO, '', $yes_no],
'$notify2' => ['notify2', t('Your connections are confirmed'), ($notify & NOTIFY_CONFIRM), NOTIFY_CONFIRM, '', $yes_no],
'$notify3' => ['notify3', t('Someone writes on your profile wall'), ($notify & NOTIFY_WALL), NOTIFY_WALL, '', $yes_no],
'$notify4' => ['notify4', t('Someone writes a followup comment'), ($notify & NOTIFY_COMMENT), NOTIFY_COMMENT, '', $yes_no],
'$notify5' => ['notify5', t('You receive a private message'), ($notify & NOTIFY_MAIL), NOTIFY_MAIL, '', $yes_no],
'$notify6' => ['notify6', t('You receive a friend suggestion'), ($notify & NOTIFY_SUGGEST), NOTIFY_SUGGEST, '', $yes_no],
'$notify7' => ['notify7', t('You are tagged in a post'), ($notify & NOTIFY_TAGSELF), NOTIFY_TAGSELF, '', $yes_no],
'$notify8' => ['notify8', t('You are poked/prodded/etc. in a post'), ($notify & NOTIFY_POKE), NOTIFY_POKE, '', $yes_no],
'$notify9' => ['notify9', t('Someone likes your post/comment'), ($notify & NOTIFY_LIKE), NOTIFY_LIKE, '', $yes_no],
'$lbl_vnot' => t('Show visual notifications including:'),
'$vnotify1' => ['vnotify1', t('Unseen stream activity'), ($vnotify & VNOTIFY_NETWORK), VNOTIFY_NETWORK, '', $yes_no],
'$vnotify2' => ['vnotify2', t('Unseen channel activity'), ($vnotify & VNOTIFY_CHANNEL), VNOTIFY_CHANNEL, '', $yes_no],
'$vnotify3' => ['vnotify3', t('Unseen private messages'), ($vnotify & VNOTIFY_MAIL), VNOTIFY_MAIL, t('Recommended'), $yes_no],
'$vnotify4' => ['vnotify4', t('Upcoming events'), ($vnotify & VNOTIFY_EVENT), VNOTIFY_EVENT, '', $yes_no],
'$vnotify5' => ['vnotify5', t('Events today'), ($vnotify & VNOTIFY_EVENTTODAY), VNOTIFY_EVENTTODAY, '', $yes_no],
'$vnotify6' => ['vnotify6', t('Upcoming birthdays'), ($vnotify & VNOTIFY_BIRTHDAY), VNOTIFY_BIRTHDAY, t('Not available in all themes'), $yes_no],
'$vnotify7' => ['vnotify7', t('System (personal) notifications'), ($vnotify & VNOTIFY_SYSTEM), VNOTIFY_SYSTEM, '', $yes_no],
'$vnotify8' => ['vnotify8', t('System info messages'), ($vnotify & VNOTIFY_INFO), VNOTIFY_INFO, t('Recommended'), $yes_no],
'$vnotify9' => ['vnotify9', t('System critical alerts'), ($vnotify & VNOTIFY_ALERT), VNOTIFY_ALERT, t('Recommended'), $yes_no],
'$vnotify10' => ['vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no],
'$vnotify11' => ((is_site_admin()) ? ['vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no] : []),
'$vnotify12' => ['vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no],
'$vnotify13' => ((($disable_discover_tab && !$site_firehose) || !Apps::system_app_installed(local_channel(), 'Public Stream')) ? [] : ['vnotify13', t('Unseen public stream activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no]),
'$vnotify14' => ['vnotify14', t('Unseen likes and dislikes'), ($vnotify & VNOTIFY_LIKE), VNOTIFY_LIKE, '', $yes_no],
'$vnotify15' => ['vnotify15', t('Unseen forum posts'), ($vnotify & VNOTIFY_FORUMS), VNOTIFY_FORUMS, '', $yes_no],
'$mailhost' => ['mailhost', t('Email notification hub (hostname)'), get_pconfig(local_channel(), 'system', 'email_notify_host', App::get_hostname()), sprintf(t('If your channel is mirrored to multiple hubs, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'), App::get_hostname())],
'$always_show_in_notices' => ['always_show_in_notices', t('Show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no],
'$update_notices_per_parent' => ['update_notices_per_parent', t('Mark all notices of the thread read if a notice is clicked'), $update_notices_per_parent, 1, t('If no, only the clicked notice will be marked read'), $yes_no],
'$desktop_notifications_info' => t('Desktop notifications are unavailable because the required browser permission has not been granted'),
'$desktop_notifications_request' => t('Grant permission'),
'$evdays' => array('evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')),
'$basic_addon' => $plugin['basic'],
'$sec_addon' => $plugin['security'],
'$notify_addon' => $plugin['notify'],
'$evdays' => ['evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')],
'$basic_addon' => $plugin['basic'],
'$notify_addon' => $plugin['notify'],
'$photo_path' => ['photo_path', t('Default photo upload folder'), get_pconfig(local_channel(), 'system', 'photo_path'), t('%Y - current year, %m - current month')],
'$attach_path' => ['attach_path', t('Default file upload folder'), get_pconfig(local_channel(), 'system', 'attach_path'), t('%Y - current year, %m - current month')],
'$removeme' => t('Remove Channel'),
'$removechannel' => t('Remove this channel.'),
'$expire' => ['expire', t('Expire other channel content after this many days'), $expire, t('0 or blank to use the website limit.') . ' ' . ((intval($sys_expire)) ? sprintf(t('This website expires after %d days.'), intval($sys_expire)) : t('This website does not expire imported content.')) . ' ' . t('The website limit takes precedence if lower than your limit.')],
]);
'$h_advn' => t('Advanced Account/Page Type Settings'),
'$h_descadvn' => t('Change the behaviour of this account for special situations'),
'$pagetype' => $pagetype,
'$lbl_misc' => t('Miscellaneous Settings'),
'$photo_path' => array('photo_path', t('Default photo upload folder'), get_pconfig(local_channel(),'system','photo_path'), t('%Y - current year, %m - current month')),
'$attach_path' => array('attach_path', t('Default file upload folder'), get_pconfig(local_channel(),'system','attach_path'), t('%Y - current year, %m - current month')),
'$removeme' => t('Remove Channel'),
'$removechannel' => t('Remove this channel.'),
));
call_hooks('settings_form',$o);
//$o .= '</form>' . "\r\n";
call_hooks('settings_form', $o);
return $o;
}

View File

@@ -0,0 +1,127 @@
<?php
namespace Zotlabs\Module\Settings;
use App;
use Zotlabs\Access\PermissionLimits;
use Zotlabs\Access\Permissions;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Group;
use Zotlabs\Lib\Libsync;
class Privacy {
function post() {
check_form_security_token_redirectOnErr('/settings/privacy', 'settings');
call_hooks('settings_post', $_POST);
$index_opt_out = (((x($_POST, 'index_opt_out')) && (intval($_POST['index_opt_out']) == 1)) ? 1 : 0);
set_pconfig(local_channel(), 'system', 'index_opt_out', $index_opt_out);
$autoperms = (((x($_POST, 'autoperms')) && (intval($_POST['autoperms']) == 1)) ? 1 : 0);
set_pconfig(local_channel(), 'system', 'autoperms', $autoperms);
$role = get_pconfig(local_channel(), 'system', 'permissions_role');
if ($role === 'custom') {
$global_perms = Permissions::Perms();
foreach ($global_perms as $k => $v) {
PermissionLimits::Set(local_channel(), $k, intval($_POST[$k]));
}
$group_actor = (((x($_POST, 'group_actor')) && (intval($_POST['group_actor']) == 1)) ? 1 : 0);
set_pconfig(local_channel(), 'system', 'group_actor', $group_actor);
}
info(t('Privacy settings updated.') . EOL);
Master::Summon(['Directory', local_channel()]);
Libsync::build_sync_packet();
goaway(z_root() . '/settings/privacy');
return; // NOTREACHED
}
function get() {
load_pconfig(local_channel());
$channel = App::get_channel();
$global_perms = Permissions::Perms();
$permiss = [];
$perm_opts = [
[t('Only me'), 0],
[t('Only those you specifically allow'), PERMS_SPECIFIC],
[t('Approved connections'), PERMS_CONTACTS],
[t('Any connections'), PERMS_PENDING],
[t('Anybody on this website'), PERMS_SITE],
[t('Anybody in this network'), PERMS_NETWORK],
[t('Anybody authenticated'), PERMS_AUTHED],
[t('Anybody on the internet'), PERMS_PUBLIC]
];
$help = [
'view_stream',
'view_wiki',
'view_pages',
'view_storage'
];
$help_txt = t('Advise: set to "Anybody on the internet" and use privacy groups to restrict access');
$limits = PermissionLimits::Get(local_channel());
$anon_comments = get_config('system', 'anonymous_comments', true);
foreach ($global_perms as $k => $perm) {
$options = [];
$can_be_public = (strstr($k, 'view') || ($k === 'post_comments' && $anon_comments));
foreach ($perm_opts as $opt) {
if ($opt[1] == PERMS_PUBLIC && (!$can_be_public))
continue;
$options[$opt[1]] = $opt[0];
}
$permiss[] = [
$k,
$perm,
$limits[$k],
((in_array($k, $help)) ? $help_txt : ''),
$options
];
}
//logger('permiss: ' . print_r($permiss,true));
$autoperms = get_pconfig(local_channel(), 'system', 'autoperms');
$index_opt_out = get_pconfig(local_channel(), 'system', 'index_opt_out');
$group_actor = get_pconfig(local_channel(), 'system', 'group_actor');
$permissions_role = get_pconfig(local_channel(), 'system', 'permissions_role', 'custom');
$permission_limits = ($permissions_role === 'custom');
$stpl = get_markup_template('settings_privacy.tpl');
$o = replace_macros($stpl, [
'$ptitle' => t('Privacy Settings'),
'$submit' => t('Submit'),
'$form_security_token' => get_form_security_token("settings"),
'$permission_limits' => $permission_limits,
'$permiss_arr' => $permiss,
'$permission_limits_label' => t('Advanced configuration'),
'$permission_limits_warning' => [
t('Proceed with caution'),
t('Changing advanced configuration settings can impact your, and your contacts channels functionality and security.'),
t('Accept the risk and continue')
],
'$autoperms' => ['autoperms', t('Automatically approve new contacts'), $autoperms, '', [t('No'), t('Yes')]],
'$index_opt_out' => ['index_opt_out', t('Opt-out of search engine indexing'), $index_opt_out, '', [t('No'), t('Yes')]],
'$group_actor' => ['group_actor', t('Group actor'), $group_actor, t('Allow this channel to act as a forum'), [t('No'), t('Yes')]],
]);
return $o;
}
}

View File

@@ -13,14 +13,14 @@ class Profiles {
$module = substr(strrchr(strtolower(static::class), '\\'), 1);
check_form_security_token_redirectOnErr('/settings/' . $module, 'settings_' . $module);
$features = get_module_features($module);
process_module_features_post(local_channel(), $features, $_POST);
$profile_assign = ((x($_POST,'profile_assign')) ? notags(trim($_POST['profile_assign'])) : '');
set_pconfig(local_channel(),'system','profile_assign',$profile_assign);
Libsync::build_sync_packet();
if($_POST['rpath'])
@@ -38,7 +38,7 @@ class Profiles {
$extra_settings_html = '';
if(feature_enabled(local_channel(),'multi_profiles'))
$extra_settings_html = contact_profile_assign(get_pconfig(local_channel(),'system','profile_assign',''));
$extra_settings_html = contact_profile_assign(get_pconfig(local_channel(),'system','profile_assign',''), t('Default profile for new contacts'));
$tpl = get_markup_template("settings_module.tpl");
@@ -51,7 +51,7 @@ class Profiles {
'$extra_settings_html' => $extra_settings_html,
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -34,6 +34,7 @@ class Sse extends Controller {
self::$uid = local_channel();
self::$ob_hash = get_observer_hash();
self::$sse_id = false;
self::$vnotify = -1;
if(! self::$ob_hash) {
if(session_id()) {
@@ -45,7 +46,9 @@ class Sse extends Controller {
}
}
self::$vnotify = get_pconfig(self::$uid, 'system', 'vnotify');
if (self::$uid) {
self::$vnotify = get_pconfig(self::$uid, 'system', 'vnotify');
}
$sleep_seconds = 3;
@@ -94,6 +97,14 @@ class Sse extends Controller {
$result = XConfig::Get(self::$ob_hash, 'sse', 'notifications', []);
$lock = XConfig::Get(self::$ob_hash, 'sse', 'lock');
// We do not have the local_channel in the addon.
// Reset pubs here if the app is not installed.
if (self::$uid && (!(self::$vnotify & VNOTIFY_PUBS) || !Apps::system_app_installed(self::$uid, 'Public Stream'))) {
$result['pubs']['count'] = 0;
$result['pubs']['notifications'] = [];
$result['pubs']['offset'] = -1;
}
if($result && !$lock) {
echo "event: notifications\n";
echo 'data: ' . json_encode($result);

View File

@@ -124,7 +124,7 @@ class Sse_bs extends Controller {
$str = '';
foreach($arr as $a) {
$mids[] = '\'' . dbesc(@base64url_decode(substr($a,4))) . '\'';
$mids[] = '\'' . dbesc(unpack_link_id($a)) . '\'';
}
$str = implode(',', $mids);
@@ -373,7 +373,7 @@ class Sse_bs extends Controller {
$result['pubs']['notifications'] = [];
$result['pubs']['count'] = 0;
if(! (self::$vnotify & VNOTIFY_PUBS)) {
if(! (self::$vnotify & VNOTIFY_PUBS) || !Apps::system_app_installed(self::$uid, 'Public Stream')) {
$result['pubs']['offset'] = -1;
return $result;
}

View File

@@ -5,6 +5,11 @@ namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Apps;
use Zotlabs\Lib\AccessList;
use Zotlabs\Lib\Permcat;
use Zotlabs\Lib\Libsync;
require_once('include/security.php');
class Tokens extends Controller {
@@ -13,15 +18,65 @@ class Tokens extends Controller {
if(! local_channel())
return;
if(! Apps::system_app_installed(local_channel(), 'Guest Access'))
return;
$channel = App::get_channel();
if(! Apps::system_app_installed($channel['channel_id'], 'Guest Access'))
return;
check_form_security_token_redirectOnErr('tokens', 'tokens');
if(isset($_POST['delete'])) {
$r = q("select * from atoken where atoken_id = %d and atoken_uid = %d",
intval($_POST['atoken_id']),
intval(local_channel())
);
if (!$r) {
return;
}
$atoken = $r[0];
$atoken_xchan = substr($channel['channel_hash'], 0, 16) . '.' . $atoken['atoken_guid'];
$atoken['deleted'] = true;
$r = q("SELECT abook.*, xchan.*
FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
intval($channel['channel_id']),
dbesc($atoken_xchan)
);
if (!$r) {
return;
}
$clone = $r[0];
unset($clone['abook_id']);
unset($clone['abook_account']);
unset($clone['abook_channel']);
$clone['deleted'] = true;
$abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']);
if ($abconfig) {
$clone['abconfig'] = $abconfig;
}
atoken_delete($atoken['atoken_id']);
Libsync::build_sync_packet($channel['channel_id'], [ 'abook' => [ $clone ], 'atoken' => [ $atoken ] ], true);
return;
}
$token_errs = 0;
if(array_key_exists('token',$_POST)) {
$atoken_id = (($_POST['atoken_id']) ? intval($_POST['atoken_id']) : 0);
if (! $atoken_id) {
$atoken_guid = new_uuid();
}
$name = trim(escape_tags($_POST['name']));
$token = trim($_POST['token']);
if((! $name) || (! $token))
@@ -30,10 +85,10 @@ class Tokens extends Controller {
$expires = datetime_convert(date_default_timezone_get(),'UTC',$_POST['expires']);
else
$expires = NULL_DATE;
$max_atokens = service_class_fetch(local_channel(),'access_tokens');
$max_atokens = service_class_fetch($channel['channel_id'],'access_tokens');
if($max_atokens) {
$r = q("select count(atoken_id) as total where atoken_uid = %d",
intval(local_channel())
intval($channel['channel_id'])
);
if($r && intval($r[0]['total']) >= $max_tokens) {
notice( sprintf( t('This channel is limited to %d tokens'), $max_tokens) . EOL);
@@ -45,6 +100,17 @@ class Tokens extends Controller {
notice( t('Name and Password are required.') . EOL);
return;
}
$old_atok = q("select * from atoken where atoken_uid = %d and atoken_name = '%s'",
intval($channel['channel_id']),
dbesc($name)
);
if ($old_atok) {
$old_atok = $old_atok[0];
$old_xchan = atoken_xchan($old_atok);
}
if($atoken_id) {
$r = q("update atoken set atoken_name = '%s', atoken_token = '%s', atoken_expires = '%s'
where atoken_id = %d and atoken_uid = %d",
@@ -56,8 +122,9 @@ class Tokens extends Controller {
);
}
else {
$r = q("insert into atoken ( atoken_aid, atoken_uid, atoken_name, atoken_token, atoken_expires )
values ( %d, %d, '%s', '%s', '%s' ) ",
$r = q("insert into atoken (atoken_guid, atoken_aid, atoken_uid, atoken_name, atoken_token, atoken_expires )
values ('%s', %d, %d, '%s', '%s', '%s' ) ",
dbesc($atoken_guid),
intval($channel['channel_account_id']),
intval($channel['channel_id']),
dbesc($name),
@@ -66,21 +133,84 @@ class Tokens extends Controller {
);
}
$atoken_xchan = substr($channel['channel_hash'],0,16) . '.' . $name;
$atok = q("select * from atoken where atoken_uid = %d and atoken_name = '%s'",
intval($channel['channel_id']),
dbesc($name)
);
$all_perms = \Zotlabs\Access\Permissions::Perms();
if ($atok) {
$xchan = atoken_xchan($atok[0]);
atoken_create_xchan($xchan);
$atoken_xchan = $xchan['xchan_hash'];
if ($old_atok && $old_xchan) {
$r = q("update xchan set xchan_name = '%s' where xchan_hash = '%s'",
dbesc($xchan['xchan_name']),
dbesc($old_xchan['xchan_hash'])
);
}
}
if($all_perms) {
foreach($all_perms as $perm => $desc) {
if(array_key_exists('perms_' . $perm, $_POST)) {
set_abconfig($channel['channel_id'],$atoken_xchan,'my_perms',$perm,intval($_POST['perms_' . $perm]));
}
else {
set_abconfig($channel['channel_id'],$atoken_xchan,'my_perms',$perm,0);
if (! $atoken_id) {
// If this is a new token, create a new abook record
$closeness = get_pconfig($channel['channel_id'], 'system', 'new_abook_closeness',80);
$profile_assign = get_pconfig($channel['channel_id'], 'system', 'profile_assign', '');
$r = abook_store_lowlevel(
[
'abook_account' => $channel['channel_account_id'],
'abook_channel' => $channel['channel_id'],
'abook_closeness' => intval($closeness),
'abook_xchan' => $atoken_xchan,
'abook_profile' => $profile_assign,
'abook_feed' => 0,
'abook_created' => datetime_convert(),
'abook_updated' => datetime_convert(),
'abook_instance' => z_root(),
]
);
if (! $r) {
logger('abook creation failed');
}
/** If there is a default group for this channel, add this connection to it */
if ($channel['channel_default_group']) {
$g = AccessList::by_hash($channel['channel_id'], $channel['channel_default_group']);
if ($g) {
AccessList::member_add($channel['channel_id'], '', $atoken_xchan,$g['id']);
}
}
}
$role = ((array_key_exists('permcat', $_POST)) ? escape_tags($_POST['permcat']) : '');
\Zotlabs\Lib\Permcat::assign($channel, $role, [$atoken_xchan]);
$r = q("SELECT abook.*, xchan.*
FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
intval($channel['chnnel_id']),
dbesc($atoken_xchan)
);
if (! $r) {
return;
}
$clone = $r[0];
unset($clone['abook_id']);
unset($clone['abook_account']);
unset($clone['abook_channel']);
$abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']);
if ($abconfig) {
$clone['abconfig'] = $abconfig;
}
Libsync::build_sync_packet($channel['channel_id'], [ 'abook' => [ $clone ], 'atoken' => $atok ], true);
info( t('Token saved.') . EOL);
return;
@@ -99,10 +229,13 @@ class Tokens extends Controller {
return Apps::app_render($papp, 'module');
}
nav_set_selected('Guest Access');
$channel = App::get_channel();
$atoken = null;
$atoken_xchan = '';
$atoken_abook = [];
if(argc() > 1) {
$id = argv(1);
@@ -114,76 +247,52 @@ class Tokens extends Controller {
if($atoken) {
$atoken = $atoken[0];
$atoken_xchan = substr($channel['channel_hash'],0,16) . '.' . $atoken['atoken_name'];
}
$atoken_xchan = substr($channel['channel_hash'],0,16) . '.' . $atoken['atoken_guid'];
if($atoken && argc() > 2 && argv(2) === 'drop') {
atoken_delete($id);
$atoken = null;
$atoken_xchan = '';
$atoken_abook = q("select * from abook where abook_channel = %d and abook_xchan = '%s'",
intval(local_channel()),
dbesc($atoken_xchan)
);
$atoken_abook = $atoken_abook[0];
}
}
$t = q("select * from atoken where atoken_uid = %d",
intval(local_channel())
);
$desc = t('Use this form to create temporary access identifiers to share things with non-members. These identities may be used in privacy groups and visitors may login using these credentials to access private content.');
$desc = t('Use this form to create temporary access identifiers to share things with non-members. These identities may be used in Access Control Lists and visitors may login using these credentials to access private content.');
$pcat = new Permcat(local_channel());
$pcatlist = $pcat->listing();
$default_role = get_pconfig(local_channel(), 'system', 'default_permcat');
$current_permcat = (($atoken_abook) ? $atoken_abook['abook_role'] : $default_role);
$desc2 = t('You may also provide <em>dropbox</em> style access links to friends and associates by adding the Login Password to any specific site URL as shown. Examples:');
$roles_dict = [];
foreach ($pcatlist as $role) {
$roles_dict[$role['name']] = $role['localname'];
}
$global_perms = \Zotlabs\Access\Permissions::Perms();
$their_perms = [];
if (!$current_permcat) {
notice(t('Please select a role for this guest!') . EOL);
$permcats[] = '';
}
$existing = get_all_perms(local_channel(),(($atoken_xchan) ? $atoken_xchan : ''),false);
if($atoken_xchan) {
$theirs = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'their_perms'",
intval(local_channel()),
dbesc($atoken_xchan)
);
if($theirs) {
foreach($theirs as $t) {
$their_perms[$t['k']] = $t['v'];
}
if ($pcatlist) {
foreach ($pcatlist as $pc) {
$permcats[$pc['name']] = $pc['localname'];
}
}
foreach($global_perms as $k => $v) {
$thisperm = get_abconfig(local_channel(),$contact['abook_xchan'],'my_perms',$k);
//fixme
$checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$k);
if($existing[$k])
$thisperm = "1";
$perms[] = array('perms_' . $k, $v, ((array_key_exists($k,$their_perms)) ? intval($their_perms[$k]) : ''),$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited);
}
$tpl = get_markup_template("tokens.tpl");
$o .= replace_macros($tpl, array(
'$form_security_token' => get_form_security_token("tokens"),
'$title' => t('Guest Access Tokens'),
'$desc' => $desc,
'$desc2' => $desc2,
'$tokens' => $t,
'$form_security_token' => get_form_security_token('tokens'),
'$permcat' => ['permcat', t('Select a role for this guest'), $current_permcat, '', $permcats],
'$title' => t('Guest Access'),
'$desc' => $desc,
'$atoken' => $atoken,
'$url1' => z_root() . '/channel/' . $channel['channel_address'],
'$url2' => z_root() . '/photos/' . $channel['channel_address'],
'$name' => array('name', t('Login Name') . ' <span class="required">*</span>', (($atoken) ? $atoken['atoken_name'] : ''),''),
'$token'=> array('token', t('Login Password') . ' <span class="required">*</span>',(($atoken) ? $atoken['atoken_token'] : autoname(8)), ''),
'$token'=> array('token', t('Login Password') . ' <span class="required">*</span>',(($atoken) ? $atoken['atoken_token'] : new_token()), ''),
'$expires'=> array('expires', t('Expires (yyyy-mm-dd)'), (($atoken['atoken_expires'] && $atoken['atoken_expires'] > NULL_DATE) ? datetime_convert('UTC',date_default_timezone_get(),$atoken['atoken_expires']) : ''), ''),
'$them' => t('Their Settings'),
'$me' => t('My Settings'),
'$perms' => $perms,
'$inherited' => t('inherited'),
'$notself' => 1,
'$self' => 0,
'$permlbl' => t('Individual Permissions'),
'$permnote' => t('Some permissions may be inherited from your channel\'s <a href="settings"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can <strong>not</strong> change those settings here.'),
'$submit' => t('Submit')
'$submit' => t('Submit'),
'$delete' => t('Delete')
));
return $o;
}

View File

@@ -2,24 +2,27 @@
namespace Zotlabs\Module;
use App;
use ZipArchive;
use Zotlabs\Lib\Apps;
use Zotlabs\Web\Controller;
class Uexport extends Controller {
function init() {
if(! local_channel())
killme();
if(! Apps::system_app_installed(local_channel(), 'Channel Export'))
if(! local_channel()) {
return;
}
if(! Apps::system_app_installed(local_channel(), 'Channel Export')) {
return;
}
if(argc() > 1) {
$sections = (($_REQUEST['sections']) ? explode(',',$_REQUEST['sections']) : '');
$zap_compat = (($_REQUEST['zap_compat']) ? intval($_REQUEST['zap_compat']) : false);
$channel = App::get_channel();
$year = null;
$month = null;
if(argc() > 1 && intval(argv(1)) > 1900) {
$year = intval(argv(1));
@@ -29,25 +32,110 @@ class Uexport extends Controller {
$month = intval(argv(2));
}
header('content-type: application/json');
header('content-disposition: attachment; filename="' . $channel['channel_address'] . (($year) ? '-' . $year : '') . (($month) ? '-' . $month : '') . (($_REQUEST['sections']) ? '-' . $_REQUEST['sections'] : '') . '.json"' );
if($year) {
echo json_encode(identity_export_year(local_channel(),$year,$month, $zap_compat));
killme();
$sections = [];
$section = '';
if(argc() > 1 && ctype_lower(argv(1))) {
$section = argv(1);
}
if(argc() > 1 && argv(1) === 'basic') {
echo json_encode(identity_basic_export(local_channel(),$sections, $zap_compat));
killme();
switch ($section) {
case 'channel':
$sections = get_default_export_sections();
break;
case 'chatrooms':
$sections = ['chatrooms'];
break;
case 'events':
$sections = ['events'];
break;
case 'webpages':
$sections = ['webpages'];
break;
case 'wikis':
$sections = ['wikis'];
break;
case 'custom':
default:
$custom_sections = ['channel', 'connections', 'config', 'apps', 'chatrooms', 'events', 'webpages', 'wikis'];
$raw_sections = (($_REQUEST['sections']) ? explode(',', $_REQUEST['sections']) : '');
if ($raw_sections) {
foreach ($raw_sections as $raw_section) {
if(in_array($raw_section, $custom_sections)) {
$sections[] = $raw_section;
}
}
}
}
// Warning: this option may consume a lot of memory
if ($sections) {
if(argc() > 1 && argv(1) === 'complete') {
$sections = get_default_export_sections();
$sections[] = 'items';
echo json_encode(identity_basic_export(local_channel(),$sections, $zap_compat));
$export = json_encode(identity_basic_export(local_channel(), $sections, $zap_compat));
header('Content-Type: application/json');
header('Content-Disposition: attachment; filename="' . $channel['channel_address'] . '-' . implode('-', $sections) . '.json"');
header('Content-Length: ' . strlen($export));
echo $export;
killme();
}
elseif ($year && !$month) {
$zip_dir = 'store/[data]/' . $channel['channel_address'] . '/tmp';
if (!is_dir($zip_dir))
mkdir($zip_dir, STORAGE_DEFAULT_PERMISSIONS, true);
$zip_file = $channel['channel_address'] . '-' . $year . '.zip';
$zip_path = $zip_dir . '/' . $zip_file;
$zip_content_available = false;
$zip = new ZipArchive();
if ($zip->open($zip_path, ZipArchive::CREATE) === true) {
$month = 1;
while ($month <= 12) {
$name = $channel['channel_address'] . '-' . $year . '-' . $month . '.json';
$content = conv_item_export_year(local_channel(), $year, $month, $zap_compat);
if(isset($content['item'])) {
$zip_content_available = true;
$zip->addFromString($name, json_encode($content));
}
$month++;
}
$zip->setCompressionName($zip_path, ZipArchive::CM_STORE);
$zip->close();
}
if (!$zip_content_available) {
unlink($zip_path);
notice(t('No content available for year') . ' ' . $year . EOL);
goaway('/uexport');
}
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . $zip_file . '"');
header('Content-Length: ' . filesize($zip_path));
$istream = fopen($zip_path, 'rb');
$ostream = fopen('php://output', 'wb');
if ($istream && $ostream) {
pipe_streams($istream, $ostream);
fclose($istream);
fclose($ostream);
}
unlink($zip_path);
killme();
}
elseif ($year && $month) {
$export = json_encode(conv_item_export_year(local_channel(), $year, $month, $zap_compat));
header('Content-Type: application/json');
header('Content-Disposition: attachment; filename="' . $channel['channel_address'] . '-' . $year . '-' . $month . '.json"');
header('Content-Length: ' . strlen($export));
echo $export;
killme();
}
else {
killme();
}
}
@@ -55,6 +143,10 @@ class Uexport extends Controller {
function get() {
if(! local_channel()) {
return;
}
if(! Apps::system_app_installed(local_channel(), 'Channel Export')) {
//Do not display any associated widgets at this point
App::$pdl = '';
@@ -62,27 +154,47 @@ class Uexport extends Controller {
return Apps::app_render($papp, 'module');
}
$y = datetime_convert('UTC',date_default_timezone_get(),'now','Y');
$account = App::get_account();
$year_start = datetime_convert('UTC', date_default_timezone_get(), $account['account_created'], 'Y');
$year_end = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y');
$years = [];
while ($year_start <= $year_end) {
$years[] = $year_start;
$year_start++;
}
$item_import_url = '/import_items';
$channel_import_url = '/import';
$yearurl = z_root() . '/uexport/' . $y;
$janurl = z_root() . '/uexport/' . $y . '/1';
$impurl = '/import_items';
$o = replace_macros(get_markup_template('uexport.tpl'), array(
'$title' => t('Export Channel'),
'$basictitle' => t('Export Channel'),
'$basic' => t('Export your basic channel information to a file. This acts as a backup of your connections, permissions, profile and basic data, which can be used to import your data to a new server hub, but does not contain your content.'),
'$fulltitle' => t('Export Content'),
'$full' => t('Export your channel information and recent content to a JSON backup that can be restored or imported to another server hub. This backs up all of your connections, permissions, profile data and several months of posts. This file may be VERY large. Please be patient - it may take several minutes for this download to begin.'),
'$by_year' => t('Export your posts from a given year.'),
'$channel_title' => t('Export channel'),
'$channel_info' => t('This will export your identity and social graph into a file which can be used to import your channel to a new hub.'),
'$extra' => t('You may also export your posts and conversations for a particular year or month. Adjust the date in your browser location bar to select other dates. If the export fails (possibly due to memory exhaustion on your server hub), please try again selecting a more limited date range.'),
'$extra2' => sprintf( t('To select all posts for a given year, such as this year, visit <a href="%1$s">%2$s</a>'),$yearurl,$yearurl),
'$extra3' => sprintf( t('To select all posts for a given month, such as January of this year, visit <a href="%1$s">%2$s</a>'),$janurl,$janurl),
'$extra4' => sprintf( t('These content files may be imported or restored by visiting <a href="%1$s">%2$s</a> on any site containing your channel. For best results please import or restore these in date order (oldest first).'),$impurl,$impurl)
'$years' => $years,
'$content_title' => t('Export content'),
'$content_info' => t('This will export your posts, direct messages, articles and cards per month stored into a zip file per year. Months with no posts will be dismissed.'),
'$wikis_title' => t('Export wikis'),
'$wikis_info' => t('This will export your wikis and wiki pages.'),
'$webpages_title' => t('Export webpages'),
'$webpages_info' => t('This will export your webpages and menus.'),
'$events_title' => t('Export channel calendar'),
'$events_info' => t('This will export your channel calendar events and associated items. CalDAV calendars are not included.'),
'$chatrooms_title' => t('Export chatrooms'),
'$chatrooms_info' => t('This will export your chatrooms. Chat history is dismissed.'),
'$items_extra_info' => sprintf( t('This export can be imported or restored by visiting <a href="%1$s">%2$s</a> on any site containing your channel.'), $item_import_url, $item_import_url),
));
return $o;
return $o;
}
}

View File

@@ -6,7 +6,7 @@ require_once('include/selectors.php');
class Viewconnections extends \Zotlabs\Web\Controller {
function init() {
if(observer_prohibited()) {
return;
}
@@ -16,58 +16,58 @@ class Viewconnections extends \Zotlabs\Web\Controller {
}
}
function get() {
if(observer_prohibited()) {
notice( t('Public access denied.') . EOL);
return;
}
if(((! count(\App::$profile)) || (\App::$profile['hide_friends']))) {
notice( t('Permission denied.') . EOL);
return;
}
}
if(! perm_is_allowed(\App::$profile['uid'], get_observer_hash(),'view_contacts')) {
notice( t('Permission denied.') . EOL);
return;
}
}
if(! $_REQUEST['aj'])
$_SESSION['return_url'] = \App::$query_string;
$is_owner = ((local_channel() && local_channel() == \App::$profile['uid']) ? true : false);
$abook_flags = " and abook_pending = 0 and abook_self = 0 ";
$abook_flags = " and abook_pending = 0 and abook_self = 0 and abook_blocked = 0 and abook_ignored = 0 ";
$sql_extra = '';
if(! $is_owner) {
$abook_flags .= " and abook_hidden = 0 ";
$sql_extra = " and xchan_hidden = 0 ";
}
$r = q("SELECT count(*) as total FROM abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d $abook_flags and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra ",
intval(\App::$profile['uid'])
);
if($r) {
\App::set_pager_total($r[0]['total']);
}
$r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d $abook_flags and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra order by xchan_name LIMIT %d OFFSET %d ",
intval(\App::$profile['uid']),
intval(\App::$pager['itemspage']),
intval(\App::$pager['start'])
);
if((! $r) && (! $_REQUEST['aj'])) {
info( t('No connections.') . EOL );
return $o;
}
$contacts = array();
foreach($r as $rr) {
$oneway = false;
@@ -103,7 +103,7 @@ class Viewconnections extends \Zotlabs\Web\Controller {
'id' => $rr['abook_id'],
'archived' => (intval($rr['abook_archived']) ? true : false),
'img_hover' => sprintf( t('Visit %s\'s profile [%s]'), $rr['xchan_name'], $rr['xchan_url']),
'thumb' => $rr['xchan_photo_m'],
'thumb' => $rr['xchan_photo_m'],
'name' => substr($rr['xchan_name'],0,20),
'username' => $rr['xchan_addr'],
'link' => $url,
@@ -137,11 +137,11 @@ class Viewconnections extends \Zotlabs\Web\Controller {
// '$paginate' => paginate($a),
));
}
if(! $contacts)
$o .= '<div id="content-complete"></div>';
return $o;
}
}

View File

@@ -24,7 +24,7 @@ class Vote extends Controller {
$fetch = null;
$id = argv(1);
$response = $_REQUEST['answer'];
if ($id) {
$fetch = q("select * from item where id = %d limit 1",
intval($id)
@@ -42,7 +42,7 @@ class Vote extends Controller {
}
$valid = false;
if ($obj['oneOf']) {
foreach($obj['oneOf'] as $selection) {
// logger('selection: ' . $selection);
@@ -80,7 +80,6 @@ class Vote extends Controller {
$item = [];
$item['aid'] = $channel['channel_account_id'];
$item['uid'] = $channel['channel_id'];
$item['item_origin'] = 1;
@@ -95,11 +94,8 @@ class Vote extends Controller {
$item['owner_xchan'] = $fetch[0]['author_xchan'];
$item['allow_cid'] = '<' . $fetch[0]['author_xchan'] . '>';
$item['item_private'] = 1;
$item['obj_type'] = 'Note';
$item['author'] = channelx_by_n($channel['channel_id']);
$item['obj'] = Activity::encode_item($item);
// now reset the placeholders
@@ -108,17 +104,15 @@ class Vote extends Controller {
$item['obj_type'] = 'Answer';
unset($item['author']);
$x = item_store($item);
retain_item($fetch[0]['id']);
if($x['success']) {
$itemid = $x['item_id'];
Master::Summon( [ 'Notifier', 'like', $itemid ] );
}
$r = q("select * from item where id = %d",
intval($itemid)
);
@@ -128,6 +122,7 @@ class Vote extends Controller {
Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]);
}
}
$ret['success'] = true;
$ret['message'] = t('Response submitted. Updates may not appear instantly.');
json_return_and_die($ret);

View File

@@ -72,20 +72,16 @@ class Wfinger extends \Zotlabs\Web\Controller {
dbesc($channel)
);
if($r) {
$r[0] = pchan_to_chan($r[0]);
$r = pchan_to_chan($r[0]);
}
}
else {
$r = q("select * from channel left join xchan on channel_hash = xchan_hash
where channel_address = '%s' limit 1",
dbesc($channel)
);
$r = channelx_by_nick($channel);
}
}
header('Access-Control-Allow-Origin: *');
if($root_resource) {
$result['subject'] = $resource;
$result['properties'] = [
@@ -107,15 +103,15 @@ class Wfinger extends \Zotlabs\Web\Controller {
if($resource && $r) {
$h = q("select hubloc_addr from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0",
dbesc($r[0]['channel_hash'])
dbesc($r['channel_hash'])
);
$result['subject'] = $resource;
$aliases = array(
z_root() . (($pchan) ? '/pchan/' : '/channel/') . $r[0]['channel_address'],
z_root() . '/~' . $r[0]['channel_address'],
z_root() . '/@' . $r[0]['channel_address']
z_root() . (($pchan) ? '/pchan/' : '/channel/') . $r['channel_address'],
z_root() . '/~' . $r['channel_address'],
z_root() . '/@' . $r['channel_address']
);
if($h) {
@@ -127,10 +123,10 @@ class Wfinger extends \Zotlabs\Web\Controller {
$result['aliases'] = [];
$result['properties'] = [
'http://webfinger.net/ns/name' => $r[0]['channel_name'],
'http://xmlns.com/foaf/0.1/name' => $r[0]['channel_name'],
'https://w3id.org/security/v1#publicKeyPem' => $r[0]['xchan_pubkey'],
'http://purl.org/zot/federation' => 'zot6,zot'
'http://webfinger.net/ns/name' => $r['channel_name'],
'http://xmlns.com/foaf/0.1/name' => $r['channel_name'],
'https://w3id.org/security/v1#publicKeyPem' => $r['xchan_pubkey'],
'http://purl.org/zot/federation' => 'zot6'
];
foreach($aliases as $alias)
@@ -143,18 +139,18 @@ class Wfinger extends \Zotlabs\Web\Controller {
[
'rel' => 'http://webfinger.net/rel/avatar',
'type' => $r[0]['xchan_photo_mimetype'],
'href' => $r[0]['xchan_photo_l']
'type' => $r['xchan_photo_mimetype'],
'href' => $r['xchan_photo_l']
],
[
'rel' => 'http://webfinger.net/rel/profile-page',
'href' => $r[0]['xchan_url'],
'href' => $r['xchan_url'],
],
[
'rel' => 'magic-public-key',
'href' => 'data:application/magic-public-key,' . Keyutils::salmonKey($r[0]['channel_pubkey']),
'href' => 'data:application/magic-public-key,' . Keyutils::salmonKey($r['channel_pubkey']),
]
];
@@ -167,14 +163,14 @@ class Wfinger extends \Zotlabs\Web\Controller {
[
'rel' => 'http://webfinger.net/rel/avatar',
'type' => $r[0]['xchan_photo_mimetype'],
'href' => $r[0]['xchan_photo_l']
'type' => $r['xchan_photo_mimetype'],
'href' => $r['xchan_photo_l']
],
[
'rel' => 'http://microformats.org/profile/hcard',
'type' => 'text/html',
'href' => z_root() . '/hcard/' . $r[0]['channel_address']
'href' => z_root() . '/hcard/' . $r['channel_address']
],
[
@@ -184,18 +180,12 @@ class Wfinger extends \Zotlabs\Web\Controller {
[
'rel' => 'http://webfinger.net/rel/profile-page',
'href' => z_root() . '/profile/' . $r[0]['channel_address'],
],
[
'rel' => 'http://schemas.google.com/g/2010#updates-from',
'type' => 'application/atom+xml',
'href' => z_root() . '/ofeed/' . $r[0]['channel_address']
'href' => z_root() . '/profile/' . $r['channel_address'],
],
[
'rel' => 'http://webfinger.net/rel/blog',
'href' => z_root() . '/channel/' . $r[0]['channel_address'],
'href' => z_root() . '/channel/' . $r['channel_address'],
],
[
@@ -206,12 +196,7 @@ class Wfinger extends \Zotlabs\Web\Controller {
[
'rel' => 'http://purl.org/zot/protocol/6.0',
'type' => 'application/x-zot+json',
'href' => channel_url($r[0])
],
[
'rel' => 'http://purl.org/zot/protocol',
'href' => z_root() . '/.well-known/zot-info' . '?address=' . $r[0]['xchan_addr'],
'href' => channel_url($r)
],
[
@@ -222,14 +207,14 @@ class Wfinger extends \Zotlabs\Web\Controller {
[
'rel' => 'magic-public-key',
'href' => 'data:application/magic-public-key,' . Keyutils::salmonKey($r[0]['channel_pubkey']),
'href' => 'data:application/magic-public-key,' . Keyutils::salmonKey($r['channel_pubkey']),
]
];
}
if($zot) {
// get a zotinfo packet and return it with webfinger
$result['zot'] = Libzot::zotinfo( [ 'address' => $r[0]['xchan_addr'] ]);
$result['zot'] = Libzot::zotinfo( [ 'address' => $r['xchan_addr'] ]);
}
}
@@ -238,7 +223,7 @@ class Wfinger extends \Zotlabs\Web\Controller {
killme();
}
$arr = [ 'channel' => $r[0], 'pchan' => $pchan, 'request' => $_REQUEST, 'result' => $result ];
$arr = [ 'channel' => $r, 'pchan' => $pchan, 'request' => $_REQUEST, 'result' => $result ];
call_hooks('webfinger',$arr);
json_return_and_die($arr['result'],'application/jrd+json');

View File

@@ -500,7 +500,7 @@ class Wiki extends Controller {
$r = NativeWiki::create_wiki($owner, $observer_hash, $wiki, $acl);
if($r['success']) {
NativeWiki::sync_a_wiki_item($owner['channel_id'],$r['item_id'],$r['item']['resource_id']);
$homePage = NativeWikiPage::create_page($owner['channel_id'],$observer_hash,'Home', $r['item']['resource_id'], $wiki['mimeType']);
$homePage = NativeWikiPage::create_page($owner, $observer_hash, 'Home', $r['item']['resource_id'], $wiki['mimeType']);
if(! $homePage['success']) {
notice( t('Wiki created, but error creating Home page.'));
goaway(z_root() . '/wiki/' . $nick . '/' . NativeWiki::name_encode($wiki['urlName']));
@@ -606,7 +606,7 @@ class Wiki extends Controller {
json_return_and_die(array('message' => 'Error creating page. Invalid name (' . print_r($_POST,true) . ').', 'success' => false));
}
$page = NativeWikiPage::create_page($owner['channel_id'],$observer_hash, $name, $resource_id, $mimetype);
$page = NativeWikiPage::create_page($owner, $observer_hash, $name, $resource_id, $mimetype);
if($page['item_id']) {
$commit = NativeWikiPage::commit([

View File

@@ -28,19 +28,18 @@ class Xrd extends \Zotlabs\Web\Controller {
$name = substr($local,0,strpos($local,'@'));
}
$r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1",
dbesc($name)
);
$r = channelx_by_nick($name);
if(! $r)
killme();
$salmon_key = Keyutils::salmonKey($r[0]['channel_pubkey']);
$salmon_key = Keyutils::salmonKey($r['channel_pubkey']);
header('Access-Control-Allow-Origin: *');
header("Content-type: application/xrd+xml");
$aliases = array('acct:' . channel_reddress($r[0]), z_root() . '/channel/' . $r[0]['channel_address'], z_root() . '/~' . $r[0]['channel_address']);
$aliases = array('acct:' . channel_reddress($r), z_root() . '/channel/' . $r['channel_address'], z_root() . '/~' . $r['channel_address']);
for($x = 0; $x < count($aliases); $x ++) {
if($aliases[$x] === $resource)
@@ -48,23 +47,23 @@ class Xrd extends \Zotlabs\Web\Controller {
}
$o = replace_macros(get_markup_template('xrd_person.tpl'), array(
'$nick' => $r[0]['channel_address'],
'$nick' => $r['channel_address'],
'$accturi' => $resource,
'$subject' => $subject,
'$aliases' => $aliases,
'$channel_url' => z_root() . '/channel/' . $r[0]['channel_address'],
'$profile_url' => z_root() . '/channel/' . $r[0]['channel_address'],
'$hcard_url' => z_root() . '/hcard/' . $r[0]['channel_address'],
'$atom' => z_root() . '/ofeed/' . $r[0]['channel_address'],
'$zot_post' => z_root() . '/post/' . $r[0]['channel_address'],
'$poco_url' => z_root() . '/poco/' . $r[0]['channel_address'],
'$photo' => z_root() . '/photo/profile/l/' . $r[0]['channel_id'],
'$channel_url' => z_root() . '/channel/' . $r['channel_address'],
'$profile_url' => z_root() . '/channel/' . $r['channel_address'],
'$hcard_url' => z_root() . '/hcard/' . $r['channel_address'],
'$atom' => z_root() . '/ofeed/' . $r['channel_address'],
'$zot_post' => z_root() . '/post/' . $r['channel_address'],
'$poco_url' => z_root() . '/poco/' . $r['channel_address'],
'$photo' => z_root() . '/photo/profile/l/' . $r['channel_id'],
'$modexp' => 'data:application/magic-public-key,' . $salmon_key,
'$subscribe' => z_root() . '/follow?f=&amp;url={uri}',
));
$arr = array('user' => $r[0], 'xml' => $o);
$arr = array('user' => $r, 'xml' => $o);
call_hooks('personal_xrd', $arr);
echo $arr['xml'];

View File

@@ -1,124 +1,22 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\Config;
use Zotlabs\Lib\ThreadListener;
use Zotlabs\Web\Controller;
use Zotlabs\Web\HTTPSig;
class Zotfeed extends Controller {
function init() {
if (ActivityStreams::is_as_request()) {
function post() {
if (observer_prohibited(true)) {
killme();
}
$channel = channelx_by_nick(argv(1));
if (!$channel) {
killme();
}
if (intval($channel['channel_system'])) {
killme();
}
$sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR);
if ($sigdata['portable_id'] && $sigdata['header_valid']) {
$portable_id = $sigdata['portable_id'];
if (!check_channelallowed($portable_id)) {
http_status_exit(403, 'Permission denied');
}
if (!check_siteallowed($sigdata['signer'])) {
http_status_exit(403, 'Permission denied');
}
observer_auth($portable_id);
}
elseif (Config::get('system', 'require_authenticated_fetch', false)) {
http_status_exit(403, 'Permission denied');
}
$observer_hash = get_observer_hash();
$params = [];
$params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
$params['end'] = ((x($_REQUEST, 'date_end')) ? $_REQUEST['date_end'] : '');
$params['type'] = 'json';
$params['pages'] = ((x($_REQUEST, 'pages')) ? intval($_REQUEST['pages']) : 0);
$params['top'] = ((x($_REQUEST, 'top')) ? intval($_REQUEST['top']) : 0);
$params['direction'] = ((x($_REQUEST, 'direction')) ? dbesc($_REQUEST['direction']) : 'desc'); // unimplemented
$params['cat'] = ((x($_REQUEST, 'cat')) ? escape_tags($_REQUEST['cat']) : '');
$params['compat'] = 1;
$total = items_fetch(
[
'total' => true,
'wall' => 1,
'datequery' => $params['end'],
'datequery2' => $params['begin'],
'direction' => dbesc($params['direction']),
'pages' => $params['pages'],
'order' => dbesc('post'),
'top' => $params['top'],
'cat' => $params['cat'],
'compat' => $params['compat']
], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module
);
if ($total) {
App::set_pager_total($total);
App::set_pager_itemspage(30);
}
if (App::$pager['unset'] && $total > 30) {
$ret = Activity::paged_collection_init($total, App::$query_string);
}
else {
$items = items_fetch(
[
'wall' => 1,
'datequery' => $params['end'],
'datequery2' => $params['begin'],
'records' => intval(App::$pager['itemspage']),
'start' => intval(App::$pager['start']),
'direction' => dbesc($params['direction']),
'pages' => $params['pages'],
'order' => dbesc('post'),
'top' => $params['top'],
'cat' => $params['cat'],
'compat' => $params['compat']
], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module
);
if ($items && $observer_hash) {
// check to see if this observer is a connection. If not, register any items
// belonging to this channel for notification of deletion/expiration
$x = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'",
intval($channel['channel_id']),
dbesc($observer_hash)
);
if (!$x) {
foreach ($items as $item) {
if (strpos($item['mid'], z_root()) === 0) {
ThreadListener::store($item['mid'], $observer_hash);
}
}
}
}
$ret = Activity::encode_item_collection($items, App::$query_string, 'OrderedCollection', $total);
}
as_return_and_die($ret, $channel);
}
}
function get() {
$outbox = new Outbox();
return $outbox->init();
}
}

View File

@@ -60,14 +60,16 @@ class Theme {
// Allow theme selection of the form 'theme_name:schema_name'
$themepair = explode(':', $chosen_theme);
// Check if $chosen_theme is compatible with core. If not fall back to default
$info = get_theme_info($themepair[0]);
$compatible = check_plugin_versions($info);
if(!$compatible) {
$chosen_theme = '';
}
App::$theme_info = $info;
if($chosen_theme && (file_exists('view/theme/' . $themepair[0] . '/css/style.css') || file_exists('view/theme/' . $themepair[0] . '/php/style.php'))) {
return($themepair);
}

31
Zotlabs/Update/_1249.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
namespace Zotlabs\Update;
class _1249 {
function run() {
dbq("START TRANSACTION");
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r1 = dbq("ALTER TABLE abook ADD abook_role TEXT NOT NULL DEFAULT ''");
$r2 = dbq("CREATE INDEX \"abook_role\" ON abook (\"abook_role\")");
$r = ($r1 && $r2);
}
else {
$r = dbq("ALTER TABLE `abook` ADD `abook_role` CHAR(191) NOT NULL DEFAULT '' ,
ADD INDEX `abook_role` (`abook_role`)");
}
if($r) {
dbq("COMMIT");
return UPDATE_SUCCESS;
}
dbq("ROLLBACK");
return UPDATE_FAILED;
}
}

31
Zotlabs/Update/_1250.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
namespace Zotlabs\Update;
class _1250 {
function run() {
dbq("START TRANSACTION");
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r1 = dbq("ALTER TABLE atoken ADD atoken_guid VARCHAR(255) NOT NULL DEFAULT ''");
$r2 = dbq("CREATE INDEX \"atoken_guid\" ON atoken (\"atoken_guid\")");
$r = ($r1 && $r2);
}
else {
$r = dbq("ALTER TABLE `atoken` ADD `atoken_guid` CHAR(191) NOT NULL DEFAULT '' ,
ADD INDEX `atoken_guid` (`atoken_guid`)");
}
if($r) {
dbq("COMMIT");
return UPDATE_SUCCESS;
}
dbq("ROLLBACK");
return UPDATE_FAILED;
}
}

23
Zotlabs/Update/_1251.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
namespace Zotlabs\Update;
class _1251 {
function run() {
dbq("START TRANSACTION");
$r = dbq("DELETE FROM app WHERE (app_name = 'Channel Home' OR app_name = 'Permission Categories') AND app_system = 1");
if($r) {
dbq("COMMIT");
return UPDATE_SUCCESS;
}
dbq("ROLLBACK");
return UPDATE_FAILED;
}
}

23
Zotlabs/Update/_1252.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
namespace Zotlabs\Update;
class _1252 {
function run() {
dbq("START TRANSACTION");
$r = dbq("DELETE FROM app WHERE app_plugin = 'mail' AND app_system = 1");
if($r) {
dbq("COMMIT");
return UPDATE_SUCCESS;
}
dbq("ROLLBACK");
return UPDATE_FAILED;
}
}

View File

@@ -2,10 +2,13 @@
namespace Zotlabs\Web;
use DateTime;
use DateTimeZone;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\Crypto;
use Zotlabs\Lib\Keyutils;
use Zotlabs\Lib\Webfinger;
use Zotlabs\Lib\Zotfinger;
use Zotlabs\Lib\Libzot;
/**
@@ -13,7 +16,6 @@ use Zotlabs\Lib\Libzot;
*
* @see https://tools.ietf.org/html/draft-cavage-http-signatures-10
*/
class HTTPSig {
/**
@@ -26,10 +28,10 @@ class HTTPSig {
* @return string The generated digest header string for $body
*/
static function generate_digest_header($body,$alg = 'sha256') {
static function generate_digest_header($body, $alg = 'sha256') {
$digest = base64_encode(hash($alg, $body, true));
switch($alg) {
switch ($alg) {
case 'sha512':
return 'SHA-512=' . $digest;
case 'sha256':
@@ -39,29 +41,29 @@ class HTTPSig {
}
}
static function find_headers($data,&$body) {
static function find_headers($data, &$body) {
// decide if $data arrived via controller submission or curl
if(is_array($data) && $data['header']) {
if(! $data['success'])
if (is_array($data) && $data['header']) {
if (!$data['success'])
return [];
$h = new HTTPHeaders($data['header']);
$headers = $h->fetcharr();
$body = $data['body'];
$h = new HTTPHeaders($data['header']);
$headers = $h->fetcharr();
$body = $data['body'];
$headers['(request-target)'] = $data['request_target'];
}
else {
$headers = [];
$headers = [];
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
$headers['content-type'] = $_SERVER['CONTENT_TYPE'];
$headers['content-length'] = $_SERVER['CONTENT_LENGTH'];
$headers['content-type'] = $_SERVER['CONTENT_TYPE'];
$headers['content-length'] = $_SERVER['CONTENT_LENGTH'];
foreach($_SERVER as $k => $v) {
if(strpos($k,'HTTP_') === 0) {
$field = str_replace('_','-',strtolower(substr($k,5)));
foreach ($_SERVER as $k => $v) {
if (strpos($k, 'HTTP_') === 0) {
$field = str_replace('_', '-', strtolower(substr($k, 5)));
$headers[$field] = $v;
}
}
@@ -77,10 +79,10 @@ class HTTPSig {
// See draft-cavage-http-signatures-10
static function verify($data,$key = '', $keytype = '') {
static function verify($data, $key = '', $keytype = '') {
$body = $data;
$headers = null;
$body = $data;
$headers = null;
$result = [
'signer' => '',
@@ -92,21 +94,21 @@ class HTTPSig {
];
$headers = self::find_headers($data,$body);
$headers = self::find_headers($data, $body);
if(! $headers)
if (!$headers)
return $result;
$sig_block = null;
if(array_key_exists('signature',$headers)) {
if (array_key_exists('signature', $headers)) {
$sig_block = self::parse_sigheader($headers['signature']);
}
elseif(array_key_exists('authorization',$headers)) {
elseif (array_key_exists('authorization', $headers)) {
$sig_block = self::parse_sigheader($headers['authorization']);
}
if(! $sig_block) {
if (!$sig_block) {
logger('no signature provided.', LOGGER_DEBUG);
return $result;
}
@@ -117,71 +119,71 @@ class HTTPSig {
$result['header_signed'] = true;
$signed_headers = $sig_block['headers'];
if(! $signed_headers)
$signed_headers = [ 'date' ];
if (!$signed_headers)
$signed_headers = ['date'];
$signed_data = '';
foreach($signed_headers as $h) {
if(array_key_exists($h,$headers)) {
foreach ($signed_headers as $h) {
if (array_key_exists($h, $headers)) {
$signed_data .= $h . ': ' . $headers[$h] . "\n";
}
if($h === 'date') {
$d = new \DateTime($headers[$h]);
$d->setTimeZone(new \DateTimeZone('UTC'));
$dplus = datetime_convert('UTC','UTC','now + 1 day');
$dminus = datetime_convert('UTC','UTC','now - 1 day');
$c = $d->format('Y-m-d H:i:s');
if($c > $dplus || $c < $dminus) {
if ($h === 'date') {
$d = new DateTime($headers[$h]);
$d->setTimeZone(new DateTimeZone('UTC'));
$dplus = datetime_convert('UTC', 'UTC', 'now + 1 day');
$dminus = datetime_convert('UTC', 'UTC', 'now - 1 day');
$c = $d->format('Y-m-d H:i:s');
if ($c > $dplus || $c < $dminus) {
logger('bad time: ' . $c);
return $result;
}
}
}
$signed_data = rtrim($signed_data,"\n");
$signed_data = rtrim($signed_data, "\n");
$algorithm = null;
if($sig_block['algorithm'] === 'rsa-sha256') {
if ($sig_block['algorithm'] === 'rsa-sha256') {
$algorithm = 'sha256';
}
if($sig_block['algorithm'] === 'rsa-sha512') {
if ($sig_block['algorithm'] === 'rsa-sha512') {
$algorithm = 'sha512';
}
if(! array_key_exists('keyId',$sig_block))
if (!array_key_exists('keyId', $sig_block))
return $result;
$result['signer'] = $sig_block['keyId'];
$cached_key = self::get_key($key,$keytype,$result['signer']);
$cached_key = self::get_key($key, $keytype, $result['signer']);
if(! ($cached_key && $cached_key['public_key'])) {
if (!($cached_key && $cached_key['public_key'])) {
return $result;
}
$x = Crypto::verify($signed_data,$sig_block['signature'],$cached_key['public_key'],$algorithm);
$x = Crypto::verify($signed_data, $sig_block['signature'], $cached_key['public_key'], $algorithm);
logger('verified: ' . $x, LOGGER_DEBUG);
$fetched_key = '';
if(! $x) {
if (!$x) {
// try again, ignoring the local actor (xchan) cache and refetching the key
// from its source
$fetched_key = self::get_key($key,$keytype,$result['signer'],true);
$fetched_key = self::get_key($key, $keytype, $result['signer'], true);
if ($fetched_key && $fetched_key['public_key']) {
$y = Crypto::verify($signed_data,$sig_block['signature'],$fetched_key['public_key'],$algorithm);
$y = Crypto::verify($signed_data, $sig_block['signature'], $fetched_key['public_key'], $algorithm);
logger('verified: (cache reload) ' . $x, LOGGER_DEBUG);
}
if (! $y) {
if (!$y) {
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));
logger('server: ' . print_r($_SERVER,true));
logger('affected sigblock: ' . print_r($sig_block, true));
logger('headers: ' . print_r($headers, true));
logger('server: ' . print_r($_SERVER, true));
return $result;
}
@@ -189,58 +191,59 @@ class HTTPSig {
$key = (($fetched_key) ? $fetched_key : $cached_key);
$result['portable_id'] = $key['portable_id'];
$result['portable_id'] = $key['portable_id'];
$result['header_valid'] = true;
if(in_array('digest',$signed_headers)) {
if (in_array('digest', $signed_headers)) {
$result['content_signed'] = true;
$digest = explode('=', $headers['digest'], 2);
if($digest[0] === 'SHA-256')
$digest = explode('=', $headers['digest'], 2);
if ($digest[0] === 'SHA-256')
$hashalg = 'sha256';
if($digest[0] === 'SHA-512')
if ($digest[0] === 'SHA-512')
$hashalg = 'sha512';
if(base64_encode(hash($hashalg,$body,true)) === $digest[1]) {
if (base64_encode(hash($hashalg, $body, true)) === $digest[1]) {
$result['content_valid'] = true;
}
logger('Content_Valid: ' . (($result['content_valid']) ? 'true' : 'false'));
if (! $result['content_valid']) {
logger('invalid content signature: data ' . print_r($data,true));
logger('invalid content signature: headers ' . print_r($headers,true));
logger('invalid content signature: body ' . print_r($body,true));
if (!$result['content_valid']) {
logger('invalid content signature: data ' . print_r($data, true));
logger('invalid content signature: headers ' . print_r($headers, true));
logger('invalid content signature: body ' . print_r($body, true));
}
}
return $result;
}
static function get_key($key,$keytype,$id) {
static function get_key($key, $keytype, $id, $force = false) {
if(is_array($key))
btlogger('key is array: ' . print_r($key,true));
if (is_array($key))
btlogger('key is array: ' . print_r($key, true));
if($key) {
if(function_exists($key)) {
if ($key) {
if (function_exists($key)) {
return $key($id);
}
return [ 'public_key' => $key ];
return ['public_key' => $key];
}
if($keytype === 'zot6') {
$key = self::get_zotfinger_key($id);
if($key) {
if ($keytype === 'zot6') {
$key = self::get_zotfinger_key($id, $force);
if ($key) {
return $key;
}
}
if(strpos($id,'#') === false) {
$key = self::get_webfinger_key($id);
if (strpos($id, '#') === false) {
$key = self::get_webfinger_key($id, $force);
if ($key) {
return $key;
}
}
if(! $key) {
$key = self::get_activitystreams_key($id);
}
$key = self::get_activitystreams_key($id, $force);
return $key;
@@ -249,10 +252,10 @@ class HTTPSig {
static function convertKey($key) {
if(strstr($key,'RSA ')) {
if (strstr($key, 'RSA ')) {
return Keyutils::rsaToPem($key);
}
elseif(substr($key,0,5) === 'data:') {
elseif (substr($key, 0, 5) === 'data:') {
return Keyutils::convertSalmonKey($key);
}
else {
@@ -263,70 +266,88 @@ class HTTPSig {
/**
* @brief
* @brief get a cached key or fetch it with ActivityStreams
*
* @param string $id
* @return boolean|string
* false if no pub key found, otherwise return the pub key
* @param boolean $force (optional, default false)
* @return boolean|array
* false if no pub key found, otherwise return an array with the pub key
*/
static function get_activitystreams_key($id) {
static function get_activitystreams_key($id, $force = false) {
// remove fragment
// Check the local cache first, but remove any fragments like #main-key since these won't be present in our cached data
$url = ((strpos($id, '#')) ? substr($id, 0, strpos($id, '#')) : $id);
$url = ((strpos($id,'#')) ? substr($id,0,strpos($id,'#')) : $id);
// $force is used to ignore the local cache and only use the remote data; for instance the cached key might be stale
if (!$force) {
$x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where (hubloc_id_url = '%s' or hubloc_hash = '%s') and hubloc_network in ('zot6', 'activitypub') order by hubloc_id desc",
dbesc($url),
dbesc($url)
);
$x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s' and hubloc_network in ('zot6', 'activitypub')",
dbesc(str_replace('acct:','',$url)),
dbesc($url)
);
$x = Libzot::zot_record_preferred($x);
if($x && $x['xchan_pubkey']) {
return [ 'portable_id' => $x['xchan_hash'], 'public_key' => $x['xchan_pubkey'] , 'hubloc' => $x ];
if ($x) {
$best = Libzot::zot_record_preferred($x);
}
if ($best && $best['xchan_pubkey']) {
return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'hubloc' => $best];
}
}
// The record wasn't in cache. Fetch it now.
$r = ActivityStreams::fetch($id);
if($r) {
if(array_key_exists('publicKey',$r) && array_key_exists('publicKeyPem',$r['publicKey']) && array_key_exists('id',$r['publicKey'])) {
if($r['publicKey']['id'] === $id || $r['id'] === $id) {
$portable_id = ((array_key_exists('owner',$r['publicKey'])) ? $r['publicKey']['owner'] : EMPTY_STR);
return [ 'public_key' => self::convertKey($r['publicKey']['publicKeyPem']), 'portable_id' => $portable_id, 'hubloc' => [] ];
if ($r) {
if (array_key_exists('publicKey', $r) && array_key_exists('publicKeyPem', $r['publicKey']) && array_key_exists('id', $r['publicKey'])) {
if ($r['publicKey']['id'] === $id || $r['id'] === $id) {
$portable_id = ((array_key_exists('owner', $r['publicKey'])) ? $r['publicKey']['owner'] : EMPTY_STR);
return ['public_key' => self::convertKey($r['publicKey']['publicKeyPem']), 'portable_id' => $portable_id, 'hubloc' => []];
}
}
}
// No key was found
return false;
}
/**
* @brief get a cached key or fetch it with Webfinger
*
* @param string $id
* @param boolean $force (optional, default false)
* @return boolean|array
* false if no pub key found, otherwise return an array with the pub key
*/
static function get_webfinger_key($id) {
static function get_webfinger_key($id, $force = false) {
$x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s'",
dbesc(str_replace('acct:','',$id)),
dbesc($id)
);
if (!$force) {
$x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' and hubloc_network in ('zot6', 'activitypub') order by hubloc_id desc",
dbesc($id)
);
$x = Libzot::zot_record_preferred($x);
if ($x) {
$best = Libzot::zot_record_preferred($x);
}
if($x && $x['xchan_pubkey']) {
return [ 'portable_id' => $x['xchan_hash'], 'public_key' => $x['xchan_pubkey'] , 'hubloc' => $x ];
if ($best && $best['xchan_pubkey']) {
return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'hubloc' => $best];
}
}
$wf = Webfinger::exec($id);
$key = [ 'portable_id' => '', 'public_key' => '', 'hubloc' => [] ];
$wf = Webfinger::exec($id);
$key = ['portable_id' => '', 'public_key' => '', 'hubloc' => []];
if($wf) {
if(array_key_exists('properties',$wf) && array_key_exists('https://w3id.org/security/v1#publicKeyPem',$wf['properties'])) {
if ($wf) {
if (array_key_exists('properties', $wf) && array_key_exists('https://w3id.org/security/v1#publicKeyPem', $wf['properties'])) {
$key['public_key'] = self::convertKey($wf['properties']['https://w3id.org/security/v1#publicKeyPem']);
}
if(array_key_exists('links', $wf) && is_array($wf['links'])) {
foreach($wf['links'] as $l) {
if(! (is_array($l) && array_key_exists('rel',$l))) {
if (array_key_exists('links', $wf) && is_array($wf['links'])) {
foreach ($wf['links'] as $l) {
if (!(is_array($l) && array_key_exists('rel', $l))) {
continue;
}
if($l['rel'] === 'magic-public-key' && array_key_exists('href',$l) && $key['public_key'] === EMPTY_STR) {
if ($l['rel'] === 'magic-public-key' && array_key_exists('href', $l) && $key['public_key'] === EMPTY_STR) {
$key['public_key'] = self::convertKey($l['href']);
}
}
@@ -336,51 +357,64 @@ class HTTPSig {
return (($key['public_key']) ? $key : false);
}
static function get_zotfinger_key($id) {
/**
* @brief get a cached key or fetch it with Zotfinger
*
* @param string $id
* @param boolean $force (optional, default false)
* @return boolean|array
* false if no pub key found, otherwise return an array with the public key
*/
$x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s' and hubloc_network = 'zot6'",
dbesc(str_replace('acct:','',$id)),
dbesc($id)
);
static function get_zotfinger_key($id, $force = false) {
if (!$force) {
$x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' and hubloc_network = 'zot6' order by hubloc_id desc",
dbesc($id)
);
if($x && $x[0]['xchan_pubkey']) {
return [ 'portable_id' => $x[0]['xchan_hash'], 'public_key' => $x[0]['xchan_pubkey'] , 'hubloc' => $x[0] ];
if ($x) {
$best = Libzot::zot_record_preferred($x);
}
if ($best && $best['xchan_pubkey']) {
return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'hubloc' => $best];
}
}
$wf = Webfinger::exec($id);
$key = [ 'portable_id' => '', 'public_key' => '', 'hubloc' => [] ];
$wf = Webfinger::exec($id);
$key = ['portable_id' => '', 'public_key' => '', 'hubloc' => []];
if($wf) {
if(array_key_exists('properties',$wf) && array_key_exists('https://w3id.org/security/v1#publicKeyPem',$wf['properties'])) {
if ($wf) {
if (array_key_exists('properties', $wf) && array_key_exists('https://w3id.org/security/v1#publicKeyPem', $wf['properties'])) {
$key['public_key'] = self::convertKey($wf['properties']['https://w3id.org/security/v1#publicKeyPem']);
}
if(array_key_exists('links', $wf) && is_array($wf['links'])) {
foreach($wf['links'] as $l) {
if(! (is_array($l) && array_key_exists('rel',$l))) {
if (array_key_exists('links', $wf) && is_array($wf['links'])) {
foreach ($wf['links'] as $l) {
if (!(is_array($l) && array_key_exists('rel', $l))) {
continue;
}
if($l['rel'] === 'http://purl.org/zot/protocol/6.0' && array_key_exists('href',$l) && $l['href'] !== EMPTY_STR) {
if ($l['rel'] === 'http://purl.org/zot/protocol/6.0' && array_key_exists('href', $l) && $l['href'] !== EMPTY_STR) {
// The third argument to Zotfinger::exec() tells it not to verify signatures
// Since we're inside a function that is fetching keys with which to verify signatures,
// this is necessary to prevent infinite loops.
$z = \Zotlabs\Lib\Zotfinger::exec($l['href'],null,false);
if($z) {
$z = Zotfinger::exec($l['href'], null, false);
if ($z) {
$i = Libzot::import_xchan($z['data']);
if($i['success']) {
if ($i['success']) {
$key['portable_id'] = $i['hash'];
$x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' and hubloc_network = 'zot6'",
$x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' and hubloc_network = 'zot6' order by hubloc_id desc",
dbesc($l['href'])
);
if($x) {
if ($x) {
$key['hubloc'] = $x[0];
}
}
}
}
if($l['rel'] === 'magic-public-key' && array_key_exists('href',$l) && $key['public_key'] === EMPTY_STR) {
if ($l['rel'] === 'magic-public-key' && array_key_exists('href', $l) && $key['public_key'] === EMPTY_STR) {
$key['public_key'] = self::convertKey($l['href']);
}
}
@@ -402,39 +436,39 @@ class HTTPSig {
* @param array $encryption [ 'key', 'algorithm' ] or false
* @return array
*/
static function create_sig($head, $prvkey, $keyid = EMPTY_STR, $auth = false, $alg = 'sha256', $encryption = false ) {
static function create_sig($head, $prvkey, $keyid = EMPTY_STR, $auth = false, $alg = 'sha256', $encryption = false) {
$return_headers = [];
if($alg === 'sha256') {
if ($alg === 'sha256') {
$algorithm = 'rsa-sha256';
}
if($alg === 'sha512') {
if ($alg === 'sha512') {
$algorithm = 'rsa-sha512';
}
$x = self::sign($head,$prvkey,$alg);
$x = self::sign($head, $prvkey, $alg);
$headerval = 'keyId="' . $keyid . '",algorithm="' . $algorithm . '",headers="' . $x['headers'] . '",signature="' . $x['signature'] . '"';
if($encryption) {
$x = Crypto::encapsulate($headerval,$encryption['key'],$encryption['algorithm']);
if(is_array($x)) {
if ($encryption) {
$x = Crypto::encapsulate($headerval, $encryption['key'], $encryption['algorithm']);
if (is_array($x)) {
$headerval = 'iv="' . $x['iv'] . '",key="' . $x['key'] . '",alg="' . $x['alg'] . '",data="' . $x['data'] . '"';
}
}
if($auth) {
if ($auth) {
$sighead = 'Authorization: Signature ' . $headerval;
}
else {
$sighead = 'Signature: ' . $headerval;
}
if($head) {
foreach($head as $k => $v) {
if ($head) {
foreach ($head as $k => $v) {
// strip the request-target virtual header from the output headers
if($k === '(request-target)') {
if ($k === '(request-target)') {
continue;
}
$return_headers[] = $k . ': ' . $v;
@@ -454,8 +488,8 @@ class HTTPSig {
static function set_headers($headers) {
if($headers && is_array($headers)) {
foreach($headers as $h) {
if ($headers && is_array($headers)) {
foreach ($headers as $h) {
header($h);
}
}
@@ -465,7 +499,7 @@ class HTTPSig {
/**
* @brief
*
* @param array $head
* @param array $head
* @param string $prvkey
* @param string $alg (optional) default 'sha256'
* @return array
@@ -478,21 +512,21 @@ class HTTPSig {
$headers = '';
$fields = '';
logger('signing: ' . print_r($head,true), LOGGER_DATA);
logger('signing: ' . print_r($head, true), LOGGER_DATA);
if($head) {
foreach($head as $k => $v) {
if ($head) {
foreach ($head as $k => $v) {
$headers .= strtolower($k) . ': ' . trim($v) . "\n";
if($fields)
if ($fields)
$fields .= ' ';
$fields .= strtolower($k);
}
// strip the trailing linefeed
$headers = rtrim($headers,"\n");
$headers = rtrim($headers, "\n");
}
$sig = base64_encode(Crypto::sign($headers,$prvkey,$alg));
$sig = base64_encode(Crypto::sign($headers, $prvkey, $alg));
$ret['headers'] = $fields;
$ret['signature'] = $sig;
@@ -513,26 +547,26 @@ class HTTPSig {
static function parse_sigheader($header) {
$ret = [];
$ret = [];
$matches = [];
// if the header is encrypted, decrypt with (default) site private key and continue
if(preg_match('/iv="(.*?)"/ism',$header,$matches))
if (preg_match('/iv="(.*?)"/ism', $header, $matches))
$header = self::decrypt_sigheader($header);
if(preg_match('/keyId="(.*?)"/ism',$header,$matches))
if (preg_match('/keyId="(.*?)"/ism', $header, $matches))
$ret['keyId'] = $matches[1];
if(preg_match('/algorithm="(.*?)"/ism',$header,$matches))
if (preg_match('/algorithm="(.*?)"/ism', $header, $matches))
$ret['algorithm'] = $matches[1];
if(preg_match('/headers="(.*?)"/ism',$header,$matches))
if (preg_match('/headers="(.*?)"/ism', $header, $matches))
$ret['headers'] = explode(' ', $matches[1]);
if(preg_match('/signature="(.*?)"/ism',$header,$matches))
$ret['signature'] = base64_decode(preg_replace('/\s+/','',$matches[1]));
if (preg_match('/signature="(.*?)"/ism', $header, $matches))
$ret['signature'] = base64_decode(preg_replace('/\s+/', '', $matches[1]));
if(($ret['signature']) && ($ret['algorithm']) && (! $ret['headers']))
$ret['headers'] = [ 'date' ];
if (($ret['signature']) && ($ret['algorithm']) && (!$ret['headers']))
$ret['headers'] = ['date'];
return $ret;
return $ret;
}
@@ -552,23 +586,23 @@ class HTTPSig {
$iv = $key = $alg = $data = null;
if(! $prvkey) {
if (!$prvkey) {
$prvkey = get_config('system', 'prvkey');
}
$matches = [];
if(preg_match('/iv="(.*?)"/ism',$header,$matches))
if (preg_match('/iv="(.*?)"/ism', $header, $matches))
$iv = $matches[1];
if(preg_match('/key="(.*?)"/ism',$header,$matches))
if (preg_match('/key="(.*?)"/ism', $header, $matches))
$key = $matches[1];
if(preg_match('/alg="(.*?)"/ism',$header,$matches))
if (preg_match('/alg="(.*?)"/ism', $header, $matches))
$alg = $matches[1];
if(preg_match('/data="(.*?)"/ism',$header,$matches))
if (preg_match('/data="(.*?)"/ism', $header, $matches))
$data = $matches[1];
if($iv && $key && $alg && $data) {
return Crypto::unencapsulate([ 'encrypted' => true, 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data ] , $prvkey);
if ($iv && $key && $alg && $data) {
return Crypto::unencapsulate(['encrypted' => true, 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data], $prvkey);
}
return '';

View File

@@ -6,71 +6,108 @@ namespace Zotlabs\Web;
class HttpMeta {
private $vars = null;
private $og = null;
private $og = null;
function __construct() {
$this->vars = array();
$this->og = array();
$this->vars = [];
$this->og = [];
$this->ogproperties = [];
}
function set($property,$value) {
if(strpos($property,'og:') === 0)
$this->og[$property] = $value;
else
//Set Meta Value
// Mode:
// 0 = Default - set if no value currently exists
// 1 = Overwrite - replace existing value(s)
// 2 = Multi - append to the array of values
function set($property,$value,$mode=0) {
$ogallowsmulti = ['image','audio','video'];
if (strpos($property,'og:') === 0) {
$count = 0;
foreach ($this->og as $ogk => $ogdata) {
if (strpos($ogdata['property'],$property) === 0) {
if ($mode == 1) {
unset($this->og[$ogk]);
unset($this->ogproperties[$property]);
}
elseif ($mode == 0) {
return;
}
elseif ($value == $ogdata['value']) {
return;
}
else {
$count++;
}
}
}
if ($value !== null) {
//mode = 1 with value === null will delete the property entirely.
$components = explode(':',$property);
$ogp=$components[1];
if (!$count || in_array($ogp,$ogallowsmulti)) {
$this->og[]=['property'=>$property,'value'=>$value];
$this->ogproperties[$property] = $property;
}
}
} else {
$this->vars[$property] = $value;
}
}
function check_required() {
if(
($this->og)
&& array_key_exists('og:title',$this->og)
&& array_key_exists('og:type', $this->og)
&& array_key_exists('og:image',$this->og)
&& array_key_exists('og:url', $this->og)
)
if (
in_array('og:title',$this->ogproperties)
&& in_array('og:type', $this->ogproperties)
&& (in_array('og:image',$this->ogproperties)
|| in_array('og:image:url',$this->ogproperties))
&& (array_key_exists('og:url', $this->ogproperties)
|| array_key_exists('og:url:secure_url', $this->ogproperties))
&& array_key_exists('og:description', $this->ogproperties)
) {
return true;
}
return false;
}
function get_field($field) {
if(strpos($field,'og:') === 0)
$arr = $this->og;
else
if (strpos($field,'og:') === 0) {
foreach ($this->og as $ogdata) {
if (strpos($ogdata['property'],$field) === 0) {
$arr[$field][] = $ogdata['value'];
}
}
}
else {
$arr = $this->vars;
}
if($arr && array_key_exists($field,$arr) && $arr[$field])
if (isset($arr) && is_array($arr) && array_key_exists($field,$arr) && $arr[$field]) {
return $arr[$field];
}
return false;
}
function get() {
// use 'name' for most meta fields, and 'property' for opengraph properties
$o = '';
if($this->vars) {
foreach($this->vars as $k => $v) {
$o .= '<meta property="' . $k . '" content="' . urlencode($v) . '" />' . "\r\n" ;
if ($this->vars) {
foreach ($this->vars as $k => $v) {
$o .= '<meta name="' . htmlspecialchars($k,ENT_COMPAT,'UTF-8',false) . '" content="' . htmlspecialchars($v,ENT_COMPAT,'UTF-8',false) . '" />' . "\r\n" ;
}
}
if($this->check_required()) {
$arrayproperties = [ 'og:image' ];
foreach($this->og as $k => $v) {
if (in_array($k,$arrayproperties)) {
if (is_array($v)) {
foreach ($v as $v2) {
$o .= '<meta property="' . $k . '" content="' . $v2 . '" />' . "\r\n" ;
}
} else {
$o .= '<meta property="' . $k . '" content="' . $v . '" />' . "\r\n" ;
}
} else {
$o .= '<meta property="' . $k . '" content="' . $v . '" />' . "\r\n" ;
}
if ($this->check_required()) {
foreach ($this->og as $ogdata) {
$o .= '<meta property="' . htmlspecialchars($ogdata['property'],ENT_COMPAT,'UTF-8',false) . '" content="' . htmlspecialchars($ogdata['value'],ENT_COMPAT,'UTF-8',false) . '" />' . "\r\n" ;
}
}
if($o)
if ($o) {
return "\r\n" . $o;
}
return $o;
}

View File

@@ -25,7 +25,7 @@ class Session {
ini_set('session.cookie_httponly', 1);
$this->custom_handler = boolval(get_config('system', 'session_custom', false));
/*
* Set our session storage functions.
*/
@@ -67,23 +67,24 @@ class Session {
}
// Force cookies to be secure (https only) if this site is SSL enabled.
// Force cookies to be secure (https only) if this site is SSL enabled.
// Must be done before session_start().
$arr = session_get_cookie_params();
// Note when setting cookies: set the domain to false which creates a single domain
// cookie. If you use a hostname it will create a .domain.com wildcard which will
// have some nasty side effects if you have any other subdomains running hubzilla.
// have some nasty side effects if you have any other subdomains running hubzilla.
session_set_cookie_params(
((isset($arr['lifetime'])) ? $arr['lifetime'] : 0),
((isset($arr['path'])) ? $arr['path'] : '/'),
(($arr['domain']) ? $arr['domain'] : false),
((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),
((isset($arr['httponly'])) ? $arr['httponly'] : true)
);
session_set_cookie_params([
'lifetime' => ((isset($arr['lifetime'])) ? $arr['lifetime'] : 0),
'path' => ((isset($arr['path'])) ? $arr['path'] : '/'),
'domain' => (($arr['domain']) ? $arr['domain'] : false),
'secure' => ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),
'httponly' => ((isset($arr['httponly'])) ? $arr['httponly'] : true),
'samesite' => 'None'
]);
register_shutdown_function('session_write_close');
@@ -127,13 +128,36 @@ class Session {
$this->handler->read(session_id());
}
}
else
else
logger('no session handler');
if (x($_COOKIE, 'jsdisabled')) {
setcookie('jsdisabled', $_COOKIE['jsdisabled'], $newxtime, '/', false,((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),((isset($arr['httponly'])) ? $arr['httponly'] : true));
setcookie(
'jsdisabled',
$_COOKIE['jsdisabled'],
[
'expires' => $newxtime,
'path' => '/',
'domain' => false,
'secure' => ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),
'httponly' => ((isset($arr['httponly'])) ? $arr['httponly'] : true),
'samesite' => 'None'
]
);
}
setcookie(session_name(),session_id(),$newxtime, '/', false,((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),((isset($arr['httponly'])) ? $arr['httponly'] : true));
setcookie(
session_name(),
session_id(),
[
'expires' => $newxtime,
'path' => '/',
'domain' => false,
'secure' => ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),
'httponly' => ((isset($arr['httponly'])) ? $arr['httponly'] : true),
'samesite' => 'None'
]
);
$arr = array('expire' => $xtime);
call_hooks('new_cookie', $arr);
@@ -148,8 +172,21 @@ class Session {
$xtime = (($_SESSION['remember_me']) ? (60 * 60 * 24 * 365) : 0 );
if($xtime)
setcookie(session_name(),session_id(),(time() + $xtime), '/', false,((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),((isset($arr['httponly'])) ? $arr['httponly'] : true));
if($xtime) {
setcookie(
session_name(),
session_id(),
[
'expires' => time() + $xtime,
'path' => '/',
'domain' => false,
'secure' => ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),
'httponly' => ((isset($arr['httponly'])) ? $arr['httponly'] : true),
'samesite' => 'None'
]
);
}
$arr = array('expire' => $xtime);
call_hooks('extend_cookie', $arr);
@@ -169,8 +206,8 @@ class Session {
if($_SESSION['addr'] && $_SESSION['addr'] != $_SERVER['REMOTE_ADDR']) {
logger('SECURITY: Session IP address changed: ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']);
$partial1 = substr($_SESSION['addr'], 0, strrpos($_SESSION['addr'], '.'));
$partial2 = substr($_SERVER['REMOTE_ADDR'], 0, strrpos($_SERVER['REMOTE_ADDR'], '.'));
$partial1 = substr($_SESSION['addr'], 0, strrpos($_SESSION['addr'], '.'));
$partial2 = substr($_SERVER['REMOTE_ADDR'], 0, strrpos($_SERVER['REMOTE_ADDR'], '.'));
$paranoia = intval(get_pconfig($_SESSION['uid'], 'system', 'paranoia'));

View File

@@ -1,15 +1,15 @@
<?php
namespace Zotlabs\Widget;
require_once('include/group.php');
use Zotlabs\Lib\AccessList;
class Collections {
function widget($args) {
if(argc() < 2)
return;
// return;
$mode = ((array_key_exists('mode',$args)) ? $args['mode'] : 'conversation');
switch($mode) {
@@ -49,6 +49,6 @@ class Collections {
break;
}
return group_side($every, $each, $edit, $current, $abook_id, $wmode);
return AccessList::widget($every, $each, $edit, $current, $abook_id, $wmode);
}
}

View File

@@ -1,163 +0,0 @@
<?php
namespace Zotlabs\Widget;
class Conversations {
function widget($arr) {
if (! local_channel())
return;
switch(argv(1)) {
case 'inbox':
$mailbox = 'inbox';
$header = t('Received Messages');
break;
case 'outbox':
$mailbox = 'outbox';
$header = t('Sent Messages');
break;
default:
$mailbox = 'combined';
$header = t('Conversations');
break;
}
$o = '';
// private_messages_list() can do other more complicated stuff, for now keep it simple
$r = self::private_messages_list(local_channel(), $mailbox, \App::$pager['start'], \App::$pager['itemspage']);
if(! $r) {
info( t('No messages.') . EOL);
return $o;
}
$messages = [];
foreach($r as $rr) {
$selected = ((argc() == 3) ? intval(argv(2)) == intval($rr['id']) : $r[0]['id'] == $rr['id']);
$messages[] = [
'mailbox' => $mailbox,
'id' => $rr['id'],
'from_name' => $rr['from']['xchan_name'],
'from_url' => chanlink_hash($rr['from_xchan']),
'from_photo' => $rr['from']['xchan_photo_s'],
'to_name' => $rr['to']['xchan_name'],
'to_url' => chanlink_hash($rr['to_xchan']),
'to_photo' => $rr['to']['xchan_photo_s'],
'subject' => (($rr['seen']) ? $rr['title'] : '<strong>' . $rr['title'] . '</strong>'),
'delete' => t('Delete conversation'),
'body' => $rr['body'],
'date' => datetime_convert('UTC',date_default_timezone_get(),$rr['created'], 'c'),
'seen' => $rr['seen'],
'selected' => ((argv(1) != 'new') ? $selected : '')
];
}
$tpl = get_markup_template('mail_head.tpl');
$o .= replace_macros($tpl, [
'$header' => $header,
'$messages' => $messages
]);
return $o;
}
function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) {
$where = '';
$limit = '';
$t0 = dba_timer();
if($numitems)
$limit = " LIMIT " . intval($numitems) . " OFFSET " . intval($start);
if($mailbox !== '') {
$x = q("select channel_hash from channel where channel_id = %d limit 1",
intval($uid)
);
if(! $x)
return array();
$channel_hash = dbesc($x[0]['channel_hash']);
$local_channel = intval(local_channel());
switch($mailbox) {
case 'inbox':
$sql = "SELECT * FROM mail WHERE channel_id = $local_channel AND from_xchan != '$channel_hash' ORDER BY created DESC $limit";
break;
case 'outbox':
$sql = "SELECT * FROM mail WHERE channel_id = $local_channel AND from_xchan = '$channel_hash' ORDER BY created DESC $limit";
break;
case 'combined':
default:
$parents = q("SELECT mail.parent_mid FROM mail LEFT JOIN conv ON mail.conv_guid = conv.guid WHERE mail.mid = mail.parent_mid AND mail.channel_id = %d ORDER BY conv.updated DESC $limit",
intval($local_channel)
);
break;
}
}
$r = null;
if($parents) {
foreach($parents as $parent) {
$all = q("SELECT * FROM mail WHERE parent_mid = '%s' AND channel_id = %d ORDER BY created DESC limit 1",
dbesc($parent['parent_mid']),
intval($local_channel)
);
if($all) {
foreach($all as $single) {
$r[] = $single;
}
}
}
}
elseif($sql) {
$r = q($sql);
}
if(! $r) {
return array();
}
$chans = array();
foreach($r as $rr) {
$s = "'" . dbesc(trim($rr['from_xchan'])) . "'";
if(! in_array($s,$chans))
$chans[] = $s;
$s = "'" . dbesc(trim($rr['to_xchan'])) . "'";
if(! in_array($s,$chans))
$chans[] = $s;
}
$c = q("select * from xchan where xchan_hash in (" . protect_sprintf(implode(',',$chans)) . ")");
foreach($r as $k => $rr) {
$r[$k]['from'] = find_xchan_in_array($rr['from_xchan'],$c);
$r[$k]['to'] = find_xchan_in_array($rr['to_xchan'],$c);
$r[$k]['seen'] = intval($rr['mail_seen']);
if(intval($r[$k]['mail_obscured'])) {
if($r[$k]['title'])
$r[$k]['title'] = base64url_decode(str_rot47($r[$k]['title']));
if($r[$k]['body'])
$r[$k]['body'] = base64url_decode(str_rot47($r[$k]['body']));
}
}
return $r;
}
}

View File

@@ -11,6 +11,6 @@ class Fullprofile {
$block = observer_prohibited();
return profile_sidebar(\App::$profile, $block);
return profile_sidebar(\App::$profile, $block, true, true);
}
}

View File

@@ -1,38 +0,0 @@
<?php
namespace Zotlabs\Widget;
class Mailmenu {
function widget($arr) {
if (! local_channel())
return;
return replace_macros(get_markup_template('message_side.tpl'), array(
'$title' => t('Private Mail Menu'),
'$combined' => array(
'label' => t('Combined View'),
'url' => z_root() . '/mail/combined',
'sel' => (argv(1) == 'combined' || argc() == 1),
),
'$inbox' => array(
'label' => t('Inbox'),
'url' => z_root() . '/mail/inbox',
'sel' => (argv(1) == 'inbox'),
),
'$outbox' => array(
'label' => t('Outbox'),
'url' => z_root() . '/mail/outbox',
'sel' => (argv(1) == 'outbox'),
),
/*
'$new' => array(
'label' => t('New Message'),
'url' => z_root() . '/mail/new',
'sel'=> (argv(1) == 'new'),
)
*/
));
}
}

View File

@@ -11,7 +11,7 @@ class Messages {
if (!local_channel())
return EMPTY_STR;
$page = self::get_messages_page($options);
$page = self::get_messages_page([]);
$_SESSION['messages_loadtime'] = datetime_convert();
@@ -24,6 +24,7 @@ class Messages {
'messages_title' => t('Public and restricted messages'),
'direct_messages_title' => t('Direct messages'),
'starred_messages_title' => t('Starred messages'),
'notice_messages_title' => t('Notices'),
'loading' => t('Loading'),
'empty' => t('No messages')
]
@@ -40,6 +41,10 @@ class Messages {
return;
}
if ($options['type'] == 'notification') {
return self::get_notices_page($options);
}
$channel = App::get_channel();
$item_normal = item_normal();
$entries = [];
@@ -81,6 +86,7 @@ class Messages {
xchan_query($items, false);
$i = 0;
$entries = [];
foreach($items as $item) {
@@ -171,8 +177,7 @@ class Messages {
stringify_array_elms($recips, true);
$query_str = implode(',', $recips);
$xchans = dbq("SELECT DISTINCT xchan_name FROM xchan WHERE $column IN ($query_str)");
$xchans = dbq("SELECT DISTINCT xchan_name FROM xchan WHERE $column IN ($query_str) AND xchan_deleted = 0");
foreach($xchans as $xchan) {
$recipients .= $xchan['xchan_name'] . ', ';
}
@@ -181,4 +186,51 @@ class Messages {
return trim($recipients, ', ');
}
public static function get_notices_page($options) {
if (!local_channel())
return;
$limit = 30;
$offset = 0;
if ($options['offset']) {
$offset = intval($options['offset']);
}
$notices = q("SELECT * FROM notify WHERE uid = %d
ORDER BY created DESC LIMIT $limit OFFSET $offset",
intval(local_channel())
);
$i = 0;
$entries = [];
foreach($notices as $notice) {
$summary = trim(strip_tags(bbcode($notice['msg'])));
if(strpos($summary, $notice['xname']) === 0) {
$summary = substr($summary, strlen($notice['xname']) + 1);
}
$entries[$i]['author_name'] = $notice['xname'];
$entries[$i]['author_addr'] = $notice['url'];
$entries[$i]['info'] = '';
$entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $notice['created']);
$entries[$i]['summary'] = $summary;
$entries[$i]['b64mid'] = (($notice['ntype'] & NOTIFY_INTRO) ? '' : basename($notice['link']));
$entries[$i]['href'] = (($notice['ntype'] & NOTIFY_INTRO) ? $notice['link'] : z_root() . '/hq/' . basename($notice['link']));
$entries[$i]['icon'] = (($notice['ntype'] & NOTIFY_INTRO) ? '<i class="fa fa-user-plus"></i>' : '');
$i++;
}
$result = [
'offset' => ((count($entries) < $limit) ? -1 : intval($offset + $limit)),
'entries' => $entries
];
return $result;
}
}

View File

@@ -18,13 +18,24 @@ class Notes {
$tpl = get_markup_template('notes.tpl');
$o = replace_macros($tpl, array(
'$banner' => t('Notes'),
'$text' => $text,
'$save' => t('Save'),
'$html' => bbcode($text),
'$app' => ((isset($arr['app'])) ? true : false),
'$hidden' => ((isset($arr['hidden'])) ? true : false)
'$hidden' => ((isset($arr['hidden'])) ? true : false),
'$strings' => [
'title' => t('Notes'),
'read' => t('Read mode'),
'edit' => t('Edit mode'),
'editing' => t('Editing'),
'saving' => t('Saving'),
'saved' => t('Saved'),
'dots' => '<span class="jumping-dots"><span class="dot-1">.</span><span class="dot-2">.</span><span class="dot-3">.</span></span>'
]
));
return $o;
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace Zotlabs\Widget;
use Zotlabs\Lib\Permcat;
use Zotlabs\Access\PermissionLimits;
class Permcats {
function widget($arr) {
$pcat = new Permcat(local_channel());
$pcatlist = $pcat->listing();
if (!$pcatlist) {
return;
}
$roles = [];
$active_role = '';
foreach($pcatlist as $pc) {
if (!$active_role) {
$active_role = ((argc() > 1 && $pc['name'] === hex2bin(argv(1))) ? $pc['name'] : '');
}
$roles[] = [
'name' => $pc['localname'],
'url' => z_root() . '/permcats/' . bin2hex($pc['name']),
'active' => (argc() > 1 && $pc['name'] === hex2bin(argv(1)))
];
}
if($active_role) {
$roles[] = [
'name' => '<i class="fa fa-plus"></i>&nbsp;' . t('Add new role'),
'url' => z_root() . '/permcats',
'active' => ''
];
/* get role members based on permissions
$test = $pcatlist[$active]['perms'];
$role_sql = '';
$count = 0;
foreach ($test as $t) {
$checkinherited = PermissionLimits::Get(local_channel(),$t['name']);
if($checkinherited & PERMS_SPECIFIC) {
$role_sql .= "( abconfig.k = '" . dbesc($t['name']) . "' AND abconfig.v = '" . intval($t['value']) . "' ) OR ";
$count++;
}
}
$role_sql = rtrim($role_sql, ' OR ');
$r = q("SELECT abconfig.xchan, xchan.xchan_name, abook.abook_id FROM abconfig LEFT JOIN xchan on abconfig.xchan = xchan.xchan_hash LEFT JOIN abook ON abconfig.xchan = abook.abook_xchan WHERE xchan.xchan_deleted = 0 and abconfig.chan = %d AND abconfig.cat = 'my_perms' AND ( $role_sql ) GROUP BY abconfig.xchan HAVING count(abconfig.xchan) = %d ORDER BY xchan.xchan_name",
intval(local_channel()),
intval($count)
);
*/
// get role members based on abook_role
$r = q("SELECT abook.abook_id, abook.abook_role, xchan.xchan_name, xchan.xchan_addr, xchan.xchan_url, xchan.xchan_photo_s FROM abook
LEFT JOIN xchan on abook.abook_xchan = xchan.xchan_hash
WHERE abook.abook_channel = %d AND abook.abook_role = '%s' AND abook_self = 0 AND xchan_deleted = 0
ORDER BY xchan.xchan_name",
intval(local_channel()),
dbesc($active_role)
);
$members = [];
foreach ($r as $rr) {
$members[] = [
'name' => $rr['xchan_name'],
'addr' => (($rr['xchan_addr']) ? $rr['xchan_addr'] : $rr['xchan_url']),
'url' => z_root() . '/connections#' . $rr['abook_id'],
'photo' => $rr['xchan_photo_s']
];
}
}
$tpl = get_markup_template("permcats_widget.tpl");
$o .= replace_macros($tpl, [
'$roles_label' => t('Contact roles'),
'$members_label' => t('Role members'),
'$roles' => $roles,
'$members' => $members
]);
return $o;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Zotlabs\Widget;
use Zotlabs\Lib\AccessList;
class Privacygroups {
function widget($arr) {
$o = '';
$groups = q("SELECT id, gname FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
intval(local_channel())
);
if (!$groups) {
return $o;
}
$menu_items = [];
$z_root = z_root();
$active = argv(1) ?? '';
foreach($groups as $group) {
$menu_items[] = [
'href' => $z_root . '/group/' . $group['id'],
'label' => $group['gname'],
'title' => '',
'active' => ($active === $group['id']),
'count' => count(AccessList::members(local_channel(), $group['id']))
];
}
if ($active) {
$menu_items[] = [
'href' => $z_root . '/group',
'label' => '<i class="fa fa-plus"></i> &nbsp;' . t('Add new group'),
'title' => '',
'active' => '',
'count' => ''
];
}
$tpl = get_markup_template("widget_menu_count.tpl");
$o .= replace_macros($tpl, [
'$title' => t('Privacy groups'),
'$menu_items' => $menu_items,
]);
return $o;
}
}

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