Compare commits

...

107 Commits
7.0.3 ... 7.2

Author SHA1 Message Date
Mario Vavti
0784cd593a Merge branch '7.2RC' 2022-03-29 11:42:53 +02:00
Mario Vavti
9c5d2ee563 version 7.2 2022-03-29 11:41:33 +02:00
Mario Vavti
be5f7c2e67 Merge branch 'dev' into 7.2RC 2022-03-29 11:39:28 +02:00
Mario Vavti
0d0f73fb67 update changelog 2022-03-29 11:38:59 +02:00
Mario
680be6cfec Merge branch 'fix-changelog' into 'dev'
Update changelog with missing fix and cve

See merge request hubzilla/core!2018
2022-03-27 19:51:20 +00:00
Harald Eilertsen
2ab3d072b0 Update changelog with missing fix and cve 2022-03-25 22:14:39 +01:00
Mario
943ecff623 fix version 2022-03-25 09:27:39 +00:00
Mario
03973f5d1d changelog 2022-03-25 09:22:57 +00:00
Mario
c42a0fa9b6 bump dev version 2022-03-23 20:20:44 +00:00
Mario
61522ed31d strings 2022-03-23 20:16:33 +00:00
Mario Vavti
29a527426a make sure to set comments_closed to the created date if nocomment is set 2022-03-23 21:08:54 +01:00
Mario
62ac0ff21e streamline comment policy with downstream 2022-03-23 19:01:42 +00:00
Mario
a41c7caa18 Merge branch 'security-fixes-lfi-xss-open-redirect' into 'dev'
Security fixes

See merge request hubzilla/core!2017
2022-03-23 18:38:03 +00:00
Harald Eilertsen
b3ca31bce7 CVE-2022-27256: Open redirect via rpath query param.
Don't follow urls to external sites when submitting forms from the
settings modules. This mitigates an Open Redirect vulnerability where an
attacker could trick a user to go to an attacker controlled destination.

Fixes part of https://framagit.org/hubzilla/core/-/issues/1666
2022-03-20 15:34:24 +01:00
Harald Eilertsen
b02f6a1dae Add function is_local_url() to check if url is local. 2022-03-20 15:34:24 +01:00
Harald Eilertsen
d35609f33a CVE-2022-27258: XSS via rpath query param.
Escape URLs provided by the rpath query param in settings modules. This
prevents a possible Cross-Site scripting vulnerability, where an
attacker could inject web scripts and html into the settings form via
the rpath query parameter, and have a user execute the script by
tricking them to clicking a link.

Fixes part of https://framagit.org/hubzilla/core/-/issues/1666
2022-03-20 15:34:24 +01:00
Harald Eilertsen
8c19ab8f9f Add helper to escape URLs.
The escaping makes the URL safe for display and for use in HTML element
attributes (such as href="..." etc), but does not guarantee that the URL
itself is valid after conversion. This should be good enough for
mitigating XSS issues caused by injecting html or javascript into a URL.
Also probably good enough for _most_ normal URLs, but there may be
devils hidden in the details somewhere.
2022-03-20 15:34:24 +01:00
Harald Eilertsen
30ae198b89 CVE-2022-27257: LFI in Redbasic theme.
Limit valid chars in schema names, and discard attempts at loading
schemas with invalid names.

This prevents a local file inclusion vulnerability where an
unauthenticated attacker can include arbitrary php files readable by the
server process and potentially obtain remote code execution.

Valid schema names may consist of ascii letters, numbers, hyphens and
underscores. Should be good enough for most cases, I think.

Fixes https://framagit.org/hubzilla/core/-/issues/1665
2022-03-20 15:34:17 +01:00
Mario
bddeab3ac1 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2022-03-20 08:59:07 +00:00
Mario
591349ee74 add the signing algo to zotinfo, and store it in import_xchan() if present 2022-03-20 08:58:18 +00:00
Mario
9081a25e64 Merge branch 'volse-redbasic-dark' into 'dev'
redbasic/dark: Use bootstrap-nightfall for dark schema.

See merge request hubzilla/core!2016
2022-03-20 08:27:30 +00:00
Mario
1beadfc6e7 Merge branch 'volse-fix-stylesheet-root-path' into 'dev'
Use correct base url for stylesheets and js.

See merge request hubzilla/core!2015
2022-03-17 08:23:02 +00:00
Harald Eilertsen
f4af532c5a Trim trailing & from query_string.
When trying to fetch an image file from the Cloud module, the default
nginx config will add a trailing & if there's no args specified.

Example:

https://example.com/cloud/username/some_image.png

This will be rewritten to:

https://example.com/index.php?q=/cloud/username/some_image.png&

This in turn will cause the Cloud module to try to redirect back to the
original because it does not match the query_string (in which the
ampersand has been converted to a question mark). And this will repeat
until the browser get's tired of it.
2022-03-13 19:37:45 +01:00
Harald Eilertsen
76eb1a9d78 redbasic/dark: Tune button colours a bit.
Makes buttons a bit less bright so they don't stick out quite as much.
2022-03-13 17:08:06 +01:00
Harald Eilertsen
14a2790dcb redbasic/dark: Use schema colour for dropdown item 2022-03-13 17:08:06 +01:00
Harald Eilertsen
46f54db197 redbasic/dark: Use bootstrap-nightfall for dark schema.
This is a color only stylesheet, modifying the original Bootstrap colors
to a dark variant. Insert this as base before the redbasic dark schema
modifications, and any custom modifications to have a nicer base for the
dark schema.
2022-03-13 17:07:45 +01:00
Mario
4ffd7587a9 make sure an announce does not overwrite an item we already have and make sure it will be a toplevel post 2022-03-11 20:29:18 +00:00
Mario
c48c62c7a8 whitespace 2022-03-10 11:41:34 +00:00
Mario
9e7fd20ade support for hs2019 2022-03-10 09:44:37 +00:00
Mario
efa1d381ba move attachments to the top 2022-03-05 13:55:16 +00:00
Mario
740fa058aa remove logging 2022-03-04 18:43:54 +00:00
Mario
37f56e1efd event fixes 2022-03-04 18:43:21 +00:00
Mario
6294be371a bump version 2022-03-04 16:12:44 +00:00
Mario
bee7549a1e fix regression 2022-03-04 16:09:44 +00:00
Mario
db14dbacc9 streamline event activity handling 2022-03-04 15:05:58 +00:00
Mario
27058e6297 bump version 2022-03-04 09:51:21 +00:00
Mario
b41175e0e2 port some ap quirks from the addon 2022-03-04 09:45:11 +00:00
Mario
eeea3251ad more work on enhanced content filters 2022-03-03 20:31:43 +00:00
Harald Eilertsen
34ffff3947 Remove now unused function script_path. 2022-03-03 19:50:07 +01:00
Harald Eilertsen
65ed3818ec Use correct base url for stylesheets and js.
Use z_root instead of script_path when formatting stylesheet and
javascript links for the head section. script_path does not preserve
information about the port if the site uses a nonstandard port.
2022-03-03 19:47:33 +01:00
Mario
c8417df6f1 fix duplicate ids in login form and move login/register buttons into the hamburger menu on small screens 2022-03-03 16:52:04 +00:00
Mario
dc3be7ecf7 collect the accept headers in an array 2022-03-03 11:22:46 +00:00
Mario
cf3c0b593b Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2022-03-02 19:53:16 +00:00
Mario
34f64148e8 composer updates 2022-03-02 19:52:47 +00:00
Mario
fc5dad1983 port some peertube tweeks from pubcrawl to lib/activity 2022-03-02 19:52:26 +00:00
Mario
c3c40548b9 Merge branch 't0rum-master-patch-68993' into 'master'
Typo in Setup.php prevents users from using Postgres

See merge request hubzilla/core!2014

(cherry picked from commit 0e2e932102)

d384f55d Typo in Setup.php prevents users from using Postgres
2022-03-01 21:21:50 +01:00
Mario
0e2e932102 Merge branch 't0rum-master-patch-68993' into 'master'
Typo in Setup.php prevents users from using Postgres

See merge request hubzilla/core!2014
2022-03-01 20:20:58 +00:00
Mario
6930c4e23b fix feedutils regression 2022-03-01 20:06:10 +00:00
Mario
01b9f2dfcf enhanced content filters 2022-03-01 10:14:05 +00:00
Mario
0cc6f66a26 make gprobe deal with URLs, fix issue in get_actor_protocols and fix missing author issue if wall2wall comment arrives and author is not yet known 2022-02-28 10:16:19 +00:00
t0rum
d384f55dd1 Typo in Setup.php prevents users from using Postgres 2022-02-26 19:15:02 +00:00
Mario
1893368aa5 missing content region for directory 2022-02-23 13:52:49 +00:00
Mario
a520063265 widget descriptions and add content region to all pdl files for convenience 2022-02-23 11:52:11 +00:00
Mario
1e171a72a0 bump version 2022-02-21 10:18:39 +00:00
Mario
5b1a0d93b9 this was required for old style forum posts only and should not be needed anymore 2022-02-21 10:16:21 +00:00
Mario
7e04662a9c do not require network for forums widget 2022-02-21 10:03:43 +00:00
Mario
a804549781 remove deprecated widgets and add some more widget descriptions 2022-02-21 10:02:33 +00:00
Mario
2a60f1cc6e merge branch pdledit_gui into dev - many widgets still miss their description and requirements (this is work in progress) 2022-02-20 20:18:24 +00:00
Mario
2ddff785e5 thr_parent lost across edits 2022-02-18 18:52:38 +00:00
Mario
38882efb5c composer update 2022-02-18 12:43:10 +00:00
Mario
6f7786b068 Merge branch 'undefined' into 'dev'
Replace htconfig.tpl fr

See merge request hubzilla/core!2008
2022-02-15 10:41:36 +00:00
Mario
0819141f03 Merge branch 'dandauge-dev-patch-02109' into 'dev'
Update lostpass_eml.tpl fr

See merge request hubzilla/core!2009
2022-02-15 10:38:43 +00:00
Mario
329ef5049f Merge branch 'dandauge-dev-patch-69038' into 'dev'
Upload New File : invite.material.subject.tpl fr

See merge request hubzilla/core!2010
2022-02-15 10:38:12 +00:00
Mario
34bb8c65d6 Merge branch 'dandauge-dev-patch-34611' into 'dev'
Upload New File : invite.material.tpl fr

See merge request hubzilla/core!2011
2022-02-15 10:37:52 +00:00
Mario
575ccae6f9 Merge branch 'dandauge-dev-patch-30995' into 'dev'
Update passchanged_eml.tpl fr

See merge request hubzilla/core!2012
2022-02-15 10:37:30 +00:00
Mario
486be87e33 Merge branch 'dandauge-dev-patch-92660' into 'dev'
Update update_fail_eml.tpl fr

See merge request hubzilla/core!2013
2022-02-15 10:37:03 +00:00
Mario
c0350861ef php8 warnings 2022-02-13 19:31:51 +00:00
Mario
a7ec1805e3 address deprecation warnings 2022-02-13 18:58:12 +00:00
Mario
4b06bc552f add inbound support for quoteUrl 2022-02-13 16:53:43 +00:00
Dan d'Auge
76ee7b7eea Update update_fail_eml.tpl fr 2022-02-13 06:21:35 +00:00
Dan d'Auge
04b1e7e34f Update passchanged_eml.tpl fr 2022-02-13 06:08:31 +00:00
Dan d'Auge
17dbb156e1 Upload New File : invite.material.tpl fr 2022-02-13 06:03:17 +00:00
Dan d'Auge
135117c637 Upload New File : invite.material.subject.tpl fr 2022-02-13 05:54:55 +00:00
Dan d'Auge
d3348f7855 Update lostpass_eml.tpl fr 2022-02-13 05:51:23 +00:00
Dan d'Auge
39bbcb66c8 Replace htconfig.tpl fr 2022-02-13 05:22:33 +00:00
Mario
d45e8e4d20 make sure we have an array 2022-02-11 12:59:08 +00:00
Mario
d65052c1ac comment out failing tests for now 2022-02-11 12:19:50 +00:00
Mario
2fbc42753f language test 2022-02-11 11:42:33 +00:00
Mario
4195865965 add options 2022-02-11 11:29:08 +00:00
Mario
de3f6fbeba more dependencies 2022-02-11 11:25:24 +00:00
Mario
6a377120bd lets try this 2022-02-11 11:22:03 +00:00
Mario
502226b0a6 more ci testing 2022-02-11 11:16:03 +00:00
Mario
78206b48f4 add php-gd 2022-02-11 11:12:28 +00:00
Mario
dc3cec06ca restructure 2022-02-11 11:08:43 +00:00
Mario
5a7688e099 do phpunit though 2022-02-11 11:05:11 +00:00
Mario
c721f01c76 skip db tests for now 2022-02-11 11:02:50 +00:00
Mario
463806822c more ci testing 2022-02-11 10:56:23 +00:00
Mario
b74c2f001d more ci testing 2022-02-11 10:54:00 +00:00
Mario
9fc7a8b626 more ci testing 2022-02-11 10:47:27 +00:00
Mario
3ffd92a6c3 more ci testing 2022-02-11 10:45:31 +00:00
Mario
29b02e5329 more ci testing 2022-02-11 10:43:42 +00:00
Mario
97584e046f more ci testing 2022-02-11 10:40:04 +00:00
Mario
09d2fce85d run composer updatee 2022-02-11 10:33:54 +00:00
Mario
fc3060cb29 we do not actually require this for the tests 2022-02-11 10:28:22 +00:00
Mario
9804a67165 more version bumps 2022-02-11 10:24:51 +00:00
Mario
21eddefa41 typo 2022-02-11 10:18:10 +00:00
Mario
9b62e7eedb bump php image to 8.0 2022-02-11 10:17:04 +00:00
Mario
bf30cfd8a4 more composer updates 2022-02-11 10:01:39 +00:00
Mario
139ffae367 fix another deprecation warning 2022-02-11 09:51:21 +00:00
Mario
51a48cc264 bump php version 2022-02-11 09:42:54 +00:00
Mario
abbca12565 typo 2022-02-11 09:37:29 +00:00
Mario
9e9d96a2ec minor restructure to omit php 8.1 deprecation warning 2022-02-11 09:36:17 +00:00
Mario
615c9f1cbe composer update smarty to version 4.1 - new files 2022-02-11 09:27:57 +00:00
Mario
7d75d0cfbd composer update smarty to version 4.1 2022-02-11 09:27:35 +00:00
Mario
5468de2c6a composer libs minor version updates add new files 2022-02-11 09:23:29 +00:00
Mario
6d8aabab23 composer libs minor version updates 2022-02-11 09:21:19 +00:00
Mario
e74359fcfe 3rd arg in str_replace() can not be null 2022-02-11 09:10:19 +00:00
926 changed files with 30074 additions and 12435 deletions

View File

@@ -1,7 +1,9 @@
# Select image from https://hub.docker.com/_/php/
#image: php:7.3
# Use a prepared Hubzilla image to optimise pipeline duration
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
# image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
image: php:8.0
stages:
- test
@@ -35,7 +37,12 @@ before_script:
- if [ -f /usr/local/etc/php/conf.d/z_prod.ini ]; then mv /usr/local/etc/php/conf.d/z_prod.ini /usr/local/etc/php/conf.d/z_prod.ini.off; fi
# Install & enable Xdebug for code coverage reports
- pecl install xdebug
- apt-get update
- apt-get install zip unzip libjpeg-dev libpng-dev -yqq
- docker-php-ext-enable xdebug
- docker-php-ext-install gd
# Install composer
- curl -sS https://getcomposer.org/installer | php
# Install dev libraries from composer
@@ -43,31 +50,38 @@ before_script:
# php.ini settings
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
# hidden job definition with template for MySQL/MariaDB
.job_template_mysql: &job_definition_mysql
# hidden job definition with template for PHP
.job_template_php: &job_definition_php
stage: test
script:
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
# hidden job definition with template for MySQL/MariaDB
#.job_template_mysql: &job_definition_mysql
# stage: test
# script:
# - echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
# - echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
# - echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
# - vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
# hidden job definition with template for PostgreSQL
.job_template_postgres: &job_definition_postgres
stage: test
services:
- postgres:latest
script:
- export PGPASSWORD=$POSTGRES_PASSWORD
- psql --version
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT VERSION();"
#.job_template_postgres: &job_definition_postgres
# stage: test
# services:
# - postgres:latest
# script:
# - export PGPASSWORD=$POSTGRES_PASSWORD
# - psql --version
# - psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT VERSION();"
# Import hubzilla's DB schema
- psql -h "postgres" -U "$POSTGRES_USER" -v ON_ERROR_STOP=1 --quiet "$POSTGRES_DB" < ./install/schema_postgres.sql
# - psql -h "postgres" -U "$POSTGRES_USER" -v ON_ERROR_STOP=1 --quiet "$POSTGRES_DB" < ./install/schema_postgres.sql
# Show databases and relations/tables of hubzilla's database
#- psql -h "postgres" -U "$POSTGRES_USER" -l
#- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt;"
# Run the actual tests
- vendor/bin/phpunit --configuration tests/phpunit-pgsql.xml --testdox
# - vendor/bin/phpunit --configuration tests/phpunit-pgsql.xml --testdox
# hidden job definition with artifacts config template
.artifacts_template:
@@ -82,36 +96,41 @@ before_script:
- tests/results/
# PHP7.3 with MySQL 5.7
php7.3_mysql5.7:
<<: *job_definition_mysql
services:
- mysql:5.7
# PHP8.0
php8.0:
<<: *job_definition_php
# PHP8.0 with MySQL 5.7
#php8.0_mysql5.7:
# <<: *job_definition_mysql
# services:
# - mysql:5.7
# PHP7.3 with MySQL 8 (latest)
php7.3_mysql8:
<<: *job_definition_mysql
services:
- name: mysql:8
command: ["--default-authentication-plugin=mysql_native_password"]
# PHP8.0 with MySQL 8 (latest)
#php8.0_mysql8:
# <<: *job_definition_mysql
# services:
# - name: mysql:8
# command: ["--default-authentication-plugin=mysql_native_password"]
# PHP7.3 with MariaDB 10.2
php7.3_mariadb10.2:
<<: *job_definition_mysql
services:
- name: mariadb:10.2
alias: mysql
# PHP8.0 with MariaDB 10.2
#php8.0_mariadb10.2:
# <<: *job_definition_mysql
# services:
# - name: mariadb:10.2
# alias: mysql
# PHP7.3 with MariaDB 10.3 (latest)
php7.3_mariadb10.3:
<<: *job_definition_mysql
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
services:
- name: mariadb:10.3
alias: mysql
# PHP8.0 with MariaDB 10.3 (latest)
#php8.0_mariadb10.3:
# <<: *job_definition_mysql
# image: php:8.0
#image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
# services:
# - name: mariadb:10.3
# alias: mysql
# PHP7.3 with PostgreSQL latest (11)
@@ -131,7 +150,7 @@ php7.3_mariadb10.3:
pages:
stage: deploy
cache: {}
image: php:7-cli-alpine
image: php:8-cli-alpine
before_script:
- apk update
- apk add doxygen ttf-freefont graphviz

View File

@@ -1,3 +1,42 @@
Hubzilla 7.2 (2022-03-29)
- Streamline comment policy with downstream project
- Add new function is_local_url()
- Add helper function to escape URLs
- Add signing algorithm to zotinfo()
- Store signing algorithm in import_xchan()
- Use bootstrap-nightfall for redbasic:dark schema
- Add support for hs2019
- Remove unused function script_path()
- Move login and register button into the hamburger menu for small screens
- Collect accept headers in an array instead of a concatenated string
- Update composer libs
- Enhanced content filters
- Improve mod gprobe to also deal with URLs
- Adapt unseen forum posts query in mod network to new forum post style
- Remove deprecated widgets
- Add inbound support for quoteUrl to Lib/Activity
- Add widget descriptions
- Add a GUI for the PDL editor
Addons
- Pubcrawl: deprecate as.php in favor of core libs
- Pubcrawl: rewrite/modernize mod inbox
- Pubcrawl: reflect core enhanced content filter changes
- Diaspora: reflect core enhanced content filter changes
- Fediwordle: new addon - a distributed word game inspired by wordle
- Pubcrawl: streamline post_local hook with diaspora
Bugfixes
- Fix comments_closed date on posts where comments are disabled
- Fix open redirect via rpath query param (CVE-2022-27256)
- Fix cross-site scripting via rpath query param (CVE-2022-27258)
- Fix local file inclusion in redbasic theme (CVE-2022-27257)
- Fix baseurl for css and js
- Fix duplicate IDs in login form
- Fix unknown author not fetched if w2w comment arrives
- Fix thr_parent lost across edits
Hubzilla 7.0.3 (2022-02-10)
- Allow to override the charset for the PDO connection string via $db_charset in .htconfig.php

View File

@@ -15,19 +15,31 @@ class Gprobe {
return;
$url = hex2bin($argv[1]);
$is_webbie = false;
$r = null;
if (!strpos($url, '@'))
return;
if (filter_var($url, FILTER_VALIDATE_EMAIL)) {
$is_webbie = true;
$r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($url)
);
$r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($url)
);
}
elseif (filter_var($url, FILTER_VALIDATE_URL)) {
$r = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($url)
);
}
if (!$r) {
$href = Webfinger::zot_url(punify($url));
if ($href) {
$zf = Zotfinger::exec($href, null);
if ($is_webbie) {
$url = Webfinger::zot_url(punify($url));
}
if ($url) {
$zf = Zotfinger::exec($url, null);
}
if (is_array($zf) && array_path_exists('signature/signer', $zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
Libzot::import_xchan($zf['data']);
}

View File

@@ -311,55 +311,6 @@ class AccessList {
return $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",
intval($_SESSION['uid'])
);
$member_of = [];
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' => "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),
];
}
}
return replace_macros(get_markup_template('group_side.tpl'), [
'$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))) {
return [];

View File

@@ -501,7 +501,7 @@ class Activity {
$ret['attributedTo'] = $i['author']['xchan_url'];
if ($i['id'] != $i['parent']) {
if ($i['mid'] !== $i['parent_mid']) {
$ret['inReplyTo'] = ((strpos($i['thr_parent'], 'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent']));
}
@@ -608,10 +608,10 @@ class Activity {
$ptr = [$ptr];
}
foreach ($ptr as $t) {
if (!array_key_exists('type', $t))
if (is_array($t) && !array_key_exists('type', $t))
$t['type'] = 'Hashtag';
if (array_key_exists('href', $t) && array_key_exists('name', $t)) {
if (is_array($t) && array_key_exists('href', $t) && array_key_exists('name', $t)) {
switch ($t['type']) {
case 'Hashtag':
$ret[] = ['ttype' => TERM_HASHTAG, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'], 0, 1) === '#') ? substr($t['name'], 1) : $t['name'])];
@@ -875,7 +875,7 @@ class Activity {
}
}
if ($i['id'] != $i['parent']) {
if ($i['mid'] !== $i['parent_mid']) {
$reply = true;
// inReplyTo needs to be set in the activity for followup actions (Like, Dislike, Announce, etc.),
@@ -1154,9 +1154,10 @@ class Activity {
$ret['url'] = $p['xchan_url'];
$ret['publicKey'] = [
'id' => $p['xchan_url'],
'owner' => $p['xchan_url'],
'publicKeyPem' => $p['xchan_pubkey']
'id' => $p['xchan_url'],
'owner' => $p['xchan_url'],
'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
'publicKeyPem' => $p['xchan_pubkey']
];
if ($c) {
@@ -1599,6 +1600,25 @@ class Activity {
return;
}
public static function drop($channel, $observer, $act) {
$r = q(
"select * from item where mid = '%s' and uid = %d limit 1",
dbesc((is_array($act->obj)) ? $act->obj['id'] : $act->obj),
intval($channel['channel_id'])
);
if (!$r) {
return;
}
if (in_array($observer, [$r[0]['author_xchan'], $r[0]['owner_xchan']])) {
drop_item($r[0]['id'], false);
} elseif (in_array($act->actor['id'], [$r[0]['author_xchan'], $r[0]['owner_xchan']])) {
drop_item($r[0]['id'], false);
}
}
static function actor_store($url, $person_obj, $force = false) {
if (!is_array($person_obj)) {
@@ -1618,6 +1638,7 @@ class Activity {
$ap_hubloc = null;
$hublocs = self::get_actor_hublocs($url);
$has_zot_hubloc = false;
if ($hublocs) {
foreach ($hublocs as $hub) {
@@ -1625,6 +1646,7 @@ class Activity {
$ap_hubloc = $hub;
}
if ($hub['hubloc_network'] === 'zot6') {
$has_zot_hubloc = true;
Libzot::update_cached_hubloc($hub);
}
}
@@ -1648,16 +1670,18 @@ class Activity {
return;
}
$inbox = $person_obj['inbox'];
$inbox = $person_obj['inbox'] ?? null;
// invalid identity
// invalid AP identity
if (!$inbox || strpos($inbox, z_root()) !== false) {
return;
}
// store the actor record in XConfig
XConfig::Set($url, 'system', 'actor_record', $person_obj);
// we already store this in Activity::fetch()
// XConfig::Set($url, 'system', 'actor_record', $person_obj);
$name = $person_obj['name'];
if (!$name) {
@@ -1814,12 +1838,12 @@ class Activity {
// 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)) {
if (!$has_zot_hubloc && 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)]);
if (!$zx) {
Master::Summon(['Gprobe', bin2hex($url)]);
}
}
@@ -1922,11 +1946,6 @@ class Activity {
}
}
$abook = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
dbesc($observer_hash),
intval($channel['channel_id'])
);
$content = self::get_content($act->obj);
if (!$content) {
@@ -2004,15 +2023,23 @@ class Activity {
}
if ($channel['channel_system']) {
if (!MessageFilter::evaluate($s, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) {
$incl = get_config('system','pubstream_incl');
$excl = get_config('system','pubstream_excl');
if(($incl || $excl) && !MessageFilter::evaluate($s, $incl, $excl)) {
logger('post is filtered');
return;
}
}
$abook = q("select * from abook where (abook_xchan = '%s' OR abook_xchan = '%s') and abook_channel = %d ",
dbesc($s['author_xchan']),
dbesc($s['owner_xchan']),
intval($channel['channel_id'])
);
if ($abook) {
if (!post_is_importable($s, $abook[0])) {
if (!post_is_importable($channel['channel_id'], $s, $abook)) {
logger('post is filtered');
return;
}
@@ -2221,6 +2248,21 @@ class Activity {
static function decode_note($act) {
$response_activity = false;
$s = [];
// These activities should have been handled separately in the Inbox module and should not be turned into posts
if (
in_array($act->type, ['Follow', 'Accept', 'Reject', 'Create', 'Update']) &&
is_array($act->obj) &&
array_key_exists('type', $act->obj) &&
($act->obj['type'] === 'Follow' || ActivityStreams::is_an_actor($act->obj['type']))
) {
return false;
}
// Within our family of projects, Follow/Unfollow of a thread is an internal activity which should not be transmitted,
// hence if we receive it - ignore or reject it.
// Unfollow is not defined by ActivityStreams, which prefers Undo->Follow.
@@ -2230,22 +2272,31 @@ class Activity {
return false;
}
$response_activity = false;
if (!isset($act->actor['id'])) {
logger('No actor!');
return false;
}
$s = [];
// ensure we store the original actor
self::actor_store($act->actor['id'], $act->actor);
$s['owner_xchan'] = $act->actor['id'];
$s['author_xchan'] = $act->actor['id'];
if (is_array($act->obj)) {
$content = self::get_content($act->obj);
}
$s['owner_xchan'] = $act->actor['id'];
$s['author_xchan'] = $act->actor['id'];
$s['mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj);
// ensure we store the original actor
self::actor_store($act->actor['id'], $act->actor);
if (!$s['mid']) {
return false;
}
// Friendica sends the diaspora guid in a nonstandard field via AP
// If no uuid is provided we will create an uuid v5 from the mid
$s['uuid'] = ((is_array($act->obj) && isset($act->obj['diaspora:guid'])) ? $act->obj['diaspora:guid'] : uuid_from_url($s['mid']));
$s['mid'] = $act->obj['id'];
$s['uuid'] = $act->obj['diaspora:guid'];
$s['parent_mid'] = $act->parent_id;
if (array_key_exists('published', $act->data)) {
@@ -2269,13 +2320,18 @@ class Activity {
$s['expires'] = datetime_convert('UTC', 'UTC', $act->obj['expires']);
}
if ($act->type === 'Invite' && is_array($act->obj) && array_key_exists('type', $act->obj) && $act->obj['type'] === 'Event') {
$s['mid'] = $s['parent_mid'] = $act->id;
}
if (ActivityStreams::is_response_activity($act->type)) {
$response_activity = true;
$s['mid'] = $act->id;
// $s['parent_mid'] = $act->obj['id'];
$s['uuid'] = $act->data['diaspora:guid'];
$s['uuid'] = ((is_array($act->data) && isset($act->data['diaspora:guid'])) ? $act->data['diaspora:guid'] : uuid_from_url($s['mid']));
$s['parent_mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj);
// over-ride the object timestamp with the activity
@@ -2288,8 +2344,8 @@ class Activity {
}
$obj_actor = ((isset($act->obj['actor'])) ? $act->obj['actor'] : $act->get_actor('attributedTo', $act->obj));
// ensure we store the original actor
// ensure we store the original actor
self::actor_store($obj_actor['id'], $obj_actor);
$mention = self::get_actor_bbmention($obj_actor['id']);
@@ -2318,13 +2374,41 @@ class Activity {
}
if ($act->type === 'Announce') {
$content['content'] = sprintf(t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, $act->obj['type']);
$s['author_xchan'] = $obj_actor['id'];
$s['mid'] = $act->obj['id'];
$s['parent_mid'] = $act->obj['id'];
}
if ($act->type === 'emojiReaction') {
$content['content'] = (($act->tgt && $act->tgt['type'] === 'Image') ? '[img=32x32]' . $act->tgt['url'] . '[/img]' : '&#x' . $act->tgt['name'] . ';');
}
}
$s['item_thread_top'] = 0;
$s['comment_policy'] = 'authenticated';
if ($s['mid'] === $s['parent_mid']) {
$s['item_thread_top'] = 1;
// it is a parent node - decode the comment policy info if present
if (isset($act->obj['commentPolicy'])) {
$until = strpos($act->obj['commentPolicy'], 'until=');
if ($until !== false) {
$s['comments_closed'] = datetime_convert('UTC', 'UTC', substr($act->obj['commentPolicy'], $until + 6));
if ($s['comments_closed'] < datetime_convert()) {
$s['nocomment'] = true;
}
}
$remainder = substr($act->obj['commentPolicy'], 0, (($until) ? $until : strlen($act->obj['commentPolicy'])));
if ($remainder) {
$s['comment_policy'] = $remainder;
}
if (!(isset($item['comment_policy']) && strlen($item['comment_policy']))) {
$s['comment_policy'] = 'contacts';
}
}
}
if (!array_key_exists('created', $s))
$s['created'] = datetime_convert();
@@ -2335,6 +2419,16 @@ class Activity {
$s['summary'] = self::bb_content($content, 'summary');
$s['body'] = ((self::bb_content($content, 'bbcode') && (!$response_activity)) ? self::bb_content($content, 'bbcode') : self::bb_content($content, 'content'));
if (isset($act->obj['quoteUrl'])) {
$quote_bbcode = self::get_quote_bbcode($act->obj['quoteUrl']);
if ($s['body']) {
$s['body'] .= "\r\n\r\n";
}
$s['body'] .= $quote_bbcode;
}
$s['verb'] = self::activity_decode_mapper($act->type);
// Mastodon does not provide update timestamps when updating poll tallies which means race conditions may occur here.
@@ -2351,6 +2445,12 @@ class Activity {
$s['obj_type'] = ACTIVITY_OBJ_COMMENT;
}
$s['obj'] = $act->obj;
if (is_array($s['obj']) && array_path_exists('actor/id', $s['obj'])) {
$s['obj']['actor'] = $s['obj']['actor']['id'];
}
/*
$eventptr = null;
if ($act->obj['type'] === 'Invite' && array_path_exists('object/type', $act->obj) && $act->obj['object']['type'] === 'Event') {
@@ -2371,19 +2471,19 @@ class Activity {
$s['obj']['asld'] = $eventptr;
$s['obj']['type'] = ACTIVITY_OBJ_EVENT;
$s['obj']['id'] = $eventptr['id'];
$s['obj']['title'] = $eventptr['name'];
$s['obj']['title'] = html2plain($eventptr['name']);
if (strpos($act->obj['startTime'], 'Z'))
$s['obj']['adjust'] = true;
else
$s['obj']['adjust'] = false;
$s['obj']['adjust'] = true;
$s['obj']['dtstart'] = datetime_convert('UTC', 'UTC', $eventptr['startTime']);
if ($act->obj['endTime'])
$s['obj']['dtend'] = datetime_convert('UTC', 'UTC', $eventptr['endTime']);
else
$s['obj']['nofinish'] = true;
$s['obj']['description'] = $eventptr['content'];
$s['obj']['description'] = html2bbcode($eventptr['content']);
if (array_path_exists('location/content', $eventptr))
$s['obj']['location'] = $eventptr['location']['content'];
@@ -2392,6 +2492,7 @@ class Activity {
else {
$s['obj'] = $act->obj;
}
*/
$generator = $act->get_property_obj('generator');
if ((!$generator) && (!$response_activity)) {
@@ -2431,7 +2532,7 @@ class Activity {
if (array_key_exists('type', $act->obj)) {
if ($act->obj['type'] === 'Note' && $s['attach']) {
$s['body'] .= self::bb_attach($s['attach'], $s['body']);
$s['body'] = self::bb_attach($s['attach'], $s['body']) . $s['body'];
}
if ($act->obj['type'] === 'Question' && in_array($act->type, ['Create', 'Update'])) {
@@ -2459,31 +2560,57 @@ class Activity {
'video/webm'
];
$mps = [];
$mps = [];
$poster = null;
$ptr = null;
// try to find a poster to display on the video element
if (array_key_exists('icon',$act->obj)) {
if (is_array($act->obj['icon'])) {
if (array_key_exists(0,$act->obj['icon'])) {
$ptr = $act->obj['icon'];
}
else {
$ptr = [ $act->obj['icon'] ];
}
}
if ($ptr) {
foreach ($ptr as $foo) {
if (is_array($foo) && array_key_exists('type',$foo) && $foo['type'] === 'Image' && is_string($foo['url'])) {
$poster = $foo['url'];
}
}
}
}
$tag = (($poster) ? '[video poster=&quot;' . $poster . '&quot;]' : '[video]' );
$ptr = null;
if (array_key_exists('url', $act->obj)) {
if (array_key_exists('url',$act->obj)) {
if (is_array($act->obj['url'])) {
if (array_key_exists(0, $act->obj['url'])) {
if (array_key_exists(0,$act->obj['url'])) {
$ptr = $act->obj['url'];
}
else {
$ptr = [$act->obj['url']];
$ptr = [ $act->obj['url'] ];
}
foreach ($ptr as $vurl) {
// peertube uses the non-standard element name 'mimeType' here
if (array_key_exists('mimeType', $vurl)) {
if (in_array($vurl['mimeType'], $vtypes)) {
if (!array_key_exists('width', $vurl)) {
$vurl['width'] = 0;
}
$mps[] = $vurl;
// handle peertube's weird url link tree if we find it here
// 0 => html link, 1 => application/x-mpegURL with 'tag' set to an array of actual media links
foreach ($ptr as $idex) {
if (is_array($idex) && array_key_exists('mediaType',$idex)) {
if ($idex['mediaType'] === 'application/x-mpegURL' && isset($idex['tag']) && is_array($idex['tag'])) {
$ptr = $idex['tag'];
break;
}
}
elseif (array_key_exists('mediaType', $vurl)) {
}
foreach ($ptr as $vurl) {
if (array_key_exists('mediaType',$vurl)) {
if (in_array($vurl['mediaType'], $vtypes)) {
if (!array_key_exists('width', $vurl)) {
$vurl['width'] = 0;
if (! array_key_exists('height',$vurl)) {
$vurl['height'] = 0;
}
$mps[] = $vurl;
}
@@ -2491,17 +2618,18 @@ class Activity {
}
}
if ($mps) {
usort($mps, [__CLASS__, 'vid_sort']);
usort($mps,[ '\Zotlabs\Lib\Activity', 'vid_sort' ]);
foreach ($mps as $m) {
if (intval($m['width']) < 500 && self::media_not_in_body($m['href'], $s['body'])) {
$s['body'] .= "\n\n" . '[video]' . $m['href'] . '[/video]';
if (intval($m['height']) < 500 && Activity::media_not_in_body($m['href'],$s['body'])) {
$s['body'] = $tag . $m['href'] . '[/video]' . "\n\n" . $s['body'];
break;
}
}
}
elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'], $s['body'])) {
$s['body'] .= "\n\n" . '[video]' . $act->obj['url'] . '[/video]';
elseif (is_string($act->obj['url']) && Activity::media_not_in_body($act->obj['url'],$s['body'])) {
$s['body'] = $tag . $act->obj['url'] . '[/video]' . "\n\n" . $s['body'];
}
}
}
@@ -2525,13 +2653,13 @@ class Activity {
}
foreach ($ptr as $vurl) {
if (in_array($vurl['mediaType'], $atypes) && self::media_not_in_body($vurl['href'], $s['body'])) {
$s['body'] .= "\n\n" . '[audio]' . $vurl['href'] . '[/audio]';
$s['body'] = '[audio]' . $vurl['href'] . '[/audio]' . "\n\n" . $s['body'];
break;
}
}
}
elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'], $s['body'])) {
$s['body'] .= "\n\n" . '[audio]' . $act->obj['url'] . '[/audio]';
$s['body'] = '[audio]' . $act->obj['url'] . '[/audio]' . "\n\n" . $s['body'];
}
}
@@ -2606,7 +2734,6 @@ class Activity {
}
}
if (in_array($act->obj['type'], ['Note', 'Article', 'Page'])) {
$ptr = null;
@@ -2744,12 +2871,6 @@ class Activity {
return;
}*/
// TODO: this his handled in pubcrawl atm.
// very unpleasant and imperfect way of determining a Mastodon DM
/*if ($act->raw_recips && array_key_exists('to',$act->raw_recips) && is_array($act->raw_recips['to']) && count($act->raw_recips['to']) === 1 && $act->raw_recips['to'][0] === channel_url($channel) && ! $act->raw_recips['cc']) {
$item['item_private'] = 2;
}*/
if ($item['parent_mid'] && $item['parent_mid'] !== $item['mid']) {
$is_child_node = true;
}
@@ -2914,19 +3035,23 @@ class Activity {
return;
if ($channel['channel_system']) {
if (!MessageFilter::evaluate($item, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) {
$incl = get_config('system','pubstream_incl');
$excl = get_config('system','pubstream_excl');
if(($incl || $excl) && !MessageFilter::evaluate($item, $incl, $excl)) {
logger('post is filtered');
return;
}
}
$abook = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
dbesc($observer_hash),
$abook = q("select * from abook where ( abook_xchan = '%s' OR abook_xchan = '%s') and abook_channel = %d ",
dbesc($item['author_xchan']),
dbesc($item['owner_xchan']),
intval($channel['channel_id'])
);
if ($abook) {
if (!post_is_importable($item, $abook[0])) {
if (!post_is_importable($channel['channel_id'], $item, $abook)) {
logger('post is filtered');
return;
}
@@ -2996,6 +3121,19 @@ class Activity {
$item['thr_parent'] = $parent[0]['parent_mid'];
}
$item['parent_mid'] = $parent[0]['parent_mid'];
//$item['item_private'] = $parent[0]['item_private'];
}
// An ugly and imperfect way to recognise a mastodon direct message
if (
$item['item_private'] === 1 &&
!isset($act->raw_recips['cc']) &&
is_array($act->raw_recips['to']) &&
in_array(channel_url($channel), $act->raw_recips['to']) &&
!in_array($act->actor['followers'], $act->raw_recips['to'])
) {
$item['item_private'] = 2;
}
// TODO: not implemented
@@ -3006,6 +3144,12 @@ class Activity {
intval($item['uid'])
);
if ($r) {
// If we already have the item, dismiss its announce
if ($act->type === 'Announce') {
return;
}
if ($item['edited'] > $r[0]['edited']) {
$item['id'] = $r[0]['id'];
$x = item_store_update($item);
@@ -3022,12 +3166,12 @@ class Activity {
logger('topfetch', LOGGER_DEBUG);
// if the thread owner is a connnection, we will already receive any additional comments to their posts
// but if they are not we can try to fetch others in the background
$x = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
$connected = 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($parent[0]['owner_xchan'])
);
if (!$x) {
if (!$connected) {
// determine if the top-level post provides a replies collection
if ($parent[0]['obj']) {
$parent[0]['obj'] = json_decode($parent[0]['obj'], true);
@@ -3279,6 +3423,7 @@ class Activity {
}
/* this is deprecated and not used anymore
static function announce_note($channel, $observer_hash, $act) {
$s = [];
@@ -3409,6 +3554,7 @@ class Activity {
}
}
*/
static function like_note($channel, $observer_hash, $act) {
@@ -3629,7 +3775,49 @@ class Activity {
$event['nofinish'] = true;
}
}
/*
$eventptr = null;
if ($act->obj['type'] === 'Invite' && array_path_exists('object/type', $act->obj) && $act->obj['object']['type'] === 'Event') {
$eventptr = $act->obj['object'];
$s['mid'] = $s['parent_mid'] = $act->obj['id'];
}
if ($act->obj['type'] === 'Event') {
if ($act->type === 'Invite') {
$s['mid'] = $s['parent_mid'] = $act->id;
}
$eventptr = $act->obj;
}
if ($eventptr) {
$s['obj'] = [];
$s['obj']['asld'] = $eventptr;
$s['obj']['type'] = ACTIVITY_OBJ_EVENT;
$s['obj']['id'] = $eventptr['id'];
$s['obj']['title'] = html2plain($eventptr['name']);
if (strpos($act->obj['startTime'], 'Z'))
$s['obj']['adjust'] = true;
else
$s['obj']['adjust'] = true;
$s['obj']['dtstart'] = datetime_convert('UTC', 'UTC', $eventptr['startTime']);
if ($act->obj['endTime'])
$s['obj']['dtend'] = datetime_convert('UTC', 'UTC', $eventptr['endTime']);
else
$s['obj']['nofinish'] = true;
$s['obj']['description'] = html2bbcode($eventptr['content']);
if (array_path_exists('location/content', $eventptr))
$s['obj']['location'] = $eventptr['location']['content'];
}
else {
$s['obj'] = $act->obj;
}
*/
foreach (['name', 'summary', 'content'] as $a) {
if (($x = self::get_textfield($act, $a)) !== false) {
$content[$a] = $x;
@@ -3786,7 +3974,7 @@ class Activity {
return $ret;
}
foreach ($tag as $t) {
foreach ($actor['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']))
@@ -3797,4 +3985,32 @@ class Activity {
return $ret;
}
static function get_quote_bbcode($url) {
$ret = '';
$a = self::fetch($url);
if ($a) {
$act = new ActivityStreams($a);
if ($act->is_valid()) {
$content = self::get_content($act->obj);
$ret .= "[share author='" . urlencode($act->actor['name']) .
"' profile='" . $act->actor['id'] .
"' avatar='" . $act->actor['icon']['url'] .
"' link='" . $act->obj['id'] .
"' auth='" . ((is_matrix_url($act->actor['id'])) ? 'true' : 'false') .
"' posted='" . $act->obj['published'] .
"' message_id='" . $act->obj['id'] .
"']";
$ret .= self::bb_content($content, 'content');
$ret .= '[/share]';
}
}
return $ret;
}
}

View File

@@ -422,15 +422,19 @@ class ActivityStreams {
static function get_accept_header_string($channel = null) {
$ret = '';
$hookdata = [];
if ($channel)
$hookdata['channel'] = $channel;
$hookdata['data'] = 'application/x-zot-activity+json';
$hookdata['data'] = ['application/x-zot-activity+json'];
call_hooks('get_accept_header_string', $hookdata);
return $hookdata['data'];
$ret = implode(', ', $hookdata['data']);
return $ret;
}

View File

@@ -87,6 +87,10 @@ class Crypto {
return false;
}
if (!$alg) {
$alg = 'sha256';
}
try {
$verify = openssl_verify($data, $sig, $key, $alg);
} catch (Exception $e) {

View File

@@ -845,6 +845,10 @@ class Enotify {
// convert this logic into a json array just like the system notifications
$who = (($item['verb'] === ACTIVITY_SHARE) ? 'owner' : 'author');
$body = html2plain(bbcode($item['body'], ['drop_media']), 75, true);
if ($body) {
$body = htmlentities($body, ENT_QUOTES, 'UTF-8', false);
}
$x = array(
'notify_link' => $item['llink'],
@@ -858,7 +862,7 @@ class Enotify {
//'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? gen_link_id($item['thr_parent']) : gen_link_id($item['mid'])),
'thread_top' => (($item['item_thread_top']) ? true : false),
'message' => bbcode(escape_tags($itemem_text)),
'body' => htmlentities(html2plain(bbcode($item['body'], ['drop_media', true]), 75, true), ENT_QUOTES, 'UTF-8', false),
'body' => $body,
// these are for the superblock addon
'hash' => $item[$who]['xchan_hash'],
'uid' => $item['uid'],

View File

@@ -75,22 +75,23 @@ class LDSignatures {
}
static function hash($obj) {
return hash('sha256',self::normalise($obj));
return hash('sha256', self::normalise($obj));
}
static function normalise($data) {
$ret = '';
if(is_string($data)) {
$data = json_decode($data);
}
if(! is_object($data))
return '';
return $ret;
jsonld_set_document_loader('jsonld_document_loader');
try {
$d = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]);
$ret = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]);
}
catch (\Exception $e) {
// Don't log the exception - this can exhaust memory
@@ -98,7 +99,7 @@ class LDSignatures {
logger('normalise error: ' . print_r($data,true));
}
return $d;
return $ret;
}
static function salmon_sign($data,$channel) {

View File

@@ -676,6 +676,10 @@ class Libzot {
logger('import_xchan: ' . $xchan_hash, LOGGER_DEBUG);
if (isset($arr['signing_algorithm']) && $arr['signing_algorithm']) {
set_xconfig($xchan_hash, 'system', 'signing_algorithm', $arr['signing_algorithm']);
}
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($xchan_hash)
);
@@ -1003,7 +1007,7 @@ class Libzot {
$x = Crypto::unencapsulate($x, get_config('system', 'prvkey'));
if (!is_array($x)) {
if ($x && !is_array($x)) {
$x = json_decode($x, true);
}
@@ -1227,6 +1231,16 @@ class Libzot {
dbesc($AS->actor['id'])
);
if (! $r) {
// Author is unknown to this site. Perform channel discovery and try again.
$z = discover_by_webbie($AS->actor['id']);
if ($z) {
$r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s'",
dbesc($AS->actor['id'])
);
}
}
if ($r) {
$r = self::zot_record_preferred($r);
$arr['author_xchan'] = $r['hubloc_hash'];
@@ -1280,22 +1294,12 @@ class Libzot {
if ($AS->data['hubloc']) {
$arr['item_verified'] = true;
if (!array_key_exists('comment_policy', $arr)) {
// set comment policy depending on source hub. Unknown or osada is ActivityPub.
// Anything else we'll say is zot - which could have a range of project names
$s = q("select site_project from site where site_url = '%s' limit 1",
dbesc($r[0]['hubloc_url'])
);
if ((!$s) || (in_array($s[0]['site_project'], ['', 'osada']))) {
$arr['comment_policy'] = 'authenticated';
}
else {
$arr['comment_policy'] = 'contacts';
}
}
}
if (!array_key_exists('comment_policy', $arr)) {
$arr['comment_policy'] = 'authenticated';
}
if ($AS->meta['signed_data']) {
IConfig::Set($arr, 'activitypub', 'signed_data', $AS->meta['signed_data'], false);
}
@@ -1568,7 +1572,11 @@ class Libzot {
$local_public = false;
continue;
}
if (!MessageFilter::evaluate($arr, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) {
$incl = get_config('system','pubstream_incl');
$excl = get_config('system','pubstream_excl');
if(($incl || $excl) && !MessageFilter::evaluate($arr, $incl, $excl)) {
$local_public = false;
continue;
}
@@ -1743,11 +1751,13 @@ class Libzot {
}
}
$ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'",
// This is used to fetch allow/deny rules if either the sender
// or owner is a connection. post_is_importable() evaluates all of them
$abook = q("select * from abook where abook_channel = %d and ( abook_xchan = '%s' OR abook_xchan = '%s' )",
intval($channel['channel_id']),
dbesc($arr['owner_xchan'])
dbesc($arr['owner_xchan']),
dbesc($arr['author_xchan'])
);
$abook = (($ab) ? $ab[0] : null);
if (intval($arr['item_deleted'])) {
@@ -1798,17 +1808,18 @@ class Libzot {
elseif ($arr['edited'] > $r[0]['edited']) {
$arr['id'] = $r[0]['id'];
$arr['uid'] = $channel['channel_id'];
if (($arr['mid'] == $arr['parent_mid']) && (!post_is_importable($arr, $abook))) {
$DR->update('update ignored');
$result[] = $DR->get();
}
else {
$item_result = self::update_imported_item($sender, $arr, $r[0], $channel['channel_id'], $tag_delivery);
$DR->update('updated');
$result[] = $DR->get();
if (!$relay)
add_source_route($item_id, $sender);
}
if (post_is_importable($channel['channel_id'], $arr, $abook)) {
$item_result = self::update_imported_item($sender, $arr, $r[0], $channel['channel_id'], $tag_delivery);
$DR->update('updated');
$result[] = $DR->get();
if (!$relay) {
add_source_route($item_id, $sender);
}
} else {
$DR->update('update ignored');
$result[] = $DR->get();
}
}
else {
$DR->update('update ignored');
@@ -1838,20 +1849,29 @@ class Libzot {
$item_id = 0;
if (($arr['mid'] == $arr['parent_mid']) && (!post_is_importable($arr, $abook))) {
$DR->update('post ignored');
$result[] = $DR->get();
$maxlen = get_max_import_size();
if ($maxlen && mb_strlen($arr['body']) > $maxlen) {
$arr['body'] = mb_substr($arr['body'], 0, $maxlen, 'UTF-8');
logger('message length exceeds max_import_size: truncated');
}
else {
if ($maxlen && mb_strlen($arr['summary']) > $maxlen) {
$arr['summary'] = mb_substr($arr['summary'], 0, $maxlen, 'UTF-8');
logger('message summary length exceeds max_import_size: truncated');
}
if (post_is_importable($arr['uid'], $arr, $abook)) {
$item_result = item_store($arr);
if ($item_result['success']) {
$item_id = $item_result['item_id'];
$parr = [
$parr = [
'item_id' => $item_id,
'item' => $arr,
'sender' => $sender,
'item' => $arr,
'sender' => $sender,
'channel' => $channel
];
/**
* @hooks activity_received
* Called when an activity (post, comment, like, etc.) has been received from a zot source.
@@ -1861,13 +1881,19 @@ class Libzot {
* * \e array \b channel
*/
call_hooks('activity_received', $parr);
// don't add a source route if it's a relay or later recipients will get a route mismatch
if (!$relay)
if (!$relay) {
add_source_route($item_id, $sender);
}
}
$DR->update(($item_id) ? 'posted' : 'storage failed: ' . $item_result['message']);
$result[] = $DR->get();
} else {
$DR->update('post ignored');
$result[] = $DR->get();
}
}
// preserve conversations with which you are involved from expiration
@@ -2845,6 +2871,7 @@ class Libzot {
];
$ret['public_key'] = $e['xchan_pubkey'];
$ret['signing_algorithm'] = 'rsa-sha256';
$ret['username'] = $e['channel_address'];
$ret['name'] = $e['xchan_name'];
$ret['name_updated'] = $e['xchan_name_date'];

View File

@@ -2,85 +2,104 @@
namespace Zotlabs\Lib;
require_once('include/html2plain.php');
class MessageFilter {
public static function evaluate($item, $incl, $excl) {
static public function evaluate($item,$incl,$excl) {
require_once('include/html2plain.php');
$text = prepare_text($item['body'],$item['mimetype']);
$text = prepare_text($item['body'],((isset($item['mimetype'])) ? $item['mimetype'] : 'text/x-multicode'));
$text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text);
$lang = null;
if((strpos($incl,'lang=') !== false) || (strpos($excl,'lang=') !== false) || (strpos($incl,'lang!=') !== false) || (strpos($excl,'lang!=') !== false)) {
if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) {
$lang = detect_language($text);
}
$tags = ((is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
$tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
// exclude always has priority
$exclude = (($excl) ? explode("\n",$excl) : null);
$exclude = (($excl) ? explode("\n", $excl) : null);
if($exclude) {
foreach($exclude as $word) {
if ($exclude) {
foreach ($exclude as $word) {
$word = trim($word);
if(! $word)
if (! $word) {
continue;
if(substr($word,0,1) === '#' && $tags) {
foreach($tags as $t)
if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
return false;
}
elseif(substr($word,0,1) === '$' && $tags) {
foreach($tags as $t)
if(($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
if (substr($word, 0, 1) === '#' && $tags) {
foreach ($tags as $t) {
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return false;
}
}
} elseif (substr($word, 0, 1) === '$' && $tags) {
foreach ($tags as $t) {
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return false;
}
}
} elseif (substr($word, 0, 2) === '?+') {
if (self::test_condition(substr($word, 2), $item['obj'])) {
return false;
}
} elseif (substr($word, 0, 1) === '?') {
if (self::test_condition(substr($word, 1), $item)) {
return false;
}
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
return false;
} elseif ((strpos($word, 'lang=') === 0) && ($lang) && (strcasecmp($lang, trim(substr($word, 5))) == 0)) {
return false;
} elseif ((strpos($word, 'lang!=') === 0) && ($lang) && (strcasecmp($lang, trim(substr($word, 6))) != 0)) {
return false;
} elseif (stristr($text, $word) !== false) {
return false;
}
elseif((strpos($word,'/') === 0) && preg_match($word,$text))
return false;
elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0))
return false;
elseif((strpos($word,'lang!=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,6))) != 0))
return false;
elseif(stristr($text,$word) !== false)
return false;
}
}
$include = (($incl) ? explode("\n",$incl) : null);
$include = (($incl) ? explode("\n", $incl) : null);
if($include) {
foreach($include as $word) {
if ($include) {
foreach ($include as $word) {
$word = trim($word);
if(! $word)
if (! $word) {
continue;
if(substr($word,0,1) === '#' && $tags) {
foreach($tags as $t)
if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
return true;
}
elseif(substr($word,0,1) === '$' && $tags) {
foreach($tags as $t)
if(($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
if (substr($word, 0, 1) === '#' && $tags) {
foreach ($tags as $t) {
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return true;
}
}
} elseif (substr($word, 0, 1) === '$' && $tags) {
foreach ($tags as $t) {
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return true;
}
}
} elseif (substr($word, 0, 2) === '?+') {
if (self::test_condition(substr($word, 2), $item['obj'])) {
return true;
}
} elseif (substr($word, 0, 1) === '?') {
if (self::test_condition(substr($word, 1), $item)) {
return true;
}
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
return true;
} elseif ((strpos($word, 'lang=') === 0) && ($lang) && (strcasecmp($lang, trim(substr($word, 5))) == 0)) {
return true;
} elseif ((strpos($word, 'lang!=') === 0) && ($lang) && (strcasecmp($lang, trim(substr($word, 6))) != 0)) {
return true;
} elseif (stristr($text, $word) !== false) {
return true;
}
elseif((strpos($word,'/') === 0) && preg_match($word,$text))
return true;
elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0))
return true;
elseif((strpos($word,'lang!=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,6))) != 0))
return true;
elseif(stristr($text,$word) !== false)
return true;
}
}
else {
} else {
return true;
}
@@ -88,4 +107,113 @@ class MessageFilter {
}
/**
* @brief Test for Conditional Execution conditions. Shamelessly ripped off from Code/Render/Comanche
*
* This is extensible. The first version of variable testing supports tests of the forms:
*
* - ?foo ~= baz which will check if item.foo contains the string 'baz';
* - ?foo == baz which will check if item.foo is the string 'baz';
* - ?foo != baz which will check if item.foo is not the string 'baz';
* - ?foo >= 3 which will check if item.foo is greater than or equal to 3;
* - ?foo > 3 which will check if item.foo is greater than 3;
* - ?foo <= 3 which will check if item.foo is less than or equal to 3;
* - ?foo < 3 which will check if item.foo is less than 3;
*
* - ?foo {} baz which will check if 'baz' is an array element in item.foo
* - ?foo {*} baz which will check if 'baz' is an array key in item.foo
* - ?foo which will check for a return of a true condition for item.foo;
*
* The values 0, '', an empty array, and an unset value will all evaluate to false.
*
* @param string $s
* @param array $item
* @return bool
*/
public static function test_condition($s,$item) {
if (preg_match('/(.*?)\s\~\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (stripos($x, trim($matches[2])) !== false) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\=\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x == trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\!\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x != trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\>\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x >= trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\<\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x <= trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x > trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x < trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/[\$](.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (is_array($x) && in_array(trim($matches[2]), $x)) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\{\*\}\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (is_array($x) && array_key_exists(trim($matches[2]), $x)) {
return true;
}
return false;
}
if (preg_match('/(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x) {
return true;
}
return false;
}
return false;
}
}

View File

@@ -254,21 +254,21 @@ class Directory extends Controller {
$connect_link = '';
$location = '';
if(strlen($rr['locale']))
if(isset($rr['locale']))
$location .= $rr['locale'];
if(strlen($rr['region'])) {
if(strlen($rr['locale']))
if(isset($rr['region'])) {
if($location)
$location .= ', ';
$location .= $rr['region'];
}
if(strlen($rr['country'])) {
if(strlen($location))
if(isset($rr['country'])) {
if($location)
$location .= ', ';
$location .= $rr['country'];
}
$age = '';
if(strlen($rr['birthday'])) {
if(isset($rr['birthday'])) {
if(($years = age($rr['birthday'],'UTC','')) > 0)
$age = $years;
}

View File

@@ -27,6 +27,8 @@ class Hq extends \Zotlabs\Web\Controller {
return;
}
$item_hash = '';
if(argc() > 1 && argv(1) !== 'load') {
$item_hash = unpack_link_id(argv(1));
}
@@ -97,7 +99,7 @@ class Hq extends \Zotlabs\Web\Controller {
'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''),
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
'permissions' => $channel_acl,
'bang' => '',
@@ -112,6 +114,7 @@ class Hq extends \Zotlabs\Web\Controller {
'reset' => t('Reset form')
];
$a = '';
$o = status_editor($a, $x, true);
}

View File

@@ -695,6 +695,7 @@ class Item extends Controller {
$expires = $orig_post['expires'];
$comments_closed = $orig_post['comments_closed'];
$mid = $orig_post['mid'];
$thr_parent = $orig_post['thr_parent'];
$parent_mid = $orig_post['parent_mid'];
$plink = $orig_post['plink'];
}
@@ -1092,7 +1093,7 @@ class Item extends Controller {
$datarray['created'] = $created;
$datarray['edited'] = (($orig_post) ? datetime_convert() : $created);
$datarray['expires'] = $expires;
$datarray['comments_closed'] = $comments_closed;
$datarray['comments_closed'] = (($nocomment) ? $created : $comments_closed);
$datarray['commented'] = (($orig_post) ? datetime_convert() : $created);
$datarray['received'] = (($orig_post) ? datetime_convert() : $created);
$datarray['changed'] = (($orig_post) ? datetime_convert() : $created);

View File

@@ -272,15 +272,17 @@ class Network extends \Zotlabs\Web\Controller {
$likes_sql = " AND verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
// This is for nouveau view public forum cid queries (if a forum notification is clicked)
$p = q("SELECT oid AS parent FROM term WHERE uid = %d AND ttype = %d AND term = '%s'",
intval(local_channel()),
intval(TERM_FORUM),
dbesc($cid_r[0]['xchan_name'])
);
//$p = q("SELECT oid AS parent FROM term WHERE uid = %d AND ttype = %d AND term = '%s'",
//intval(local_channel()),
//intval(TERM_FORUM),
//dbesc($cid_r[0]['xchan_name'])
//);
$p_str = ids_to_querystr($p, 'parent');
if($p_str)
$p_sql = " OR item.parent IN ( $p_str ) ";
//$p_str = ids_to_querystr($p, 'parent');
$p_sql = '';
//if($p_str)
//$p_sql = " OR item.parent IN ( $p_str ) ";
$sql_extra = " AND ( owner_xchan = '" . protect_sprintf(dbesc($cid_r[0]['abook_xchan'])) . "' OR owner_xchan = '" . protect_sprintf(dbesc($cid_r[0]['abook_xchan'])) . "' $p_sql ) AND item_unseen = 1 $likes_sql ";
}

View File

@@ -0,0 +1,553 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Render\Comanche;
use Zotlabs\Lib\Libsync;
class Pdledit_gui extends Controller {
function post() {
if (!local_channel()) {
return;
}
if (!$_REQUEST['module']) {
return;
}
$module = $_REQUEST['module'];
$ret = [
'success' => false,
'module' => $module
];
if ($_REQUEST['reset']) {
del_pconfig(local_channel(), 'system', 'mod_' . $module . '.pdl');
Libsync::build_sync_packet();
$ret['success'] = true;
json_return_and_die($ret);
}
if ($_REQUEST['save']) {
if (!$_REQUEST['data']) {
return $ret;
}
$data = json_decode($_REQUEST['data'],true);
$stored_pdl_result = self::get_pdl($module);
$pdl = $stored_pdl_result['pdl'];
foreach ($data as $region => $entries) {
$region_pdl = '';
foreach ($entries as $entry) {
$region_pdl .= base64_decode($entry) . "\r\n";
}
$pdl = preg_replace('/\[region=' . $region . '\](.*?)\[\/region\]/ism', '[region=' . $region . ']' . "\r\n" . $region_pdl . "\r\n" . '[/region]', $pdl);
}
set_pconfig(local_channel(), 'system', 'mod_' . $module . '.pdl', escape_tags($pdl));
Libsync::build_sync_packet();
$ret['success'] = true;
json_return_and_die($ret);
}
if ($_REQUEST['save_src']) {
set_pconfig(local_channel(), 'system', 'mod_' . $module . '.pdl', escape_tags($_REQUEST['src']));
Libsync::build_sync_packet();
$ret['success'] = true;
json_return_and_die($ret);
}
if ($_REQUEST['save_template']) {
if (!$_REQUEST['data']) {
return $ret;
}
$template = $_REQUEST['data'][0]['value'];
$pdl_result = self::get_pdl($module);
$stored_template = self::get_template($pdl_result['pdl']);
if ($template === $stored_template) {
$ret['success'] = true;
return $ret;
}
$cnt = preg_match("/\[template\](.*?)\[\/template\]/ism", $pdl_result['pdl'], $matches);
if ($cnt) {
$pdl = str_replace('[template]' . $stored_template . '[/template]', '[template]' . $template . '[/template]', $pdl_result['pdl']);
}
else {
$pdl = '[template]' . $template . '[/template]' . "\r\n";
$pdl .= $pdl_result['pdl'];
}
set_pconfig(local_channel(), 'system', 'mod_' . $module . '.pdl', escape_tags($pdl));
Libsync::build_sync_packet();
$ret['success'] = true;
json_return_and_die($ret);
}
}
function get() {
if(! local_channel()) {
return EMPTY_STR;
}
$module = argv(1);
if (!$module) {
goaway(z_root() . '/pdledit_gui/hq');
}
$pdl_result = self::get_pdl($module);
$pdl = $pdl_result['pdl'];
$modified = $pdl_result['modified'];
if(!$pdl) {
return t('Layout not found');
}
$template = self::get_template($pdl);
$template_info = self::get_template_info($template);
if(empty($template_info['contentregion'])) {
return t('This template does not support pdledi_gui (no content regions defined)');
}
App::$page['template'] = $template;
$regions = self::get_regions($pdl);
foreach ($regions as $k => $v) {
$region_str = '';
if (is_array($v)) {
ksort($v);
foreach ($v as $entry) {
// Get the info from the file and replace entry if we get anything useful
$widget_info = get_widget_info($entry['name']);
$entry['name'] = (($widget_info['name']) ? $widget_info['name'] : $entry['name']);
$entry['desc'] = (($widget_info['description']) ? $widget_info['description'] : $entry['desc']);
$region_str .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [
'$entry' => $entry
]);
}
}
App::$layout['region_' . $k] = $region_str;
}
$templates = self::get_templates();
$templates_html = replace_macros(get_markup_template('pdledit_gui_templates.tpl'), [
'$templates' => $templates,
'$active' => $template
]);
$items_html = '';
//$items_html .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [
//'$entry' => [
//'type' => 'content',
//'name' => t('Main page content'),
//'src' => base64_encode('$content')
//],
//'$disable_controls' => true
//]);
foreach (self::get_widgets($module) as $entry) {
$items_html .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [
'$entry' => $entry,
'$disable_controls' => true
]);
}
foreach (self::get_menus() as $entry) {
$items_html .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [
'$entry' => $entry,
'$disable_controls' => true
]);
}
foreach (self::get_blocks() as $entry) {
$items_html .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [
'$entry' => $entry,
'$disable_controls' => true
]);
}
App::$layout['region_content'] .= replace_macros(get_markup_template('pdledit_gui.tpl'), [
'$content_regions' => $template_info['contentregion'],
'$page_src' => base64_encode($pdl),
'$templates' => base64_encode($templates_html),
'$modules' => base64_encode(self::get_modules()),
'$items' => base64_encode($items_html),
'$module_modified' => $modified,
'$module' => $module
]);
}
function get_templates() {
$ret = [];
$files = glob('view/php/*.php');
if($files) {
foreach($files as $f) {
$name = basename($f, '.php');
$x = get_template_info($name);
if(!empty($x['contentregion'])) {
$ret[] = [
'name' => $name,
'desc' => $x['description']
];
}
}
}
return $ret;
}
function get_modules() {
$ret = '';
$files = glob('Zotlabs/Module/*.php');
if($files) {
foreach($files as $f) {
$name = lcfirst(basename($f,'.php'));
if ($name === 'admin' && !is_site_admin()) {
continue;
}
$x = theme_include('mod_' . $name . '.pdl');
if($x) {
$ret .= '<div class="mb-2"><a href="pdledit_gui/' . $name . '">' . $name . '</a></div>';
}
}
}
return $ret;
}
function get_widgets($module) {
$ret = [];
$checkpaths = [
'Zotlabs/Widget/*.php'
];
foreach ($checkpaths as $path) {
$files = glob($path);
if($files) {
foreach($files as $f) {
$name = lcfirst(basename($f, '.php'));
$widget_info = get_widget_info($name);
if ($widget_info['requires'] && strpos($widget_info['requires'], 'admin') !== false && !is_site_admin()) {
continue;
}
if ($widget_info['requires'] && strpos($widget_info['requires'], $module) === false) {
continue;
}
$ret[] = [
'type' => 'widget',
'name' => $widget_info['name'] ?? $name,
'desc' => $widget_info['description'] ?? '',
'src' => base64_encode('[widget=' . $name . '][/widget]')
];
}
}
}
return $ret;
}
function get_menus() {
$ret = [];
$r = q("select * from menu where menu_channel_id = %d and menu_flags = 0",
intval(local_channel())
);
foreach ($r as $rr) {
$name = $rr['menu_name'];
$desc = $rr['menu_desc'];
$ret[] = [
'type' => 'menu',
'name' => $name,
'desc' => $desc,
'src' => base64_encode('[menu]' . $name . '[/menu]')
];
}
return $ret;
}
function get_blocks() {
$ret = [];
$r = q("select v, title, summary from item join iconfig on iconfig.iid = item.id and item.uid = %d
and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK'",
intval(local_channel())
);
foreach ($r as $rr) {
$name = $rr['v'];
$desc = (($rr['title']) ? $rr['title'] : $rr['summary']);
$ret[] = [
'type' => 'block',
'name' => $name,
'desc' => $desc,
'src' => base64_encode('[block]' . $name . '[/block]')
];
}
return $ret;
}
function get_template($pdl) {
$ret = 'default';
$cnt = preg_match("/\[template\](.*?)\[\/template\]/ism", $pdl, $matches);
if($cnt && isset($matches[1])) {
$ret = trim($matches[1]);
}
return $ret;
}
function get_regions($pdl) {
$ret = [];
$supported_regions = ['aside', 'content', 'right_aside'];
$cnt = preg_match_all("/\[region=(.*?)\](.*?)\[\/region\]/ism", $pdl, $matches, PREG_SET_ORDER);
if($cnt) {
foreach($matches as $mtch) {
if (!in_array($mtch[1], $supported_regions)) {
continue;
}
$ret[$mtch[1]] = self::parse_region($mtch[2]);
}
}
return $ret;
}
function parse_region($pdl) {
$ret = [];
$cnt = preg_match_all('/\$content\b/ism', $pdl, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
if($cnt) {
foreach($matches as $mtch) {
$offset = intval($mtch[0][1]);
$name = trim($mtch[0][0]);
//$src = base64url_encode(preg_replace(['/\s*\[/', '/\]\s*/'], ['[', ']'], $mtch[0][0]));
$src = base64_encode($mtch[0][0]);
$ret[$offset] = [
'type' => 'content',
'name' => t('Main page content'),
'desc' => t('The main page content can not be edited!'),
'src' => $src
];
}
}
$cnt = preg_match_all("/\[menu\](.*?)\[\/menu\]/ism", $pdl, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
if($cnt) {
foreach($matches as $mtch) {
$offset = intval($mtch[1][1]);
$name = trim($mtch[1][0]);
//$src = base64url_encode(preg_replace(['/\s*\[/', '/\]\s*/'], ['[', ']'], $mtch[0][0]));
$src = base64_encode($mtch[0][0]);
$ret[$offset] = [
'type' => 'menu',
'name' => $name,
'desc' => '',
'src' => $src
];
}
}
// menu class e.g. [menu=horizontal]my_menu[/menu] or [menu=tabbed]my_menu[/menu]
// allows different menu renderings to be applied
//$cnt = preg_match_all("/\[menu=(.*?)\](.*?)\[\/menu\]/ism", $s, $matches, PREG_SET_ORDER);
//if($cnt) {
//foreach($matches as $mtch) {
//$s = str_replace($mtch[0],$this->menu(trim($mtch[2]),$mtch[1]),$s);
//}
//}
$cnt = preg_match_all("/\[block\](.*?)\[\/block\]/ism", $pdl, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
if($cnt) {
foreach($matches as $mtch) {
$offset = intval($mtch[1][1]);
$name = trim($mtch[1][0]);
//$src = base64url_encode(preg_replace(['/\s*\[/', '/\]\s*/'], ['[', ']'], $mtch[0][0]));
$src = base64_encode($mtch[0][0]);
$ret[$offset] = [
'type' => 'block',
'name' => $name,
'desc' => '',
'src' => $src
];
}
}
//$cnt = preg_match_all("/\[block=(.*?)\](.*?)\[\/block\]/ism", $s, $matches, PREG_SET_ORDER);
//if($cnt) {
//foreach($matches as $mtch) {
//$s = str_replace($mtch[0],$this->block(trim($mtch[2]),trim($mtch[1])),$s);
//}
//}
//$cnt = preg_match_all("/\[js\](.*?)\[\/js\]/ism", $s, $matches, PREG_SET_ORDER);
//if($cnt) {
//foreach($matches as $mtch) {
//$s = str_replace($mtch[0],$this->js(trim($mtch[1])),$s);
//}
//}
//$cnt = preg_match_all("/\[css\](.*?)\[\/css\]/ism", $s, $matches, PREG_SET_ORDER);
//if($cnt) {
//foreach($matches as $mtch) {
//$s = str_replace($mtch[0],$this->css(trim($mtch[1])),$s);
//}
//}
// need to modify this to accept parameters
$cnt = preg_match_all("/\[widget=(.*?)\](.*?)\[\/widget\]/ism", $pdl, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
if($cnt) {
foreach($matches as $mtch) {
$offset = intval($mtch[1][1]);
$name = trim($mtch[1][0]);
//$src = base64url_encode(preg_replace(['/\s*\[/', '/\]\s*/'], ['[', ']'], $mtch[0][0]));
$src = base64_encode($mtch[0][0]);
$ret[$offset] = [
'type' => 'widget',
'name' => $name,
'desc' => '',
'src' => $src
];
}
}
return $ret;
}
/**
* @brief Parse template comment in search of template info.
*
* like
* \code
* * Name: MyWidget
* * Description: A widget
* * Version: 1.2.3
* * Author: John <profile url>
* * Author: Jane <email>
* * ContentRegionID: some_id
* * ContentRegionID: some_other_id
* *
*\endcode
* @param string $template the name of the template
* @return array with the information
*/
function get_template_info($template){
$m = [];
$info = [
'name' => $template,
'description' => '',
'author' => [],
'maintainer' => [],
'version' => '',
'contentregion' => []
];
$checkpaths = [
'view/php/' . $template . '.php',
];
$template_found = false;
foreach ($checkpaths as $path) {
if (is_file($path)) {
$template_found = true;
$f = file_get_contents($path);
break;
}
}
if(!($template_found && $f))
return $info;
$f = escape_tags($f);
$r = preg_match('|/\*.*\*/|msU', $f, $m);
if ($r) {
$ll = explode("\n", $m[0]);
foreach($ll as $l) {
$l = trim($l, "\t\n\r */");
if ($l != ''){
list($k, $v) = array_map('trim', explode(':', $l, 2));
$k = strtolower($k);
if (in_array($k, ['author', 'maintainer'])){
$r = preg_match('|([^<]+)<([^>]+)>|', $v, $m);
if ($r) {
$info[$k][] = array('name' => $m[1], 'link' => $m[2]);
} else {
$info[$k][] = array('name' => $v);
}
}
elseif (in_array($k, ['contentregion'])){
$info[$k][] = array_map('trim', explode(',', $v));
}
else {
$info[$k] = $v;
}
}
}
}
return $info;
}
function get_pdl($module) {
$ret = [
'pdl' => null,
'modified' => true
];
$pdl_path = 'mod_' . $module . '.pdl';
$ret['pdl'] = get_pconfig(local_channel(), 'system', $pdl_path);
if(!$ret['pdl']) {
$pdl_path = theme_include($pdl_path);
if ($pdl_path) {
$ret['pdl'] = file_get_contents($pdl_path);
$ret['modified'] = false;
}
}
return $ret;
}
}

View File

@@ -11,14 +11,14 @@ class Calendar {
$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);
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -34,14 +34,14 @@ class Calendar {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Calendar Settings'),
'$features' => process_module_features_get(local_channel(), $features),
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -35,6 +35,8 @@ class Channel {
$pageflags = $channel['channel_pageflags'];
$existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0);
$expire = ((x($_POST, 'expire')) ? intval($_POST['expire']) : 0);
$incl = ((x($_POST['message_filter_incl'])) ? htmlspecialchars_decode(trim($_POST['message_filter_incl']), ENT_QUOTES) : '');
$excl = ((x($_POST['message_filter_excl'])) ? htmlspecialchars_decode(trim($_POST['message_filter_excl']), ENT_QUOTES) : '');
if ($adult != $existing_adult) {
$pageflags = ($pageflags ^ PAGE_ADULT);
@@ -131,6 +133,8 @@ class Channel {
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);
set_pconfig(local_channel(), 'system', 'message_filter_incl', $incl);
set_pconfig(local_channel(), 'system', 'message_filter_excl', $excl);
$r = q("update channel set channel_pageflags = %d, channel_timezone = '%s',
channel_location = '%s', channel_notifyflags = %d, channel_expire_days = %d
@@ -277,6 +281,8 @@ class Channel {
'$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.')],
'$message_filter_excl' => ['message_filter_excl', t('Do not import posts with this text'), get_pconfig(local_channel(), 'system', 'message_filter_excl', ''), t('Words one per line or #tags, $categories, /patterns/, lang=xx, lang!=xx - leave blank to import all posts')],
'$message_filter_incl' => ['message_filter_incl', t('Only import posts with this text'), get_pconfig(local_channel(), 'system', 'message_filter_incl', ''), t('Words one per line or #tags, $categories, /patterns/, lang=xx, lang!=xx - leave blank to import all posts')]
]);
call_hooks('settings_form', $o);

View File

@@ -13,7 +13,7 @@ class Channel_home {
$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);
@@ -25,10 +25,10 @@ class Channel_home {
$channel_menu = ((x($_POST['channel_menu'])) ? htmlspecialchars_decode(trim($_POST['channel_menu']),ENT_QUOTES) : '');
set_pconfig(local_channel(),'system','channel_menu',$channel_menu);
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -82,7 +82,7 @@ class Channel_home {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Channel Home Settings'),
@@ -90,7 +90,7 @@ class Channel_home {
'$extra_settings_html' => $extra_settings_html,
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -11,14 +11,14 @@ class Connections {
$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);
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -34,14 +34,14 @@ class Connections {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Connections Settings'),
'$features' => process_module_features_get(local_channel(), $features),
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -11,14 +11,14 @@ class Directory {
$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);
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -34,14 +34,14 @@ class Directory {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Directory Settings'),
'$features' => process_module_features_get(local_channel(), $features),
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -11,14 +11,14 @@ class Editor {
$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);
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -34,14 +34,14 @@ class Editor {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Editor Settings'),
'$features' => process_module_features_get(local_channel(), $features),
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -11,14 +11,14 @@ class Events {
$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);
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -34,14 +34,14 @@ class Events {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Events Settings'),
'$features' => process_module_features_get(local_channel(), $features),
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -12,14 +12,14 @@ class Manage {
$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);
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -35,14 +35,14 @@ class Manage {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Channel Manager Settings'),
'$features' => process_module_features_get(local_channel(), $features),
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -21,10 +21,10 @@ class Network {
$network_divmore_height = 50;
set_pconfig(local_channel(),'system','network_divmore_height', $network_divmore_height);
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -53,7 +53,7 @@ class Network {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Stream Settings'),
@@ -61,7 +61,7 @@ class Network {
'$extra_settings_html' => $extra_settings_html,
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -7,18 +7,18 @@ use Zotlabs\Lib\Libsync;
class Photos {
function post() {
$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);
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -34,14 +34,14 @@ class Photos {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Photos Settings'),
'$features' => process_module_features_get(local_channel(), $features),
'$submit' => t('Submit')
));
return $o;
}

View File

@@ -119,7 +119,7 @@ class Privacy {
],
'$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')]],
'$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

@@ -23,7 +23,7 @@ class Profiles {
Libsync::build_sync_packet();
if($_POST['rpath'])
if(isset($_POST['rpath']) && is_local_url($_POST['rpath']))
goaway($_POST['rpath']);
return;
@@ -43,7 +43,7 @@ class Profiles {
$tpl = get_markup_template("settings_module.tpl");
$o .= replace_macros($tpl, array(
'$rpath' => $rpath,
'$rpath' => escape_url($rpath),
'$action_url' => 'settings/' . $module,
'$form_security_token' => get_form_security_token('settings_' . $module),
'$title' => t('Profiles Settings'),

View File

@@ -75,7 +75,7 @@ class Setup extends \Zotlabs\Web\Controller {
$siteurl = ((isset($_POST['siteurl'])) ? trim($_POST['siteurl']) : '');
if (empty($db_charset)) {
$db_charset = ((intval($db_type) === 0) ? 'utf8mb4' : 'UTF8');
$db_charset = ((intval($dbtype) === 0) ? 'utf8mb4' : 'UTF8');
}
// $siteurl should not have a trailing slash
@@ -106,7 +106,7 @@ class Setup extends \Zotlabs\Web\Controller {
$siteurl = ((isset($_POST['siteurl'])) ? trim($_POST['siteurl']) : '');
if (empty($db_charset)) {
$db_charset = ((intval($db_type) === 0) ? 'utf8mb4' : 'UTF8');
$db_charset = ((intval($dbtype) === 0) ? 'utf8mb4' : 'UTF8');
}
if($siteurl != z_root()) {

View File

@@ -2,7 +2,7 @@
namespace Zotlabs\Photo;
use Zotlabs\Lib\Hashpath;
use Zotlabs\Lib\Hashpath;
/**
* @brief Abstract photo driver class.
@@ -494,11 +494,11 @@ abstract class PhotoDriver {
( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires, profile )
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($p['profile']));
}
logger('Photo save imgscale ' . $p['imgscale'] . ' returned ' . intval($r));
logger('Photo save imgscale ' . $p['imgscale'] . ' returned: ' . (($r) ? 1 : 0));
return $r;
}
/**
* @brief Stores thumbnail to database or filesystem.
*
@@ -530,13 +530,13 @@ abstract class PhotoDriver {
}
else
$arr['os_storage'] = 0;
if(! $this->save($arr)) {
if(array_key_exists('os_syspath', $arr))
@unlink($arr['os_syspath']);
return false;
}
return true;
}

View File

@@ -127,6 +127,7 @@ class HTTPSig {
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'));
@@ -142,20 +143,34 @@ class HTTPSig {
$signed_data = rtrim($signed_data, "\n");
$algorithm = null;
if ($sig_block['algorithm'] === 'rsa-sha256') {
$algorithm = 'sha256';
}
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']);
if ($sig_block['algorithm'] === 'hs2019') {
if (isset($cached_key['algorithm'])) {
if (strpos($cached_key['algorithm'], 'rsa-sha256') !== false) {
$algorithm = 'sha256';
}
if (strpos($cached_key['algorithm'], 'rsa-sha512') !== false) {
$algorithm = 'sha512';
}
}
}
if (!($cached_key && $cached_key['public_key'])) {
return $result;
@@ -296,7 +311,7 @@ class HTTPSig {
$best = Libzot::zot_record_preferred($x);
}
if ($best && $best['xchan_pubkey']) {
return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'hubloc' => $best];
return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'algorithm' => get_xconfig($best['xchan_hash'], 'system', 'signing_algorithm'), 'hubloc' => $best];
}
}
@@ -308,12 +323,38 @@ class HTTPSig {
// The record wasn't in cache. Fetch it now.
$r = ActivityStreams::fetch($id);
$signatureAlgorithm = EMPTY_STR;
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' => []];
// the w3c sec context has conflicting names and no defined values for this property except
// "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
// Since the names conflict, it could mess up LD-signatures but we will accept both, and at this
// time we will only look for the substrings 'rsa-sha256' and 'rsa-sha512' within those properties.
// We will also accept a toplevel 'sigAlgorithm' regardless of namespace with the same constraints.
// Default to rsa-sha256 if we can't figure out. If they're sending 'hs2019' we have to
// look for something.
if (isset($r['publicKey']['signingAlgorithm'])) {
$signatureAlgorithm = $r['publicKey']['signingAlgorithm'];
set_xconfig($portable_id, 'system', 'signing_algorithm', $signatureAlgorithm);
}
if (isset($r['publicKey']['signatureAlgorithm'])) {
$signatureAlgorithm = $r['publicKey']['signatureAlgorithm'];
set_xconfig($portable_id, 'system', 'signing_algorithm', $signatureAlgorithm);
}
if (isset($r['sigAlgorithm'])) {
$signatureAlgorithm = $r['sigAlgorithm'];
set_xconfig($portable_id, 'system', 'signing_algorithm', $signatureAlgorithm);
}
return ['public_key' => self::convertKey($r['publicKey']['publicKeyPem']), 'portable_id' => $portable_id, 'algorithm' => (($signatureAlgorithm) ? $signatureAlgorithm : 'rsa-sha256'), 'hubloc' => []];
}
}
}
@@ -343,7 +384,7 @@ class HTTPSig {
}
if ($best && $best['xchan_pubkey']) {
return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'hubloc' => $best];
return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'algorithm' => get_xconfig($best['xchan_hash'], 'system', 'signing_algorithm'), 'hubloc' => $best];
}
}
@@ -389,7 +430,7 @@ class HTTPSig {
}
if ($best && $best['xchan_pubkey']) {
return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'hubloc' => $best];
return ['portable_id' => $best['xchan_hash'], 'public_key' => $best['xchan_pubkey'], 'algorithm' => get_xconfig($best['xchan_hash'], 'system', 'signing_algorithm'), 'hubloc' => $best];
}
}
@@ -461,6 +502,9 @@ class HTTPSig {
$x = self::sign($head, $prvkey, $alg);
// TODO: should we default to hs2019?
// $headerval = 'keyId="' . $keyid . '",algorithm="' . (($algorithm === 'rsa-sha256') ? 'hs2019' : $algorithm) . '",headers="' . $x['headers'] . '",signature="' . $x['signature'] . '"';
$headerval = 'keyId="' . $keyid . '",algorithm="' . $algorithm . '",headers="' . $x['headers'] . '",signature="' . $x['signature'] . '"';
if ($encryption) {
@@ -528,11 +572,14 @@ class HTTPSig {
if ($head) {
foreach ($head as $k => $v) {
$headers .= strtolower($k) . ': ' . trim($v) . "\n";
$k = strtolower($k);
$v = (($v) ? trim($v) : '');
$headers .= $k . ': ' . $v . "\n";
if ($fields)
$fields .= ' ';
$fields .= strtolower($k);
$fields .= $k;
}
// strip the trailing linefeed
$headers = rtrim($headers, "\n");

View File

@@ -107,7 +107,8 @@ class WebServer {
$Router->Dispatch();
$this->set_homebase();
// TODO: this is not used for anything atm and messes up comanche templates by adding some javascript
//$this->set_homebase();
// now that we've been through the module content, see if the page reported
// a permission problem and if so, a 403 response would seem to be in order.

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Activity
* * Description: Shows the unseen activity count per contact
*/
namespace Zotlabs\Widget;
class Activity {

View File

@@ -1,5 +1,12 @@
<?php
/**
* * Name: Activity filters
* * Description: Filters for the network stream
* * Requires: network
*/
namespace Zotlabs\Widget;
use App;

View File

@@ -2,6 +2,12 @@
namespace Zotlabs\Widget;
/**
* * Name: Activity order
* * Description: Order the network stream by posted date, last commented or by date unthreaded
* * Requires: network
*/
class Activity_order {
function widget($arr) {
@@ -22,7 +28,7 @@ class Activity_order {
switch($_GET['order']){
case 'post':
$postord_active = 'active';
set_pconfig(local_channel(), 'mod_network', 'order', 1);
set_pconfig(local_channel(), 'mod_network', 'order', 1);
break;
case 'comment':
$commentord_active = 'active';

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Admin menu
* * Requires: admin
*/
namespace Zotlabs\Widget;
class Admin {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Affinity Tool
* * Description: Filter the network stream by affinity, requires the Affinity Tool App
* * Requires: network
*/
namespace Zotlabs\Widget;
use Zotlabs\Lib\Apps;
@@ -13,7 +19,7 @@ class Affinity {
if(! Apps::system_app_installed(local_channel(),'Affinity Tool'))
return;
$default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0);
$default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99);
@@ -54,7 +60,7 @@ class Affinity {
'$refresh' => t('Refresh'),
'$labels' => $label_str,
));
$arr = array('html' => $x);
call_hooks('main_slider',$arr);
@@ -63,4 +69,4 @@ class Affinity {
}
}

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Album
* * Description: Displays an album with a title which can be defined via the 'album' and 'title' variable
* * Requires: channel, articles, cards, wiki
*/
namespace Zotlabs\Widget;
require_once('include/attach.php');
@@ -99,7 +105,7 @@ class Album {
'$upload_form' => $upload_form,
'$usage' => $usage_message
));
return $o;
}
}

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: App categories
* * Description: Shows a menu with various app categories
* * Requires: apps
*/
namespace Zotlabs\Widget;
class Appcategories {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: App cloud
* * Description: Shows a cloud with various app categories
* * Requires: apps
*/
namespace Zotlabs\Widget;
class Appcloud {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: App store menu
* * Description: Shows a menu with links to installed and available apps
* * Requires: apps
*/
namespace Zotlabs\Widget;

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Archive
* * Description: A menu with links to content sorted by years and months
* * Requires: channel, articles, cards
*/
namespace Zotlabs\Widget;

View File

@@ -1,14 +1,15 @@
<?php
/**
* * Name: Bookmarked chats
* * Description: A menu with bookmarked chats
*/
namespace Zotlabs\Widget;
class Bookmarkedchats {
function widget($arr) {
if(! feature_enabled(\App::$profile['profile_uid'],'ajaxchat'))
return '';
$h = get_observer_hash();
if(! $h)
return;

View File

@@ -2,6 +2,13 @@
namespace Zotlabs\Widget;
/**
* * Name: Category cloud
* * Description: Display category links in a cloud
* * Requires: channel, cards, articles
*/
class Catcloud {
function widget($arr) {
@@ -22,7 +29,7 @@ class Catcloud {
return card_catblock(\App::$profile['profile_uid'], $limit, '', \App::$profile['channel_hash']);
case 'articles':
if(! perm_is_allowed(\App::$profile['profile_uid'], get_observer_hash(), 'view_pages'))
return '';

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Category cloud wall
* * Description: Display category links in a cloud restricted to wall posts
* * Requires: channel
*/
namespace Zotlabs\Widget;
class Catcloud_wall {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Categories
* * Description: Display a menu with links to categories
* * Requires: channel, articles, cards, cloud
*/
namespace Zotlabs\Widget;
use App;

View File

@@ -1,9 +1,13 @@
<?php
/**
* * Name: CalDAV/CardDAV tools
* * Description: A widget with various CalDAV and CardDAV tools
* * Requires: cdav
*/
namespace Zotlabs\Widget;
class Cdav {
function widget() {
@@ -164,7 +168,7 @@ class Cdav {
'uri' => $sabreabook['uri'],
'displayname' => $sabreabook['{DAV:}displayname'],
'id' => $sabreabook['id']
];
}

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Chatroom list
* * Description: A menu with links to your chatrooms
* * Requires: chat, channel, articles, cards, wiki
*/
namespace Zotlabs\Widget;
class Chatroom_list {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Chatroom members
* * Description: A widget that shows members of a chatroom
* * Requires: chat
*/
namespace Zotlabs\Widget;
class Chatroom_members {

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Clock
* * Description: A simple widget that shows the current time
*/
namespace Zotlabs\Widget;
class Clock {

View File

@@ -1,54 +0,0 @@
<?php
namespace Zotlabs\Widget;
use Zotlabs\Lib\AccessList;
class Collections {
function widget($args) {
if(argc() < 2)
// return;
$mode = ((array_key_exists('mode',$args)) ? $args['mode'] : 'conversation');
switch($mode) {
case 'conversation':
$every = argv(0);
$each = argv(0);
$edit = true;
$current = $_REQUEST['gid'];
$abook_id = 0;
$wmode = 0;
break;
case 'connections':
$every = 'connections';
$each = 'group';
$edit = true;
$current = $_REQUEST['gid'];
$abook_id = 0;
$wmode = 0;
case 'groups':
$every = 'connections';
$each = argv(0);
$edit = false;
$current = intval(argv(1));
$abook_id = 0;
$wmode = 1;
break;
case 'abook':
$every = 'connections';
$each = 'group';
$edit = false;
$current = 0;
$abook_id = \App::$poi['abook_xchan'];
$wmode = 1;
break;
default:
return '';
break;
}
return AccessList::widget($every, $each, $edit, $current, $abook_id, $wmode);
}
}

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Common friends
* * Description: Display common friends to visitors
* * Requires: channel, articles, cards, wiki
*/
namespace Zotlabs\Widget;
require_once('include/contact_widgets.php');
@@ -8,7 +14,7 @@ class Common_friends {
function widget($arr) {
if((! \App::$profile['profile_uid'])
if((! \App::$profile['profile_uid'])
|| (! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),'view_contacts'))) {
return '';
}

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Cover photo
* * Description: Display a cover photo in the banner region
* * Requires: disabled_for_pdledit_gui
*/
namespace Zotlabs\Widget;
class Cover_photo {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Design tools
* * Description: Links to useful tools for webpages
* * Requires: webpages
*/
namespace Zotlabs\Widget;
class Design_tools {
@@ -11,4 +17,4 @@ class Design_tools {
return EMPTY_STR;
}
}
}

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Dirsort
* * Description: Various options to provide different vies of the directory
* * Requires: directory
*/
namespace Zotlabs\Widget;
use Zotlabs\Lib\Libzotdir;

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Directory tags
* * Description: Show directory tags in a cloud
* * Requires: directory
*/
namespace Zotlabs\Widget;
class Dirtags {

View File

@@ -1,19 +0,0 @@
<?php
namespace Zotlabs\Widget;
class Eventstools {
function widget($arr) {
if(! local_channel())
return;
return replace_macros(get_markup_template('events_tools_side.tpl'), array(
'$title' => t('Events Tools'),
'$export' => t('Export Calendar'),
'$import' => t('Import Calendar'),
'$submit' => t('Submit')
));
}
}

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Saved folders
* * Description: A menu containing saved folders
* * Requires: network
*/
namespace Zotlabs\Widget;
require_once('include/contact_widgets.php');
@@ -10,7 +16,6 @@ class Filer {
if(! local_channel())
return '';
$selected = ((x($_REQUEST,'file')) ? $_REQUEST['file'] : '');
$terms = array();

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Find channels
* * Description: A simple form to search for channels in the directory
*/
namespace Zotlabs\Widget;
require_once('include/contact_widgets.php');

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Follow
* * Description: A simple form which allows you to enter an address and send a follow request
*/
namespace Zotlabs\Widget;
@@ -24,7 +29,7 @@ class Follow {
else {
$abook_usage_message = '';
}
return replace_macros(get_markup_template('follow.tpl'),array(
'$connect' => t('Add New Connection'),
'$desc' => t('Enter channel address'),

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Forums
* * Description: A list of forum channels with unseen item counts
*/
namespace Zotlabs\Widget;
class Forums {
@@ -21,35 +26,7 @@ class Forums {
$unseen = 1;
$perms_sql = item_permissions_sql(local_channel()) . item_normal();
$xf = false;
$x1 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'send_stream' and v = '0'",
intval(local_channel())
);
if($x1) {
$xc = ids_to_querystr($x1,'xchan',true);
$x2 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'tag_deliver' and v = '1' and xchan in (" . $xc . ") ",
intval(local_channel())
);
if($x2) {
$xf = ids_to_querystr($x2,'xchan',true);
// private forums
$x3 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'post_wall' and v = '1' and xchan in (" . $xc . ") and not xchan in (" . $xf . ") ",
intval(local_channel())
);
if($x3) {
$xf = ids_to_querystr(array_merge($x2,$x3),'xchan',true);
}
}
}
$sql_extra = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 ");
$sql_extra = " and xchan_pubforum = 1 ";
$r1 = q("select abook_id, xchan_hash, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_deleted = 0 and abook_channel = %d and abook_pending = 0 and abook_ignored = 0 and abook_blocked = 0 and abook_archived = 0 $sql_extra order by xchan_name $limit ",
intval(local_channel())

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Full profile
* * Description: Profile card with extended profile info
* * Requires: channel, articles, cards, wiki, cloud, photos
*/
namespace Zotlabs\Widget;
class Fullprofile {

View File

@@ -1,11 +1,18 @@
<?php
/**
* * Name: Help index
* * Description: Help pages index
*/
namespace Zotlabs\Widget;
class Helpindex {
function widget($arr) {
require_once('include/help.php');
$o .= '<div class="widget">';
$level_0 = get_help_content('sitetoc');

View File

@@ -1,5 +1,12 @@
<?php
/**
* * Name: HQ Controls
* * Description: Control buttons for the HQ module
* * Author: Mario Vavti
* * Requires: hq
*/
namespace Zotlabs\Widget;
use Zotlabs\Lib\Apps;

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Item
* * Description: Display a webpage by title or mid,
* * Requires: channel, articles, cards, wiki
*/
namespace Zotlabs\Widget;
require_once('include/security.php');
@@ -35,7 +41,7 @@ class Item {
);
}
else {
$r = q("select * from item where mid = '%s' and uid = %d and item_type = "
$r = q("select * from item where mid = '%s' and uid = %d and item_type = "
. intval(ITEM_TYPE_WEBPAGE) . " $sql_extra limit 1",
dbesc($arr['mid']),
intval($channel_id)

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Menu preview
* * Description: Shows a preview of the current menu
* * Requires: mitem
*/
namespace Zotlabs\Widget;
require_once('include/menu.php');

View File

@@ -1,5 +1,13 @@
<?php
/**
* * Name: HQ Messages
* * Description: Quick access to messages, direct messages, starred messages (if enabled) and notifications
* * Author: Mario Vavti
* * Requires: hq
*/
namespace Zotlabs\Widget;
use App;
@@ -17,8 +25,8 @@ class Messages {
$tpl = get_markup_template('messages_widget.tpl');
$o = replace_macros($tpl, [
'$entries' => $page['entries'],
'$offset' => $page['offset'],
'$entries' => $page['entries'] ?? [],
'$offset' => $page['offset'] ?? 0,
'$feature_star' => feature_enabled(local_channel(), 'star_posts'),
'$strings' => [
'messages_title' => t('Public and restricted messages'),
@@ -37,11 +45,11 @@ class Messages {
if (!local_channel())
return;
if ($options['offset'] == -1) {
if (isset($options['offset']) && $options['offset'] == -1) {
return;
}
if ($options['type'] == 'notification') {
if (isset($options['type']) && $options['type'] == 'notification') {
return self::get_notices_page($options);
}
@@ -103,13 +111,20 @@ class Messages {
if (!$summary) {
$summary = $item['summary'];
}
if (!$summary) {
$summary = htmlentities(html2plain(bbcode($item['body'], ['drop_media' => true]), 75, true), ENT_QUOTES, 'UTF-8', false);
$summary = html2plain(bbcode($item['body'], ['drop_media' => true]), 75, true);
if ($summary) {
$summary = htmlentities($summary, ENT_QUOTES, 'UTF-8', false);
}
}
if (!$summary) {
$summary = '...';
}
$summary = substr_words($summary, 68);
else {
$summary = substr_words($summary, 68);
}
switch(intval($item['item_private'])) {
case 1:

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: New member
* * Description: Display useful links for new members to help them get started
*/
namespace Zotlabs\Widget;
class Newmember {
@@ -29,7 +34,7 @@ class Newmember {
$options = [
t('Profile Creation'),
[
[
'profile_photo' => t('Upload profile photo'),
'cover_photo' => t('Upload cover photo'),
'profiles' => t('Edit your profile'),
@@ -84,4 +89,4 @@ class Newmember {
}

View File

@@ -1,5 +1,13 @@
<?php
/**
* * Name: Notes
* * Description: A simple notes widget, requires the Notes App
* * Author: Mike Macgirvin
* * Author: Mario Vavti
* * Maintainer: Mario Vavti
*/
namespace Zotlabs\Widget;
use Zotlabs\Lib\Apps;
@@ -33,9 +41,6 @@ class Notes {
]
));
return $o;
}
}

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Notifications
* * Description: Shows all kind of notifications
* * Author: Mario Vavti
*/
namespace Zotlabs\Widget;
class Notifications {

View File

@@ -1,5 +1,12 @@
<?php
/**
* * Name: Contact roles
* * Description: Display a menu with all defined contact roles and contacts which are assigned to the selected role
* * Author: Mario Vavti
* * Requires: permcats
*/
namespace Zotlabs\Widget;
use Zotlabs\Lib\Permcat;

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Photo
* * Description: Displays a single photo
*/
namespace Zotlabs\Widget;

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Photo albums
* * Description: Displays a menu with links to existing photo albums
* * Requires: photos
*/
namespace Zotlabs\Widget;
require_once('include/photos.php');

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Random photo
* * Description: Display a random photo
*/
namespace Zotlabs\Widget;
require_once('include/photos.php');
@@ -40,15 +45,15 @@ class Photo_rand {
if(strpos($url, 'http') !== 0)
return '';
if(array_key_exists('style', $arr) && isset($arr['style']))
$style = $arr['style'];
// ensure they can't sneak in an eval(js) function
if(strpos($style,'(') !== false)
return '';
$url = zid($url);
$o = '<div class="widget">';

View File

@@ -1,11 +1,14 @@
<?php
namespace Zotlabs\Widget;
/*
* Show pinned content
*
/**
* * Name: Pinned items
* * Description: Display pinned items
* * Author: Max Kostikov
* * Requires: disabled_for_pdledit_gui
*/
class Pinned {
private $allowed_types = 0;

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Portfolio
* * Description: Display a photo album in a portfolio style
* * Requires: channel, articles, cards, wiki
*/
namespace Zotlabs\Widget;
require_once('include/attach.php');
@@ -8,11 +14,9 @@ class Portfolio {
function widget($args) {
$owner_uid = \App::$profile_uid;
$sql_extra = permissions_sql($owner_uid);
if(! perm_is_allowed($owner_uid,get_observer_hash(),'view_storage'))
return '';
@@ -112,7 +116,7 @@ class Portfolio {
'$upload_form' => $upload_form,
'$usage' => $usage_message
));
return $o;
}
}

View File

@@ -1,5 +1,12 @@
<?php
/**
* * Name: Privacy Groups
* * Description: Display a menu with links to existing privacy groups
* * Requires: group
*/
namespace Zotlabs\Widget;
use Zotlabs\Lib\AccessList;

View File

@@ -1,5 +1,12 @@
<?php
/**
* * Name: Profile
* * Description: Your profile card
* * Requires: channel, articles, cards, wiki, cloud, photos
*/
namespace Zotlabs\Widget;
use App;

View File

@@ -1,16 +0,0 @@
<?php
namespace Zotlabs\Widget;
class Pubsites {
// used by site ratings pages to provide a return link
function widget($arr) {
if(\App::$poi)
return;
return '<div class="widget"><ul class="nav nav-pills"><li><a href="pubsites">' . t('Public Hubs') . '</a></li></ul></div>';
}
}

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Public stream tags
* * Description: Display public stream tags in a cloud
*/
namespace Zotlabs\Widget;
class Pubtagcloud {

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Random block
* * Description: Display a random block item
*/
namespace Zotlabs\Widget;
class Random_block {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Rating
* * Description: Deprecated rating tool
* * Requires: disabled_for_pdledit_gui
*/
namespace Zotlabs\Widget;
class Rating {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Saved searches
* * Description: A search form which also displays saved searches if the feature is enabled
* * Requires: network
*/
namespace Zotlabs\Widget;
class Savedsearch {

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Settings menu
* * Description: Display the channel settings menu
*/
namespace Zotlabs\Widget;
class Settings_menu {

View File

@@ -1,18 +0,0 @@
<?php
namespace Zotlabs\Widget;
class Shortprofile {
function widget($arr) {
if(! \App::$profile['profile_uid'])
return;
$block = observer_prohibited();
return profile_sidebar(\App::$profile, $block, true, true);
}
}

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Search
* * Description: A search form
* * Requires: search, network, channel
*/
namespace Zotlabs\Widget;

View File

@@ -1,14 +1,18 @@
<?php
/**
* * Name: Suggested chats
* * Description: A menu with chatroom suggestions
* * Requires: disabled_due_to_reasons
*/
namespace Zotlabs\Widget;
class Suggestedchats {
function widget($arr) {
if(! feature_enabled(\App::$profile['profile_uid'],'ajaxchat'))
return '';
// There are reports that this tool does not ever remove chatrooms on dead sites,
// and also will happily link to private chats which you cannot enter.
// For those reasons, it will be disabled until somebody decides it's worth

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Suggestions
* * Description: Display suggestions for new contacts
*/
namespace Zotlabs\Widget;
use Zotlabs\Lib\Apps;
@@ -12,7 +17,7 @@ class Suggestions {
function widget($arr) {
if((! local_channel()) || (! Apps::system_app_installed(local_channel(), 'Suggest Channels')))
if(! local_channel())
return EMPTY_STR;
$r = suggestion_query(local_channel(),get_observer_hash(),0,20);
@@ -23,14 +28,14 @@ class Suggestions {
$arr = array();
// Get two random entries from the top 20 returned.
// Get four random entries from the top 20 returned.
// We'll grab the first one and the one immediately following.
// This will throw some entropy intot he situation so you won't
// be looking at the same two mug shots every time the widget runs
$index = ((count($r) > 2) ? mt_rand(0,count($r) - 2) : 0);
$index = ((count($r) > 4) ? mt_rand(0,count($r) - 4) : 0);
for($x = $index; $x <= ($index+1); $x ++) {
for($x = $index; $x <= ($index+3); $x ++) {
$rr = $r[$x];
if(! $rr['xchan_url'])
break;

View File

@@ -2,6 +2,11 @@
namespace Zotlabs\Widget;
/**
* * Name: Tag cloud
* * Description: Display hashtags of your network items in a cloud
* * Requires: network, hq
*/
class Tagcloud {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Tag cloud wall
* * Description: Display hashtags of your channel wall items in a cloud if the feature is enabled
* * Requires: channel
*/
namespace Zotlabs\Widget;
class Tagcloud_wall {

View File

@@ -1,8 +1,11 @@
<?php
namespace Zotlabs\Widget;
/**
* * Name: Task list
* * Description: Simple task list mangager
*/
require_once('include/event.php');
namespace Zotlabs\Widget;
class Tasklist {
@@ -20,7 +23,7 @@ class Tasklist {
}
</script>';
$o .= '<div class="widget">' . '<h3>' . t('Tasks') . '</h3><div class="tasklist-tasks">';
$o .= '<div class="widget">' . '<h3>' . t('Tasks') . '</h3><div class="tasklist-tasks mb-1">';
$o .= '</div><form id="tasklist-new-form" action="" ><input class="form-control" id="tasklist-new-summary" type="text" name="summary" value="" /></form>';
$o .= '</div>';
return $o;

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Tokens
* * Description: Display a menu with links to existing guest access tokens
* * Requires: tokens
*/
namespace Zotlabs\Widget;
class Tokens {

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Vcard
* * Description: Your default profile card
*/
namespace Zotlabs\Widget;
class Vcard {

View File

@@ -1,7 +1,12 @@
<?php
namespace Zotlabs\Widget;
/**
* * Name: Website import/export
* * Description: Tools to import and export websites
* * Requires: website
*/
namespace Zotlabs\Widget;
class Website_portation_tools {

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Wiki list
* * Description: A list of existing wikis
*/
namespace Zotlabs\Widget;
class Wiki_list {

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Wiki page history
* * Description: History of an existing wiki page
* * Requires: wiki
*/
namespace Zotlabs\Widget;
class Wiki_page_history {
@@ -10,7 +16,7 @@ class Wiki_page_history {
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$pageHistory = \Zotlabs\Lib\NativeWikiPage::page_history([
'channel_id' => \App::$profile_uid,
'channel_id' => \App::$profile_uid,
'observer_hash' => get_observer_hash(),
'resource_id' => $resource_id,
'pageUrlName' => $pageUrlName

View File

@@ -1,5 +1,11 @@
<?php
/**
* * Name: Wiki pages
* * Description: A list of existing pages of a wiki
* * Requires: wiki
*/
namespace Zotlabs\Widget;
use Zotlabs\Lib\NativeWiki;
@@ -34,7 +40,7 @@ class Wiki_pages {
'$addnew' => t('Add new page'),
'$typelock' => $typelock,
'$lockedtype' => $w['mimeType'],
'$mimetype' => mimetype_select(0,$w['mimeType'],
'$mimetype' => mimetype_select(0,$w['mimeType'],
[ 'text/markdown' => t('Markdown'), 'text/bbcode' => t('BBcode'), 'text/plain' => t('Text') ]),
'$pageName' => array('missingPageName', 'Create Page' , $pageName),
'$refresh' => $arr['refresh'],
@@ -91,7 +97,7 @@ class Wiki_pages {
'$addnew' => t('Add new page'),
'$typelock' => $typelock,
'$lockedtype' => $w['mimeType'],
'$mimetype' => mimetype_select(0,$w['mimeType'],
'$mimetype' => mimetype_select(0,$w['mimeType'],
[ 'text/markdown' => t('Markdown'), 'text/bbcode' => t('BBcode'), 'text/plain' => t('Text') ]),
'$pageName' => array('pageName', t('Page name')),
'$refresh' => $arr['refresh'],

View File

@@ -1,5 +1,10 @@
<?php
/**
* * Name: Zcard
* * Description: Your default profile card including your cover photo
*/
namespace Zotlabs\Widget;
class Zcard {

View File

@@ -1,5 +1,5 @@
version: 3
url: $baseurl/pdledit
version: 1
url: $baseurl/pdledit_gui
requires: local_channel
name: PDL Editor
photo: icon:object-group

View File

@@ -60,7 +60,7 @@ require_once('include/bbcode.php');
require_once('include/items.php');
define('PLATFORM_NAME', 'hubzilla');
define('STD_VERSION', '7.0.3');
define('STD_VERSION', '7.2');
define('ZOT_REVISION', '6.0');
define('DB_UPDATE_VERSION', 1252);
@@ -915,9 +915,14 @@ class App {
if ((x($_SERVER, 'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'], 0, 2) === "q=") {
self::$query_string = str_replace(['<', '>'], ['&lt;', '&gt;'], substr($_SERVER['QUERY_STRING'], 2));
// removing trailing / - maybe a nginx problem
if (substr(self::$query_string, 0, 1) == "/")
self::$query_string = substr(self::$query_string, 1);
// trim trailing '&' if no extra args are present
self::$query_string = rtrim(self::$query_string, '&');
// change the first & to ?
self::$query_string = preg_replace('/&/', '?', self::$query_string, 1);
}
@@ -1687,7 +1692,7 @@ function fix_system_urls($oldurl, $newurl) {
* @param boolean $login_page (optional) default true
* @return string Parsed HTML code.
*/
function login($register = false, $form_id = 'main-login', $hiddens = false, $login_page = true) {
function login($register = false, $form_id = 'main_login', $hiddens = false, $login_page = true) {
$o = '';
$reg = null;
@@ -1747,9 +1752,9 @@ function login($register = false, $form_id = 'main-login', $hiddens = false, $lo
'$login' => t('Login'),
'$remote_login' => t('Remote Authentication'),
'$form_id' => $form_id,
'$lname' => ['username', $lname_label],
'$lpassword' => ['password', t('Password')],
'$remember_me' => [(($login_page) ? 'remember' : 'remember_me'), t('Remember me'), '', '', [t('No'), t('Yes')]],
'$lname' => [$form_id . '_username', $lname_label],
'$lpassword' => [$form_id . '_password', t('Password')],
'$remember_me' => [$form_id . '_remember', t('Remember me'), '', '', [t('No'), t('Yes')]],
'$hiddens' => $hiddens,
'$register' => $reg,
'$lostpass' => t('Forgot your password?'),

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