diff --git a/.gitignore b/.gitignore index 0e5233eaf..b001b6b27 100755 --- a/.gitignore +++ b/.gitignore @@ -19,10 +19,11 @@ Thumbs.db -## Ignore RedMatrix site specific files and folders +## Ignore site specific files and folders .htconfig.php favicon.* addon/ +widget/ custom/ /store/ # site apps diff --git a/.homeinstall/README.md b/.homeinstall/README.md index 96ce3b6d4..7e16e2c52 100644 --- a/.homeinstall/README.md +++ b/.homeinstall/README.md @@ -28,8 +28,8 @@ Hardware Software -+ Fresh installation of Debian 9 (Stretch) on your mini-pc -+ Router with open ports 80 and 443 for your Debian ++ Fresh installation of Debian 9 (Stretch) ++ Router with open ports 80 and 443 for your Hub ## The basic steps (quick overview) @@ -39,10 +39,12 @@ Software - mkdir -p /var/www - cd /var/www - git clone https://github.com/redmatrix/hubzilla.git html - - cp .homeinstall/hubzilla-config.txt.template .homeinstall/hubzilla-config.txt - - nano .homeinstall/hubzilla-config.txt + - cd /html/.homeinstall + - cp hubzilla-config.txt.template hubzilla-config.txt + - nano hubzilla-config.txt - Read the comments carefully - Enter your values: db pass, domain, values for dyn DNS + - Make sure your your external drive (for backups) is mounted - hubzilla-setup.sh as root - ... wait, wait, wait until the script is finised - reboot @@ -56,16 +58,19 @@ Software ### Recommended: USB Drive for Backups -The installation will create a daily backup. - -If the backup process does not find an external device than the backup goes to -the internal disk. +The installation will create a daily backup written to an external drive. The USB drive must be compatible with the filesystems - ext4 (if you do not want to encrypt the USB) - LUKS + ext4 (if you want to encrypt the USB) +The backup includes + +- Hubzilla DB +- Hubzilla installation /var/www/html +- Certificates for letsencrypt + ## Preparations Software ### Install Debian Linux on the Mini-PC @@ -107,20 +112,17 @@ You can use subdomains as well my.cooldomain.org -There are two way to get a domain +There are two ways to get a domain... -- buy a domain, or -- register a free subdomain - -### Method 1: Buy an own Domain +### Method 1: Buy a Domain ...for example buy at selfHOST.de The cost are around 10,- € once and 1,50 € per month (2017). -### Method 2 Register a (free) Subdomain +The cost are around 10,- € once and 1,50 € per month (2017). -...for example register at freeDNS +...for example register at freedns.afraid.org Follow the instructions in .homeinstall/hubzilla-config.txt. @@ -158,10 +160,12 @@ Copy the template file cp hubzilla-config.txt.template hubzilla-config.txt -Change the file "hubzilla-config.txt". Read the instructions there carefully and enter your values. +Modify the file "hubzilla-config.txt". Read the instructions there carefully and enter your values. nano hubzilla-config.txt +Make sure your external drive (for backups) is plugged in and can be mounted as configured in "hubzilla-config.txt". Otherwise the daily backups will not work. + Run the script ./hubzilla-setup.sh @@ -185,6 +189,13 @@ Leave db type "MySQL" untouched. Follow the instructions in the next pages. +After the daily script was executed at 05:30 (am) + +- look at var/www/html/hubzilla-daily.log +- check your backup on the external drive +- optionally view the daily log under yourdomain.org/admin/logs/ + - set the logfile to var/www/html/hubzilla-daily.log + ## Note for the Rasperry The script was tested with an Raspberry 3 under Raspian (Debian 9.3, 2017-11-29-raspbian-stretch.img). diff --git a/.homeinstall/hubzilla-setup.sh b/.homeinstall/hubzilla-setup.sh index 023b9eed1..635bb3518 100755 --- a/.homeinstall/hubzilla-setup.sh +++ b/.homeinstall/hubzilla-setup.sh @@ -459,11 +459,6 @@ function configure_cron_selfhost { fi } -function install_git { - print_info "installing git..." - nocheck_install "git" -} - function install_letsencrypt { print_info "installing let's encrypt ..." # check if user gave domain @@ -570,20 +565,13 @@ function check_https { } function install_hubzilla { - print_info "installing hubzilla..." - # rm -R /var/www/html/ # for "stand alone" usage - cd /var/www/ - # git clone https://github.com/redmatrix/hubzilla html # for "stand alone" usage - cd html/ - git clone https://github.com/redmatrix/hubzilla-addons addon + print_info "installing hubzilla addons..." + cd /var/www/html/ + util/add_addon_repo https://github.com/redmatrix/hubzilla-addons.git hzaddons mkdir -p "store/[data]/smarty3" chmod -R 777 store touch .htconfig.php chmod ou+w .htconfig.php - # uncomment the last function call "install_hubzilla_plugins" - # - if you want to install addons and themes that are not officially supported - # - and read the comments in function "install_hubzilla_plugins" how do do it - # install_hubzilla_plugins cd /var/www/ chown -R www-data:www-data html chown root:www-data /var/www/html/ @@ -598,72 +586,6 @@ function install_hubzilla { print_info "installed hubzilla" } -function install_hubzilla_plugins { - print_info "installing hubzilla plugins..." - cd /var/www/html - plugin_install=.homeinstall/plugin_install.txt - theme_install=.homeinstall/theme_install.txt - # overwrite script to update the plugin and themes - rm -f $plugins_update - echo "cd /var/www/html" >> $plugins_update - ################### - # write plugin file - if [ ! -f "$plugin_install" ] - then - echo "# To install a plugin" >> $plugin_install - echo "# 1. add the plugin in a new line and run" >> $plugin_install - echo "# 2. run" >> $plugin_install - echo "# cd /var/www/html/.homeinstall" >> $plugin_install - echo "# ./hubzilla-setup.sh" >> $plugin_install - echo "https://gitlab.com/zot/ownmapp.git ownMapp" >> $plugin_install - fi - # install plugins - while read -r line; do - [[ "$line" =~ ^#.*$ ]] && continue - p_url=$(echo $line | awk -F' ' '{print $1}') - p_name=$(echo $line | awk -F' ' '{print $2}') - # basic check of format - if [ ${#p_url} -ge 1 ] && [ ${#p_name} -ge 1 ] - then - # install addon - util/add_addon_repo $line - util/update_addon_repo $p_name # not sure if this line is neccessary - echo "util/update_addon_repo $p_name" >> $plugins_update - else - print_info "skipping installation of a plugin from file $plugin_install - something wrong with format in line: $line" - fi - done < "$plugin_install" - ################### - # write theme file - if [ ! -f "$theme_install" ] - then - echo "# To install a theme" >> $theme_install - echo "# 1. add the theme in a new line and run" >> $theme_install - echo "# 2. run" >> $theme_install - echo "# cd /var/www/html/.homeinstall" >> $theme_install - echo "# ./hubzilla-setup.sh" >> $theme_install - echo "https://github.com/DeadSuperHero/hubzilla-themes.git DeadSuperHeroThemes" >> $theme_install - - fi - # install plugins - while read -r line; do - [[ "$line" =~ ^#.*$ ]] && continue - p_url=$(echo $line | awk -F' ' '{print $1}') - p_name=$(echo $line | awk -F' ' '{print $2}') - # basic check of format - if [ ${#p_url} -ge 1 ] && [ ${#p_name} -ge 1 ] - then - # install addon - util/add_theme_repo $line - util/update_theme_repo $p_name # not sure if this line is neccessary - echo "util/update_theme_repo $p_name" >> $plugins_update - else - print_info "skipping installation of a theme from file $theme_install - something wrong with format in line: $line" - fi - done < "$theme_install" - print_info "installed hubzilla plugins and themes" -} - function rewrite_to_https { print_info "configuring apache to redirect http to httpS ..." htaccessfile=/var/www/html/.htaccess @@ -788,12 +710,11 @@ echo " fi" >> /var/www/$hubzilladaily echo "fi" >> /var/www/$hubzilladaily echo "if [ \$device_mounted == 0 ]" >> /var/www/$hubzilladaily echo "then" >> /var/www/$hubzilladaily -echo " echo \"device could not be mounted $backup_device_name. Using internal disk for backup...\"" >> /var/www/$hubzilladaily -echo " rsnapshot -c $snapshotconfig alpha" >> /var/www/$hubzilladaily +echo " echo \"device could not be mounted $backup_device_name. No backup written.\"" >> /var/www/$hubzilladaily echo "fi" >> /var/www/$hubzilladaily echo "#" >> /var/www/$hubzilladaily echo "echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily -echo "du -h /var/cache/rsnapshot/ | grep mysql/hubzilla" >> /var/www/$hubzilladaily +echo "du -h /var/lib/mysql/ | grep mysql/hubzilla" >> /var/www/$hubzilladaily echo "#" >> /var/www/$hubzilladaily echo "# update" >> /var/www/$hubzilladaily echo "echo \"\$(date) - updating dehydrated...\"" >> /var/www/$hubzilladaily diff --git a/.travis.yml b/.travis.yml index 41b480cf9..94c7b4226 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,6 +39,7 @@ addons: php: - '7.0' - '7.1' + - '7.2' # HHVM does not fulfil PHPUnit platform requirements as being compatible with PHP7 yet #- 'hhvm' @@ -69,7 +70,7 @@ matrix: mariadb: '10.1' # PHP7.1, PostgreSQL 9.6 - php: '7.1' - env: DB=pgsql POSTGRESQL_VERSION=9.6 + env: DB=pgsql POSTGRESQL_VERSION=9.6 PHPUNITFILE=phpunit-pgsql.xml # Use newer postgres than 9.2 default addons: postgresql: '9.6' @@ -125,15 +126,19 @@ before_script: - if [[ "$DB" == "pgsql" ]]; then ./tests/travis/prepare_pgsql.sh; fi # omitting "script:" will default to phpunit -script: ./vendor/bin/phpunit $PHPUCOV -c tests/phpunit-$DB.xml +script: + - ./vendor/bin/phpunit $PHPUCOV -c tests/$PHPUNITFILE after_success: - # Generate API documentation and deploy it to gh-pages + - cat tests/results/testdox.txt + # Generate API documentation and prepare for deployment - ./tests/travis/gen_apidocs.sh -#after_failure: +after_failure: + - cat tests/results/testdox.txt # Deploying release and API documentation to GitHub -#before_deploy: +before_deploy: + - if [[ "$CODECOV" == "1" ]]; then zip -9 -r -q tests/hubzilla-testresults.zip tests/results; fi deploy: - provider: pages skip_cleanup: true @@ -152,6 +157,15 @@ deploy: repo: redmatrix/hubzilla tags: true condition: '(-n "$GH_TOKEN") && ("$TRAVIS_JOB_NUMBER" == "${TRAVIS_BUILD_NUMBER}.1")' + # add code coverage and test results to release + - provider: releases + skip_cleanup: true + api_key: $GH_TOKEN + file: 'tests/hubzilla-testresults.zip' + on: + repo: redmatrix/hubzilla + tags: true + condition: '(-n "$GH_TOKEN") && ("$CODECOV" == "1")' #after_deploy: #after_script: diff --git a/CHANGELOG b/CHANGELOG index 210123623..6e540a04d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,147 @@ +Hubzilla 3.0 (2018-01-09) + - Updated homeinstall script + - Sort cloud directory by 1. is_dir and 2. name + - Document that imagick calls/execs ffmpeg for mp4 video thumbnails + - Use pipe_stream() instead of file_{get, put}_contents() in attach_store() + - Make homeinstall script ready for Debian 9 + - Add url and headings to bbco_autocomplete() + - Remove additional linebreaks after headings + - html2bbcode: use headings bbcode for headings + - Don't zidify all permalinks, only zot permalinks + - Make remote homelink link to the home host and not to the home channel + - Auto promote beginner (techlevel 0) accounts to level 1 after they show signs of active participation. + - Go back to including the photo thumbnail data in the export file. + - Improvements to file import/export + - Default value for xlink_rating_text + - Implement IMoveTarget and recursive file/directory move/rename - github issue #680 + - Synchronise an attach_move operation to clones + - Provide a themed page with an error notification on errors instead of an obtuse XML error structure in mod cloud + - Disallow backslashes in wiki and wiki-page names + - We only require one update module. The rest are superfluous. + - Render installable elements as buttons instead of links + - Implement chunked uploads for photos page + - Remove warning for large files on cloud upload + - Add a filter for notification to show new posts only + - Implement chunked uploads for cloud + - Use httpsig auth for getfile + - Load the profile images in the custom acl selector only if we actually need them + - Rework liveUpdate() and notificationsUpdate() (aka ping) to first do the liveUpdate and when this is done only do the ping once. + - Don't include invisible "update activities" in category widget + - Default profile assign + - Provide system config option for minimum registration age. + - Remove deprecated $a argument from advanced_profile() + - Change to bbcode calling parameters + - Extra checking of server headers in upload functions + - Provide a handler for chunked uploads in mod file_upload + - Optional divider between item header and body + - Allow toggle to SMBC scaling mode. + - Add thumbnail hook + - Implement SVG thumbnails and expose security setting + - Implement video thumbnail generator + - Implement pdf thumbnails + - Implement thumbnail generator for epubs + - Make browser history buttons work with ajax calls in mod display and hq + - Implement tile view for mod cloud (read only) + - Add mp3 audio thumbnail generator + - Set display_path for photo_upload from the DAV File interface + - Provide a generalised interface for thumbnail generators to support various content types + - Add ID3Parser library. + - Text thumbnails in cloud tile mode + - Revisit media breakpoints - do not switch to mobile view to early. + - Add French to help pages language dropdown selector + - Inroduce the HQ module - an alternative landing page for hubzilla + - Strip author name from notify messages in notifications - github issue #911 + - Remove column item.diaspora_meta + - Provide ability to pin apps to navbar from mod apps + - Add private forums to forum widget + - Move notifications style to widgets.css + - Sort out a few more large image upload issues + - Move notifications full-screen handling to notifications widget + - Move mailhost settings from plugin to core + - Sort combined private mail conversations by latest updated conversation instead of created parent + - Filter atokens on acl search + - Allow a site to block (public) the directory separately from other resources. + - Improve removed_channel final cleanup - github issue #386 + - Cleanup of upload_to_comments( + - Dedicate the first click to slideup the cover again but make sure the nav buttons remain functional + - Set os_syspath in DAV file put operation so that photos will scale correctly. + - Unit tests for Zotlabs\Access classes + - Bring back tabindex to submit comments + - attach.php minor cleanup and doc + - Allow cloud filenames to include ampersands without messing up auth tokens (zid, owt, and zat, and the constant placeholder 'f=') + - Provide short localised summary for likes that will end up in displayed notifications + - Improving Doxygen documentation. + - Update item_normal() to not include ACTIVITY_OBJ_FILE obj_type + - Sort out issues with pubstream item interactions + - Don't perform zot_refresh on dead sites unless $force is set + - Do not send message_list responses to dead sites (this delivery method bypassed the notifier) + - Support for netselect query + - Add another delivery control parameter (queue threshold) + - Add some documentation about shareable widgets + - Allow plugin class widgets + - Some more work on unit tests + - Encrypt the owa token + - Bring back the markdown post feature + - We call Theme:url() statically, make it also static. + - Table structure for pseudo or proxy channels (pchan) + + Bugfixes + - Fix sync non-default profile photo changes to clones - github issue #113 + - Fix prev/next buttons on connedit can show deleted connections - github issue #673 + - Fix affinity widget settings + - Fix dupe bug in content hooks - github issue #943 + - Fix directory keywords returned from dir_tagadelic() in standalone mode + - Fix argument warning when arguments are correct in util/dcp + - Fix issue with long filenames in mod cloud + - Fix misc. issues with new 'insert photo from photo album' github issue #475 + - Fix regression in channel sources delivery + - Fix loading of theme-specific widgets + - Fix unable to add wiki pages with spaces + - Fix mod display and others that require a non-zero profile_uid for updates + - Fix various PHP 7.2 issues + - Fix typo in HTTPSig + - Fix pagetitle lost importing a pdl element from conversation + - Fix js warning - getelementbyid (id doesn't exist) + - Fix some pubstream on/off weirdness + - Fix default addressbook has no name - github issue #921 + - Fix double html ids in caldav widget if more than one sharee + - Fix regression in cdav calendar widget + - Fix sync packet not generated when deleting a file using the web browser interface + - Fix album cover thumb generator + - Fix like-button for images - github issue #826 + - Fix typo - github issue #910 + - Fix issue with group_rmv() + - Fix php warnings on photo delete + - Fix some conflicts between private tags and forum tags + - Fix some schema issues + - Fix wiki pages not updating after creating new page + - Fix a PHP warning in Permissions::FilledPerms() + - Fix unicode characters in urls tripping up url regexes - github issue #901 + - Fix second half of github issue #893 + - Fix common connections on suggestion page showing wildly different results than remote profile, and is consistently off by one + - Fix cloud redirects with owt tokens + - Fix issues with diaspora xchans + - Fix memory overflow trying to delete a connection with a very high noise to signal ratio + - Fix sql error in page module + - Fix unstar + + Plugins/Addon + Diaspora: fix 'view full size' photo link - core github issue #947 + Diaspora: implement recent changes in diaspora account_migration spec + GNU-Social: fix uploading a photo to a post results in double post - github issue 75 + GNU-Social: fix gnusoc plugin not respecting delayed delivery - github issue 74 + Pubcrawl: fix PHP warning + Diaspora: remove garbage from magic envelope + Diaspora: fix permalinks for zot reshares + New addon: hzfiles - sync files across hubzilla servers + Fix various PHP 7.2 issues + Remove Firefox social plugin - it was deprecated and removed in firefox version 57 + Diaspora: unset id and parent for local comments + Pubsubhubbub: set interactive flag to avoid delivery killing if block_public is enabled + Mailhost addon moved to core + Remove js_upload addon + + Hubzilla 2.8.1 (2017-11-11) - Rename channel app events to calendar and add nav_set_selected() to /cal - Load notifications links to /display via ajax if we are already in /display @@ -31,7 +175,6 @@ Hubzilla 2.8.1 (2017-11-11) Diaspora: fix comments from unknown persons not accepted if allow public comments is enabled - github issue #68 XMPP: fix php warning - Hubzilla 2.8 (2017-10-25) - Redirect to be moderated items to /moderate - Update notifications if notifications area remains open diff --git a/DEVELOPERS b/DEVELOPERS new file mode 100644 index 000000000..8201f9921 --- /dev/null +++ b/DEVELOPERS @@ -0,0 +1,37 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/Zotlabs/Access/PermissionRoles.php b/Zotlabs/Access/PermissionRoles.php index 49d478c5c..b335bf825 100644 --- a/Zotlabs/Access/PermissionRoles.php +++ b/Zotlabs/Access/PermissionRoles.php @@ -1,12 +1,21 @@ [ - 'social' => t('Social - Mostly Public'), - 'social_restricted' => t('Social - Restricted'), + 'social' => t('Social - Mostly Public'), + 'social_restricted' => t('Social - Restricted'), 'social_private' => t('Social - Private') ], t('Community Forum') => [ - 'forum' => t('Forum - Mostly Public'), - 'forum_restricted' => t('Forum - Restricted'), + 'forum' => t('Forum - Mostly Public'), + 'forum_restricted' => t('Forum - Restricted'), 'forum_private' => t('Forum - Private') ], t('Feed Republish') => [ - 'feed' => t('Feed - Mostly Public'), + 'feed' => t('Feed - Mostly Public'), 'feed_restricted' => t('Feed - Restricted') ], t('Special Purpose') => [ - 'soapbox' => t('Special - Celebrity/Soapbox'), + 'soapbox' => t('Special - Celebrity/Soapbox'), 'repository' => t('Special - Group Repository') ], t('Other') => [ 'custom' => t('Custom/Expert Mode') ] - ]; - return $roles; + return $roles; } } \ No newline at end of file diff --git a/Zotlabs/Access/Permissions.php b/Zotlabs/Access/Permissions.php index 62c4af0ff..bca40a9c1 100644 --- a/Zotlabs/Access/Permissions.php +++ b/Zotlabs/Access/Permissions.php @@ -33,19 +33,22 @@ use Zotlabs\Lib as Zlib; */ class Permissions { + /** + * @brief Permissions version. + * + * This must match the version in PermissionRoles.php before permission updates can run. + * + * @return number + */ static public function version() { - // This must match the version in PermissionRoles.php before permission updates can run. return 2; } /** * @brief Return an array with Permissions. * - * @hooks permissions_list - * * \e array \b permissions - * * \e string \b filter - * @param string $filter (optional) only passed to hook permission_list - * @return Associative array with permissions and short description. + * @param string $filter (optional) only passed to hook permissions_list + * @return array Associative array with permissions and short description. */ static public function Perms($filter = '') { @@ -74,6 +77,11 @@ class Permissions { 'permissions' => $perms, 'filter' => $filter ]; + /** + * @hooks permissions_list + * * \e array \b permissions + * * \e string \b filter + */ call_hooks('permissions_list', $x); return($x['permissions']); @@ -84,9 +92,7 @@ class Permissions { * * e.g. you must be authenticated. * - * @hooks write_perms - * * \e array \b permissions - * @return Associative array with permissions and short description. + * @return array Associative array with permissions and short description. */ static public function BlockedAnonPerms() { @@ -99,6 +105,10 @@ class Permissions { } $x = ['permissions' => $res]; + /** + * @hooks write_perms + * * \e array \b permissions + */ call_hooks('write_perms', $x); return($x['permissions']); @@ -117,6 +127,7 @@ class Permissions { static public function FilledPerms($arr) { if(is_null($arr)) { btlogger('FilledPerms: null'); + $arr = []; } $everything = self::Perms(); @@ -138,7 +149,7 @@ class Permissions { * to [ 0 => ['name' => 'view_stream', 'value' => 1], ... ] * * @param array $arr associative perms array 'view_stream' => 1 - * @return Indexed array with elements that look like + * @return array Indexed array with elements that look like * * \e string \b name the perm name (e.g. view_stream) * * \e int \b value the value of the perm (e.g. 1) */ @@ -197,11 +208,10 @@ class Permissions { * @brief * * @param int $channel_id A channel id - * @return associative array + * @return array Associative array with * * \e array \b perms Permission array * * \e int \b automatic 0 or 1 */ - static public function connect_perms($channel_id) { $my_perms = []; diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index 65edbedfa..01c43262a 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -78,7 +78,7 @@ class Cron { // channels and sites that quietly vanished and prevent the directory from accumulating stale // or dead entries. - $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s", + $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s and channel_removed = 0", db_utcnow(), db_quoteinterval('30 DAY') ); diff --git a/Zotlabs/Daemon/Cron_weekly.php b/Zotlabs/Daemon/Cron_weekly.php index 5b185f475..d44400767 100644 --- a/Zotlabs/Daemon/Cron_weekly.php +++ b/Zotlabs/Daemon/Cron_weekly.php @@ -21,6 +21,21 @@ class Cron_weekly { mark_orphan_hubsxchans(); + // Find channels that were removed in the last three weeks, but + // haven't been finally cleaned up. These should be older than 10 + // days to ensure that "purgeall" messages have gone out or bounced + // or timed out. + + $r = q("select channel_id from channel where channel_removed = 1 and + channel_deleted > %s - INTERVAL %s and channel_deleted < %s - INTERVAL %s", + db_utcnow(), db_quoteinterval('21 DAY'), + db_utcnow(), db_quoteinterval('10 DAY') + ); + if($r) { + foreach($r as $rv) { + channel_remove_final($rv['channel_id']); + } + } // get rid of really old poco records diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index d0175549b..ca6a7c08a 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -426,8 +426,10 @@ class Notifier { logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); stringify_array_elms($recipients); - if(! $recipients) + if(! $recipients) { + logger('no recipients'); return; + } // logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG); diff --git a/Zotlabs/Daemon/Queue.php b/Zotlabs/Daemon/Queue.php index 11cbe4494..17d150250 100644 --- a/Zotlabs/Daemon/Queue.php +++ b/Zotlabs/Daemon/Queue.php @@ -12,6 +12,7 @@ class Queue { require_once('include/items.php'); require_once('include/bbcode.php'); + if(argc() > 1) $queue_id = argv(1); else diff --git a/Zotlabs/Daemon/Ratenotif.php b/Zotlabs/Daemon/Ratenotif.php index a94b89004..c7bf79854 100644 --- a/Zotlabs/Daemon/Ratenotif.php +++ b/Zotlabs/Daemon/Ratenotif.php @@ -88,6 +88,14 @@ class Ratenotif { 'msg' => json_encode($encoded_item) )); + + $x = q("select count(outq_hash) as total from outq where outq_delivered = 0"); + if(intval($x[0]['total']) > intval(get_config('system','force_queue_threshold',300))) { + logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); + update_queue_item($hash); + continue; + } + $deliver[] = $hash; if(count($deliver) >= $deliveries_per_process) { diff --git a/Zotlabs/Daemon/Thumbnail.php b/Zotlabs/Daemon/Thumbnail.php new file mode 100644 index 000000000..e1f17c304 --- /dev/null +++ b/Zotlabs/Daemon/Thumbnail.php @@ -0,0 +1,78 @@ + $attach, + 'preview_style' => $preview_style, + 'preview_width' => $preview_width, + 'preview_height' => $preview_height, + 'thumbnail' => null + ]; + + /** + * @hooks thumbnail + * * \e array \b attach + * * \e int \b preview_style + * * \e int \b preview_width + * * \e int \b preview_height + * * \e string \b thumbnail + */ + + call_hooks('thumbnail',$p); + if($p['thumbnail']) { + return; + } + + + $default_controller = null; + + $files = glob('Zotlabs/Thumbs/*.php'); + if($files) { + foreach($files as $f) { + $clsname = '\\Zotlabs\\Thumbs\\' . ucfirst(basename($f,'.php')); + if(class_exists($clsname)) { + $x = new $clsname(); + if(method_exists($x,'Match')) { + $matched = $x->Match($attach['filetype']); + if($matched) { + $x->Thumb($attach,$preview_style,$preview_width,$preview_height); + } + } + if(method_exists($x,'MatchDefault')) { + $default_matched = $x->MatchDefault(substr($attach['filetype'],0,strpos($attach['filetype'],'/'))); + if($default_matched) { + $default_controller = $x; + } + } + } + } + } + if(($default_controller) + && ((! file_exists(dbunescbin($attach['content']) . '.thumb')) + || (filectime(dbunescbin($attach['content']) . 'thumb') < (time() - 60)))) { + $default_controller->Thumb($attach,$preview_style,$preview_width,$preview_height); + } + } +} diff --git a/Zotlabs/Extend/Hook.php b/Zotlabs/Extend/Hook.php index c6f9ea850..81260ead6 100644 --- a/Zotlabs/Extend/Hook.php +++ b/Zotlabs/Extend/Hook.php @@ -2,7 +2,12 @@ namespace Zotlabs\Extend; +use App; +/** + * @brief Hook class. + * + */ class Hook { static public function register($hook,$file,$function,$version = 1,$priority = 0) { @@ -64,11 +69,14 @@ class Hook { return $r; } - // unregister all hooks with this file component. - // Useful for addon upgrades where you want to clean out old interfaces. - + /** + * @brief Unregister all hooks with this file component. + * + * Useful for addon upgrades where you want to clean out old interfaces. + * + * @param string $file + */ static public function unregister_by_file($file) { - $r = q("DELETE FROM hook WHERE file = '%s' ", dbesc($file) ); @@ -76,7 +84,6 @@ class Hook { return $r; } - /** * @brief Inserts a hook into a page request. * @@ -98,7 +105,6 @@ class Hook { * @param int $priority * currently not implemented in this function, would require the hook array to be resorted */ - static public function insert($hook, $fn, $version = 0, $priority = 0) { if(is_array($fn)) { $fn = serialize($fn); diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php index 379e78a59..2e9bb0703 100644 --- a/Zotlabs/Lib/ActivityStreams.php +++ b/Zotlabs/Lib/ActivityStreams.php @@ -2,6 +2,11 @@ namespace Zotlabs\Lib; +/** + * @brief ActivityStreams class. + * + * Parses an ActivityStream JSON string. + */ class ActivityStreams { public $data; @@ -19,9 +24,16 @@ class ActivityStreams { public $recips = null; public $raw_recips = null; + /** + * @brief Constructor for ActivityStreams. + * + * Takes a JSON string as parameter, decodes it and sets up this object. + * + * @param string $string + */ function __construct($string) { - $this->data = json_decode($string,true); + $this->data = json_decode($string, true); if($this->data) { $this->valid = true; } @@ -50,6 +62,11 @@ class ActivityStreams { } } + /** + * @brief Return if instantiated ActivityStream is valid. + * + * @return boolean Return true if the JSON string could be decoded. + */ function is_valid() { return $this->valid; } @@ -58,18 +75,26 @@ class ActivityStreams { $this->saved_recips = $arr; } - function collect_recips($base = '',$namespace = '') { + /** + * @brief Collects all recipients. + * + * @param string $base + * @param string $namespace (optional) default empty + * @return array + */ + function collect_recips($base = '', $namespace = '') { $x = []; - $fields = [ 'to','cc','bto','bcc','audience']; + $fields = [ 'to', 'cc', 'bto', 'bcc', 'audience']; foreach($fields as $f) { - $y = $this->get_compound_property($f,$base,$namespace); + $y = $this->get_compound_property($f, $base, $namespace); if($y) { - $x = array_merge($x,$y); + $x = array_merge($x, $y); if(! is_array($this->raw_recips)) $this->raw_recips = []; + $this->raw_recips[$f] = $x; } - } + } // not yet ready for prime time // $x = $this->expand($x,$base,$namespace); return $x; @@ -96,23 +121,30 @@ class ActivityStreams { } } - // @fixme de-duplicate + /// @fixme de-duplicate return $ret; } - function get_namespace($base,$namespace) { + /** + * @brief + * + * @param array $base + * @param string $namespace if not set return empty string + * @return string|NULL + */ + function get_namespace($base, $namespace) { if(! $namespace) return ''; $key = null; - foreach( [ $this->data, $base ] as $b ) { if(! $b) continue; - if(array_key_exists('@context',$b)) { + + if(array_key_exists('@context', $b)) { if(is_array($b['@context'])) { foreach($b['@context'] as $ns) { if(is_array($ns)) { @@ -135,19 +167,35 @@ class ActivityStreams { } } } + return $key; } - - function get_property_obj($property,$base = '',$namespace = '' ) { - $prefix = $this->get_namespace($base,$namespace); + /** + * @brief + * + * @param string $property + * @param array $base (optional) + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ + function get_property_obj($property, $base = '', $namespace = '') { + $prefix = $this->get_namespace($base, $namespace); if($prefix === null) - return null; + return null; + $base = (($base) ? $base : $this->data); $propname = (($prefix) ? $prefix . ':' : '') . $property; - return ((array_key_exists($propname,$base)) ? $base[$propname] : null); + + return ((array_key_exists($propname, $base)) ? $base[$propname] : null); } + /** + * @brief Fetches a property from an URL. + * + * @param string $url + * @return NULL|mixed + */ function fetch_property($url) { $redirects = 0; if(! check_siteallowed($url)) { @@ -155,44 +203,70 @@ class ActivityStreams { return null; } - $x = z_fetch_url($url,true,$redirects, + $x = z_fetch_url($url, true, $redirects, ['headers' => [ 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/activity+json' ]]); if($x['success']) - return json_decode($x['body'],true); + return json_decode($x['body'], true); + return null; } - function get_compound_property($property,$base = '',$namespace = '') { - $x = $this->get_property_obj($property,$base,$namespace); + /** + * @brief + * + * @param string $property + * @param array $base + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ + function get_compound_property($property, $base = '', $namespace = '') { + $x = $this->get_property_obj($property, $base, $namespace); if($this->is_url($x)) { - $x = $this->fetch_property($x); + $x = $this->fetch_property($x); } + return $x; } + /** + * @brief Check if string starts with http. + * + * @param string $url + * @return boolean + */ function is_url($url) { - if(($url) && (! is_array($url)) && (strpos($url,'http') === 0)) { + if(($url) && (! is_array($url)) && (strpos($url, 'http') === 0)) { return true; } + return false; } - function get_primary_type($base = '',$namespace = '') { + /** + * @brief Gets the type property. + * + * @param array $base + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ + function get_primary_type($base = '', $namespace = '') { if(! $base) $base = $this->data; - $x = $this->get_property_obj('type',$base,$namespace); + + $x = $this->get_property_obj('type', $base, $namespace); if(is_array($x)) { foreach($x as $y) { - if(strpos($y,':') === false) { + if(strpos($y, ':') === false) { return $y; } } } + return $x; } function debug() { - $x = var_export($this,true); + $x = var_export($this, true); return $x; } diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index f13fbe362..457b85b62 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -352,7 +352,7 @@ class Apps { break; default: if($config) - $unset = ((get_config('system', $require[0]) == $require[1]) ? false : true); + $unset = ((get_config('system', $require[0]) === $require[1]) ? false : true); else $unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true); if($unset) @@ -401,11 +401,15 @@ class Apps { '$undelete' => ((local_channel() && $installed && $mode == 'edit') ? t('Undelete') : ''), '$deleted' => $papp['deleted'], '$feature' => (($papp['embed']) ? false : true), + '$pin' => (($papp['embed']) ? false : true), '$featured' => ((strpos($papp['categories'], 'nav_featured_app') === false) ? false : true), + '$pinned' => ((strpos($papp['categories'], 'nav_pinned_app') === false) ? false : true), '$navapps' => (($mode == 'nav') ? true : false), '$order' => (($mode == 'nav-order') ? true : false), '$add' => t('Add to app-tray'), - '$remove' => t('Remove from app-tray') + '$remove' => t('Remove from app-tray'), + '$add_nav' => t('Pin to navbar'), + '$remove_nav' => t('Unpin from navbar') )); } @@ -498,25 +502,27 @@ class Apps { } } - static public function app_feature($uid,$app) { + static public function app_feature($uid,$app,$term) { $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['guid']), intval($uid) ); - $x = q("select * from term where otype = %d and oid = %d and term = 'nav_featured_app' limit 1", + $x = q("select * from term where otype = %d and oid = %d and term = '%s' limit 1", intval(TERM_OBJ_APP), - intval($r[0]['id']) + intval($r[0]['id']), + dbesc($term) ); if($x) { - q("delete from term where otype = %d and oid = %d and term = 'nav_featured_app'", + q("delete from term where otype = %d and oid = %d and term = '%s'", intval(TERM_OBJ_APP), - intval($x[0]['oid']) + intval($x[0]['oid']), + dbesc($term) ); } else { - store_item_tag($uid,$r[0]['id'],TERM_OBJ_APP,TERM_CATEGORY,'nav_featured_app',escape_tags(z_root() . '/apps/?f=&cat=nav_featured_app')); + store_item_tag($uid, $r[0]['id'], TERM_OBJ_APP, TERM_CATEGORY, $term, escape_tags(z_root() . '/apps/?f=&cat=' . $term)); } } @@ -531,16 +537,27 @@ class Apps { } - static public function app_list($uid, $deleted = false, $cat = '') { + static public function app_list($uid, $deleted = false, $cats = []) { if($deleted) $sql_extra = ""; else $sql_extra = " and app_deleted = 0 "; - if($cat) { - $r = q("select oid from term where otype = %d and term = '%s'", - intval(TERM_OBJ_APP), - dbesc($cat) + if($cats) { + + $cat_sql_extra = " and ( "; + + foreach($cats as $cat) { + if(strpos($cat_sql_extra, 'term')) + $cat_sql_extra .= "or "; + + $cat_sql_extra .= "term = '" . dbesc($cat) . "' "; + } + + $cat_sql_extra .= ") "; + + $r = q("select oid from term where otype = %d $cat_sql_extra", + intval(TERM_OBJ_APP) ); if(! $r) return $r; @@ -616,7 +633,7 @@ class Apps { static function moveup($uid,$guid) { $syslist = array(); - $list = self::app_list($uid, false, 'nav_featured_app'); + $list = self::app_list($uid, false, ['nav_featured_app', 'nav_pinned_app']); if($list) { foreach($list as $li) { $syslist[] = self::app_encode($li); @@ -657,7 +674,7 @@ class Apps { static function movedown($uid,$guid) { $syslist = array(); - $list = self::app_list($uid, false, 'nav_featured_app'); + $list = self::app_list($uid, false, ['nav_featured_app', 'nav_pinned_app']); if($list) { foreach($list as $li) { $syslist[] = self::app_encode($li); diff --git a/Zotlabs/Lib/Chatroom.php b/Zotlabs/Lib/Chatroom.php index e1a9a10b3..e762620ae 100644 --- a/Zotlabs/Lib/Chatroom.php +++ b/Zotlabs/Lib/Chatroom.php @@ -2,22 +2,18 @@ namespace Zotlabs\Lib; /** - * @brief Chat related functions. + * @brief A class with chatroom related static methods. */ - - - class Chatroom { /** * @brief Creates a chatroom. * * @param array $channel * @param array $arr - * @return An associative array containing: - * - success: A boolean - * - message: (optional) A string + * @return array An associative array containing: + * * \e boolean \b success - A boolean success status + * * \e string \b message - (optional) A string */ - static public function create($channel, $arr) { $ret = array('success' => false); @@ -150,8 +146,8 @@ class Chatroom { } if(intval($x[0]['cr_expire'])) { - $r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d", - db_utcnow(), + $r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d", + db_utcnow(), db_quoteinterval( intval($x[0]['cr_expire']) . ' MINUTE' ), intval($x[0]['cr_id']) ); @@ -225,10 +221,16 @@ class Chatroom { } /** - * create a chat message via API. + * @brief Create a chat message via API. + * * It is the caller's responsibility to enter the room. - */ - + * + * @param int $uid + * @param int $room_id + * @param string $xchan + * @param string $text + * @return array + */ static public function message($uid, $room_id, $xchan, $text) { $ret = array('success' => false); @@ -245,12 +247,18 @@ class Chatroom { if(! $r) return $ret; - $arr = array( + $arr = [ 'chat_room' => $room_id, 'chat_xchan' => $xchan, 'chat_text' => $text - ); - + ]; + /** + * @hooks chat_message + * Called to create a chat message. + * * \e int \b chat_room + * * \e string \b chat_xchan + * * \e string \b chat_text + */ call_hooks('chat_message', $arr); $x = q("insert into chat ( chat_room, chat_xchan, created, chat_text ) diff --git a/Zotlabs/Lib/Config.php b/Zotlabs/Lib/Config.php index 6e042feba..f9f22ba3a 100644 --- a/Zotlabs/Lib/Config.php +++ b/Zotlabs/Lib/Config.php @@ -1,4 +1,4 @@ - relative_date($item['created']), 'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'), 'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])), + 'notify_id' => 'undefined', + 'thread_top' => (($item['item_thread_top']) ? true : false), 'message' => strip_tags(bbcode($itemem_text)) ); diff --git a/Zotlabs/Lib/NativeWikiPage.php b/Zotlabs/Lib/NativeWikiPage.php index 209a5ef3c..919c51276 100644 --- a/Zotlabs/Lib/NativeWikiPage.php +++ b/Zotlabs/Lib/NativeWikiPage.php @@ -68,6 +68,9 @@ class NativeWikiPage { return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); } + // backslashes won't work well in the javascript functions + $name = str_replace('\\','',$name); + // create an empty activity $arr = []; @@ -351,6 +354,7 @@ class NativeWikiPage { // fetch the most recently saved revision. $item = self::load_page($arr); + if(! $item) { return array('message' => t('Page not found'), 'success' => false); } diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php index 2a0b18aac..ec0792ce1 100644 --- a/Zotlabs/Lib/PConfig.php +++ b/Zotlabs/Lib/PConfig.php @@ -1,8 +1,21 @@ -PConfig is used for channel specific configurations and takes a + * channel_id as identifier. It stores for example which features are + * enabled per channel. The storage is of size MEDIUMTEXT. + * + * @code{.php}$var = Zotlabs\Lib\PConfig::Get('uid', 'category', 'key'); + * // with default value for non existent key + * $var = Zotlabs\Lib\PConfig::Get('uid', 'category', 'unsetkey', 'defaultvalue');@endcode + * + * The old (deprecated?) way to access a PConfig value is: + * @code{.php}$var = get_pconfig(local_channel(), 'category', 'key');@endcode + */ class PConfig { /** @@ -13,9 +26,8 @@ class PConfig { * * @param string $uid * The channel_id - * @return void|false Nothing or false if $uid is false + * @return void|false Nothing or false if $uid is null or false */ - static public function Load($uid) { if(is_null($uid) || $uid === false) return false; @@ -64,11 +76,11 @@ class PConfig { * The category of the configuration value * @param string $key * The configuration key to query - * @param boolean $instore (deprecated, without function) + * @param mixed $default (optional, default false) + * Default value to return if key does not exist * @return mixed Stored value or false if it does not exist */ - - static public function Get($uid,$family,$key,$default = false) { + static public function Get($uid, $family, $key, $default = false) { if(is_null($uid) || $uid === false) return $default; @@ -79,11 +91,10 @@ class PConfig { if((! array_key_exists($family, \App::$config[$uid])) || (! array_key_exists($key, \App::$config[$uid][$family]))) return $default; - return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key])) + return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key])) ? unserialize(\App::$config[$uid][$family][$key]) : \App::$config[$uid][$family][$key] ); - } /** @@ -102,12 +113,11 @@ class PConfig { * The value to store * @return mixed Stored $value or false */ - static public function Set($uid, $family, $key, $value) { - // this catches subtle errors where this function has been called + // this catches subtle errors where this function has been called // with local_channel() when not logged in (which returns false) - // and throws an error in array_key_exists below. + // and throws an error in array_key_exists below. // we provide a function backtrace in the logs so that we can find // and fix the calling function. @@ -132,7 +142,6 @@ class PConfig { dbesc($key), dbesc($dbvalue) ); - } else { @@ -142,7 +151,6 @@ class PConfig { dbesc($family), dbesc($key) ); - } // keep a separate copy for all variables which were @@ -178,7 +186,6 @@ class PConfig { * The configuration key to delete * @return mixed */ - static public function Delete($uid, $family, $key) { if(is_null($uid) || $uid === false) @@ -186,12 +193,12 @@ class PConfig { $ret = false; - if(array_key_exists($uid,\App::$config) - && is_array(\App::$config['uid']) - && array_key_exists($family,\App::$config['uid']) + if(array_key_exists($uid,\App::$config) + && is_array(\App::$config['uid']) + && array_key_exists($family,\App::$config['uid']) && array_key_exists($key, \App::$config[$uid][$family])) unset(\App::$config[$uid][$family][$key]); - + $ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'", intval($uid), dbesc($family), @@ -202,4 +209,3 @@ class PConfig { } } - \ No newline at end of file diff --git a/Zotlabs/Lib/SConfig.php b/Zotlabs/Lib/SConfig.php index ca0d133b2..ab6f49025 100644 --- a/Zotlabs/Lib/SConfig.php +++ b/Zotlabs/Lib/SConfig.php @@ -2,8 +2,11 @@ namespace Zotlabs\Lib; -// account configuration storage is built on top of the under-utilised xconfig - +/** + * @brief Account configuration storage is built on top of the under-utilised xconfig. + * + * @see XConfig + */ class SConfig { static public function Load($server_id) { diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 67a507025..748edcdb7 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -38,7 +38,7 @@ class ThreadItem { $this->toplevel = ($this->get_id() == $this->get_data_value('parent')); // Prepare the children - if(count($data['children'])) { + if($data['children']) { foreach($data['children'] as $item) { /* @@ -105,7 +105,17 @@ class ThreadItem { $mode = $conv->get_mode(); - $edlink = (($item['item_type'] == ITEM_TYPE_CARD) ? 'card_edit' : 'editpost'); + switch($item['item_type']) { + case ITEM_TYPE_CARD: + $edlink = 'card_edit'; + break; + case ITEM_TYPE_ARTICLE: + $edlink = 'article_edit'; + break; + default: + $edlink = 'editpost'; + break; + } if(local_channel() && $observer['xchan_hash'] === $item['author_xchan']) $edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit')); @@ -186,7 +196,7 @@ class ThreadItem { $like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : ''); $like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : ''); - if (count($like_list) > MAX_LIKERS) { + if (($like_list) && (count($like_list) > MAX_LIKERS)) { $like_list_part = array_slice($like_list, 0, MAX_LIKERS); array_push($like_list_part, '' . t('View all') . ''); } else { @@ -198,7 +208,7 @@ class ThreadItem { $dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : ''); $dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : ''); $dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun'); - if (count($dislike_list) > MAX_LIKERS) { + if (($dislike_list) && (count($dislike_list) > MAX_LIKERS)) { $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS); array_push($dislike_list_part, '' . t('View all') . ''); } else { @@ -303,7 +313,7 @@ class ThreadItem { $comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children ); $list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : ''); - + @@ -360,6 +370,7 @@ class ThreadItem { 'unverified' => $unverified, 'forged' => $forged, 'location' => $location, + 'divider' => get_pconfig($conv->get_profile_owner(),'system','item_divider'), 'attend_label' => t('Attend'), 'attend_title' => t('Attendance Options'), 'vote_label' => t('Vote'), diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php index 436723f8c..d0c964149 100644 --- a/Zotlabs/Lib/ThreadStream.php +++ b/Zotlabs/Lib/ThreadStream.php @@ -54,6 +54,14 @@ class ThreadStream { $this->profile_owner = local_channel(); $this->writable = true; break; + case 'pubstream': + $this->profile_owner = local_channel(); + $this->writable = ((local_channel()) ? true : false); + break; + case 'hq': + $this->profile_owner = local_channel(); + $this->writable = true; + break; case 'channel': $this->profile_owner = \App::$profile['profile_uid']; $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); @@ -63,6 +71,11 @@ class ThreadStream { $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); $this->reload = $_SESSION['return_url']; break; + case 'articles': + $this->profile_owner = \App::$profile['profile_uid']; + $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); + $this->reload = $_SESSION['return_url']; + break; case 'display': // in this mode we set profile_owner after initialisation (from conversation()) and then // pull some trickery which allows us to re-invoke this function afterward @@ -179,6 +192,10 @@ class ThreadStream { $item->set_commentable(can_comment_on_post($ob_hash,$item->data)); } } + if($this->mode === 'pubstream' && (! local_channel())) { + $item->set_commentable(false); + } + require_once('include/channel.php'); $item->set_conversation($this); diff --git a/Zotlabs/Lib/XConfig.php b/Zotlabs/Lib/XConfig.php index bf78c360f..c5a108ac9 100644 --- a/Zotlabs/Lib/XConfig.php +++ b/Zotlabs/Lib/XConfig.php @@ -2,7 +2,26 @@ namespace Zotlabs\Lib; - +/** + * @brief Class for handling observer's config. + * + * XConfig is comparable to PConfig, except that it uses xchan + * (an observer hash) as an identifier. + * + * XConfig is used for observer specific configurations and takes a + * xchan as identifier. + * The storage is of size MEDIUMTEXT. + * + * @code{.php}$var = Zotlabs\Lib\XConfig::Get('xchan', 'category', 'key'); + * // with default value for non existent key + * $var = Zotlabs\Lib\XConfig::Get('xchan', 'category', 'unsetkey', 'defaultvalue');@endcode + * + * The old (deprecated?) way to access a XConfig value is: + * @code{.php}$observer = App::get_observer_hash(); + * if ($observer) { + * $var = get_xconfig($observer, 'category', 'key'); + * }@endcode + */ class XConfig { /** @@ -15,7 +34,6 @@ class XConfig { * The observer's hash * @return void|false Returns false if xchan is not set */ - static public function Load($xchan) { if(! $xchan) @@ -56,9 +74,9 @@ class XConfig { * The category of the configuration value * @param string $key * The configuration key to query + * @param boolean $default (optional) default false * @return mixed Stored $value or false if it does not exist */ - static public function Get($xchan, $family, $key, $default = false) { if(! $xchan) @@ -70,7 +88,7 @@ class XConfig { if((! array_key_exists($family, \App::$config[$xchan])) || (! array_key_exists($key, \App::$config[$xchan][$family]))) return $default; - return ((! is_array(\App::$config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$xchan][$family][$key])) + return ((! is_array(\App::$config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$xchan][$family][$key])) ? unserialize(\App::$config[$xchan][$family][$key]) : \App::$config[$xchan][$family][$key] ); @@ -82,7 +100,6 @@ class XConfig { * Stores a config value ($value) in the category ($family) under the key ($key) * for the observer's $xchan hash. * - * * @param string $xchan * The observer's hash * @param string $family @@ -93,7 +110,6 @@ class XConfig { * The value to store * @return mixed Stored $value or false */ - static public function Set($xchan, $family, $key, $value) { // manage array value @@ -106,7 +122,7 @@ class XConfig { if(! array_key_exists($family, \App::$config[$xchan])) \App::$config[$xchan][$family] = array(); - $ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' ) ", + $ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' )", dbesc($xchan), dbesc($family), dbesc($key), @@ -126,6 +142,7 @@ class XConfig { if($ret) return $value; + return $ret; } @@ -143,11 +160,11 @@ class XConfig { * The configuration key to delete * @return mixed */ - static public function Delete($xchan, $family, $key) { if(x(\App::$config[$xchan][$family], $key)) unset(\App::$config[$xchan][$family][$key]); + $ret = q("DELETE FROM xconfig WHERE xchan = '%s' AND cat = '%s' AND k = '%s'", dbesc($xchan), dbesc($family), diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index e164875e8..ad1c8b8cd 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -176,11 +176,18 @@ class Acl extends \Zotlabs\Web\Controller { $extra_channels_sql = " OR (abook_channel IN ($extra_channels_sql)) and abook_hidden = 0 "; - // Add atokens belonging to the local channel @TODO restrict by search + // Add atokens belonging to the local channel + + if($search) { + $sql_extra_atoken = "AND ( atoken_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . ") "; + } + else { + $sql_extra_atoken = ''; + } $r2 = null; - $r1 = q("select * from atoken where atoken_uid = %d", + $r1 = q("select * from atoken where atoken_uid = %d $sql_extra_atoken", intval(local_channel()) ); diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index d3d058c53..a9db1ad55 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -61,8 +61,10 @@ class Site { $maxloadavg = ((x($_POST,'maxloadavg')) ? intval(trim($_POST['maxloadavg'])) : 50); $feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0); $verify_email = ((x($_POST,'verify_email')) ? 1 : 0); - $techlevel_lock = ((x($_POST,'techlock')) ? intval($_POST['techlock']) : 0); - $imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : ''); + $techlevel_lock = ((x($_POST,'techlock')) ? intval($_POST['techlock']) : 0); + $imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : ''); + $thumbnail_security = ((x($_POST,'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0); + $force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 300); $techlevel = null; if(array_key_exists('techlevel', $_POST)) @@ -84,7 +86,7 @@ class Site { set_config('system', 'from_email', $from_email); set_config('system', 'from_email_name' , $from_email_name); set_config('system', 'imagick_convert_path' , $imagick_path); - + set_config('system', 'thumbnail_security' , $thumbnail_security); set_config('system', 'techlevel_lock', $techlevel_lock); @@ -128,6 +130,7 @@ class Site { set_config('system','allowed_sites', $allowed_sites); set_config('system','publish_all', $force_publish); set_config('system','disable_discover_tab', $disable_discover_tab); + set_config('system','force_queue_threshold', $force_queue); if ($global_directory == '') { del_config('system', 'directory_submit_url'); } else { @@ -248,6 +251,7 @@ class Site { ); $discover_tab = get_config('system','disable_discover_tab'); + // $disable public streams by default if($discover_tab === false) $discover_tab = 1; @@ -318,8 +322,10 @@ class Site { '$timeout' => array('timeout', t("Network timeout"), (x(get_config('system','curl_timeout'))?get_config('system','curl_timeout'):60), t("Value is in seconds. Set to 0 for unlimited (not recommended).")), '$delivery_interval' => array('delivery_interval', t("Delivery interval"), (x(get_config('system','delivery_interval'))?get_config('system','delivery_interval'):2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")), '$delivery_batch_count' => array('delivery_batch_count', t('Deliveries per process'),(x(get_config('system','delivery_batch_count'))?get_config('system','delivery_batch_count'):1), t("Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.")), + '$force_queue' => array('force_queue', t("Queue Threshold"), get_config('system','force_queue_threshold',300), t("Always defer immediate delivery if queue contains more than this number of entries.")), '$poll_interval' => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")), '$imagick_path' => array('imagick_path', t("Path to ImageMagick convert program"), get_config('system','imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert")), + '$thumbnail_security' => array('thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.")), '$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")), '$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days')), t('0 for no expiration of imported content')), '$form_security_token' => get_form_security_token("admin_site"), diff --git a/Zotlabs/Module/Appman.php b/Zotlabs/Module/Appman.php index 5c0667357..64d4628ae 100644 --- a/Zotlabs/Module/Appman.php +++ b/Zotlabs/Module/Appman.php @@ -64,7 +64,11 @@ class Appman extends \Zotlabs\Web\Controller { } if($_POST['feature']) { - Zlib\Apps::app_feature(local_channel(),$papp); + Zlib\Apps::app_feature(local_channel(), $papp, $_POST['feature']); + } + + if($_POST['pin']) { + Zlib\Apps::app_feature(local_channel(), $papp, $_POST['pin']); } if($_SESSION['return_url']) diff --git a/Zotlabs/Module/Apporder.php b/Zotlabs/Module/Apporder.php index 956548d1f..a9f66ba69 100644 --- a/Zotlabs/Module/Apporder.php +++ b/Zotlabs/Module/Apporder.php @@ -18,7 +18,7 @@ class Apporder extends \Zotlabs\Web\Controller { nav_set_selected('Order Apps'); $syslist = array(); - $list = Zlib\Apps::app_list(local_channel(), false, 'nav_featured_app'); + $list = Zlib\Apps::app_list(local_channel(), false, ['nav_featured_app', 'nav_pinned_app']); if($list) { foreach($list as $li) { $syslist[] = Zlib\Apps::app_encode($li); @@ -31,14 +31,20 @@ class Apporder extends \Zotlabs\Web\Controller { $syslist = Zlib\Apps::app_order(local_channel(),$syslist); foreach($syslist as $app) { - $nav_apps[] = Zlib\Apps::app_render($app,'nav-order'); + if(strpos($app['categories'],'nav_pinned_app') !== false) { + $navbar_apps[] = Zlib\Apps::app_render($app,'nav-order'); + } + else { + $nav_apps[] = Zlib\Apps::app_render($app,'nav-order'); + } } return replace_macros(get_markup_template('apporder.tpl'), [ - '$header' => t('Change Order of Navigation Apps'), - '$desc' => t('Use arrows to move the corresponding app up or down in the display list'), - '$nav_apps' => $nav_apps + '$header' => [t('Change Order of Pinned Navbar Apps'), t('Change Order of App Tray Apps')], + '$desc' => [t('Use arrows to move the corresponding app left (top) or right (bottom) in the navbar'), t('Use arrows to move the corresponding app up or down in the app tray')], + '$nav_apps' => $nav_apps, + '$navbar_apps' => $navbar_apps ] ); } diff --git a/Zotlabs/Module/Apps.php b/Zotlabs/Module/Apps.php index 2f61f2932..c672ea467 100644 --- a/Zotlabs/Module/Apps.php +++ b/Zotlabs/Module/Apps.php @@ -22,7 +22,8 @@ class Apps extends \Zotlabs\Web\Controller { if(local_channel()) { Zlib\Apps::import_system_apps(); $syslist = array(); - $list = Zlib\Apps::app_list(local_channel(), (($mode == 'edit') ? true : false), $_GET['cat']); + $cat = ((array_key_exists('cat',$_GET) && $_GET['cat']) ? [ escape_tags($_GET['cat']) ] : ''); + $list = Zlib\Apps::app_list(local_channel(), (($mode == 'edit') ? true : false), $cat); if($list) { foreach($list as $x) { $syslist[] = Zlib\Apps::app_encode($x); @@ -43,7 +44,7 @@ class Apps extends \Zotlabs\Web\Controller { return replace_macros(get_markup_template('myapps.tpl'), array( '$sitename' => get_config('system','sitename'), - '$cat' => ((array_key_exists('cat',$_GET) && $_GET['cat']) ? escape_tags($_GET['cat']) : ''), + '$cat' => $cat, '$title' => t('Apps'), '$apps' => $apps, '$authed' => ((local_channel()) ? true : false), diff --git a/Zotlabs/Module/Article_edit.php b/Zotlabs/Module/Article_edit.php new file mode 100644 index 000000000..758c1db2e --- /dev/null +++ b/Zotlabs/Module/Article_edit.php @@ -0,0 +1,138 @@ + 1) ? intval(argv(1)) : 0); + + if(! $post_id) { + notice( t('Item not found') . EOL); + return; + } + + $itm = q("SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1", + intval($post_id), + intval(ITEM_TYPE_ARTICLE) + ); + if($itm) { + $item_id = q("select * from iconfig where cat = 'system' and k = 'ARTICLE' and iid = %d limit 1", + intval($itm[0]['id']) + ); + if($item_id) + $card_title = $item_id[0]['v']; + } + else { + notice( t('Item not found') . EOL); + return; + } + + $owner = $itm[0]['uid']; + $uid = local_channel(); + + $observer = \App::get_observer(); + + $channel = channelx_by_n($owner); + if(! $channel) { + notice( t('Channel not found.') . EOL); + return; + } + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + if(! perm_is_allowed($owner,$ob_hash,'write_pages')) { + notice( t('Permission denied.') . EOL); + return; + } + + $is_owner = (($uid && $uid == $owner) ? true : false); + + $o = ''; + + + + $category = ''; + $catsenabled = ((feature_enabled($owner,'categories')) ? 'categories' : ''); + + if ($catsenabled){ + $itm = fetch_post_tags($itm); + + $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY); + + foreach ($cats as $cat) { + if (strlen($category)) + $category .= ', '; + $category .= $cat['term']; + } + } + + if($itm[0]['attach']) { + $j = json_decode($itm[0]['attach'],true); + if($j) { + foreach($j as $jj) { + $itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n"; + } + } + } + + + $mimetype = $itm[0]['mimetype']; + + $content = $itm[0]['body']; + + + + $rp = 'articles/' . $channel['channel_address']; + + $x = array( + 'nickname' => $channel['channel_address'], + 'bbco_autocomplete'=> 'bbcode', + 'return_path' => $rp, + 'webpage' => ITEM_TYPE_ARTICLE, + 'button' => t('Edit'), + 'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'), + 'weblink' => t('Insert web link'), + 'hide_voting' => false, + 'hide_future' => false, + 'hide_location' => false, + 'hide_expire' => false, + 'showacl' => true, + 'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')), + 'permissions' => $itm[0], + 'lockstate' => (($itm[0]['allow_cid'] || $itm[0]['allow_gid'] || $itm[0]['deny_cid'] || $itm[0]['deny_gid']) ? 'lock' : 'unlock'), + 'ptyp' => $itm[0]['type'], + 'mimeselect' => false, + 'mimetype' => $itm[0]['mimetype'], + 'body' => undo_post_tagging($content), + 'post_id' => $post_id, + 'visitor' => true, + 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'), + 'placeholdertitle' => t('Title (optional)'), + 'pagetitle' => $card_title, + 'profile_uid' => (intval($channel['channel_id'])), + 'catsenabled' => $catsenabled, + 'category' => $category, + 'bbcode' => (($mimetype == 'text/bbcode') ? true : false) + ); + + $editor = status_editor($a, $x); + + $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( + '$title' => t('Edit Article'), + '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false), + '$id' => $itm[0]['id'], + '$editor' => $editor + )); + + return $o; + + } + +} diff --git a/Zotlabs/Module/Articles.php b/Zotlabs/Module/Articles.php new file mode 100644 index 000000000..25daca81d --- /dev/null +++ b/Zotlabs/Module/Articles.php @@ -0,0 +1,187 @@ + 1) + $which = argv(1); + else + return; + + profile_load($which); + + } + + function get($update = 0, $load = false) { + + if(observer_prohibited(true)) { + return login(); + } + + if(! \App::$profile) { + notice( t('Requested profile is not available.') . EOL ); + \App::$error = 404; + return; + } + + if(! feature_enabled(\App::$profile_uid,'articles')) { + return; + } + + nav_set_selected(t('Cards')); + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), + 'title' => 'oembed' + ]); + + + $category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : ''); + + if($category) { + $sql_extra2 .= protect_sprintf(term_item_parent_query(\App::$profile['profile_uid'],'item', $category, TERM_CATEGORY)); + } + + + $which = argv(1); + + $selected_card = ((argc() > 2) ? argv(2) : ''); + + $_SESSION['return_url'] = \App::$query_string; + + $uid = local_channel(); + $owner = \App::$profile_uid; + $observer = \App::get_observer(); + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + if(! perm_is_allowed($owner,$ob_hash,'view_pages')) { + notice( t('Permission denied.') . EOL); + return; + } + + $is_owner = ($uid && $uid == $owner); + + $channel = channelx_by_n($owner); + + if($channel) { + $channel_acl = array( + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ); + } + else { + $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; + } + + + + if(perm_is_allowed($owner,$ob_hash,'write_pages')) { + + $x = [ + 'webpage' => ITEM_TYPE_ARTICLE, + 'is_owner' => true, + 'content_label' => t('Add Article'), + 'button' => t('Create'), + 'nickname' => $channel['channel_address'], + 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] + || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'acl' => (($is_owner) ? populate_acl($channel_acl, false, + \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')) : ''), + 'permissions' => $channel_acl, + 'showacl' => (($is_owner) ? true : false), + 'visitor' => true, + 'hide_location' => false, + 'hide_voting' => false, + 'profile_uid' => intval($owner), + 'mimetype' => 'text/bbcode', + 'mimeselect' => false, + 'layoutselect' => false, + 'expanded' => false, + 'novoting' => false, + 'catsenabled' => feature_enabled($owner,'categories'), + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true + ]; + + if($_REQUEST['title']) + $x['title'] = $_REQUEST['title']; + if($_REQUEST['body']) + $x['body'] = $_REQUEST['body']; + $editor = status_editor($a,$x); + + } + else { + $editor = ''; + } + + + $sql_extra = item_permissions_sql($owner); + + if($selected_card) { + $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.v = '%s' limit 1", + dbesc($selected_card) + ); + if($r) { + $sql_extra .= "and item.id = " . intval($r[0]['iid']) . " "; + } + } + + $r = q("select * from item + where item.uid = %d and item_type = %d + $sql_extra order by item.created desc", + intval($owner), + intval(ITEM_TYPE_ARTICLE) + ); + + $item_normal = " and item.item_hidden = 0 and item.item_type in (0,7) and item.item_deleted = 0 + and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 "; + + if($r) { + + $parents_str = ids_to_querystr($r,'id'); + + $items = q("SELECT item.*, item.id AS item_id + FROM item + WHERE item.uid = %d $item_normal + AND item.parent IN ( %s ) + $sql_extra $sql_extra2 ", + intval(\App::$profile['profile_uid']), + dbesc($parents_str) + ); + if($items) { + xchan_query($items); + $items = fetch_post_tags($items, true); + $items = conv_sort($items,'updated'); + } + else + $items = []; + } + + $mode = 'articles'; + + $content = conversation($items,$mode,false,'traditional'); + + $o = replace_macros(get_markup_template('cards.tpl'), [ + '$title' => t('Articles'), + '$editor' => $editor, + '$content' => $content, + '$pager' => alt_pager($a,count($items)) + ]); + + return $o; + } + +} diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php index 91d279f7a..6737ac4ee 100644 --- a/Zotlabs/Module/Cdav.php +++ b/Zotlabs/Module/Cdav.php @@ -39,7 +39,7 @@ class Cdav extends \Zotlabs\Web\Controller { $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); if($sigblock) { - $keyId = $sigblock['keyId']; + $keyId = str_replace('acct:','',$sigblock['keyId']); if($keyId) { $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", dbesc($keyId) diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php index 14d02d873..7c4c900a1 100644 --- a/Zotlabs/Module/Channel.php +++ b/Zotlabs/Module/Channel.php @@ -332,6 +332,7 @@ class Channel extends \Zotlabs\Web\Controller { '$tags' => (($hashtags) ? urlencode($hashtags) : ''), '$mid' => $mid, '$verb' => '', + '$net' => '', '$dend' => $datequery, '$dbegin' => $datequery2 )); diff --git a/Zotlabs/Module/Cloud.php b/Zotlabs/Module/Cloud.php index 75191a279..2215507ca 100644 --- a/Zotlabs/Module/Cloud.php +++ b/Zotlabs/Module/Cloud.php @@ -57,12 +57,12 @@ class Cloud extends \Zotlabs\Web\Controller { $auth->observer = $ob_hash; } + // if we arrived at this path with any query parameters in the url, build a clean url without + // them and redirect. - $_SERVER['QUERY_STRING'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['QUERY_STRING']); - $_SERVER['QUERY_STRING'] = strip_zids($_SERVER['QUERY_STRING']); - - $_SERVER['REQUEST_URI'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['REQUEST_URI']); - $_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']); + $x = clean_query_string(); + if($x !== \App::$query_string) + goaway(z_root() . '/' . $x); $rootDirectory = new \Zotlabs\Storage\Directory('/', $auth); @@ -83,17 +83,42 @@ class Cloud extends \Zotlabs\Web\Controller { $server->addPlugin($browser); // Experimental QuotaPlugin - // require_once('\Zotlabs\Storage/QuotaPlugin.php'); - // $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth)); + // require_once('\Zotlabs\Storage/QuotaPlugin.php'); + // $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth)); + + + // over-ride the default XML output on thrown exceptions + + $server->on('exception', [ $this, 'DAVException' ]); -// ob_start(); // All we need to do now, is to fire up the server + $server->exec(); -// ob_end_flush(); if($browser->build_page) construct_page(); + + killme(); + } + + + function DAVException($err) { + + if($err instanceof \Sabre\DAV\Exception\NotFound) { + notice( t('Not found') . EOL); + } + elseif($err instanceof \Sabre\DAV\Exception\Forbidden) { + notice( t('Permission denied') . EOL); + } + else { + notice( t('Unknown error') . EOL); + } + + construct_page(); + killme(); } } + + diff --git a/Zotlabs/Module/Cloud_tiles.php b/Zotlabs/Module/Cloud_tiles.php new file mode 100644 index 000000000..da551904f --- /dev/null +++ b/Zotlabs/Module/Cloud_tiles.php @@ -0,0 +1,21 @@ + $desc) { + + $checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$perm); + $inherited = (($checkinherited & PERMS_SPECIFIC) ? false : true); + + if(array_key_exists('perms_' . $perm, $_POST)) { + set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'my_perms',$perm, + intval($_POST['perms_' . $perm])); + if($autoperms) { + set_pconfig($channel['channel_id'],'autoperms',$perm,intval($_POST['perms_' . $perm])); + } + } + else { + set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'my_perms',$perm,0); + if($autoperms) { + set_pconfig($channel['channel_id'],'autoperms',$perm,0); + } + } + } + } + + if(! is_null($autoperms)) + set_pconfig($channel['channel_id'],'system','autoperms',$autoperms); + + + notice( t('Settings updated.') . EOL); + + + // Refresh the structure in memory with the new data + + $r = q("SELECT abook.*, xchan.* + FROM abook left join xchan on abook_xchan = xchan_hash + WHERE abook_channel = %d and abook_id = %d LIMIT 1", + intval(local_channel()), + intval($contact_id) + ); + if($r) { + \App::$poi = $r[0]; + } + + + $this->defperms_clone($a); + + goaway(z_root() . '/defperms'); + + return; + + } + + /* @brief Clone connection + * + * + */ + + function defperms_clone(&$a) { + + if(! \App::$poi) + return; + + $channel = \App::get_channel(); + + $r = q("SELECT abook.*, xchan.* + FROM abook left join xchan on abook_xchan = xchan_hash + WHERE abook_channel = %d and abook_id = %d LIMIT 1", + intval(local_channel()), + intval(\App::$poi['abook_id']) + ); + if($r) { + \App::$poi = array_shift($r); + } + + $clone = \App::$poi; + + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); + + $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); + if($abconfig) + $clone['abconfig'] = $abconfig; + + build_sync_packet(0 /* use the current local_channel */, array('abook' => array($clone))); + } + + /* @brief Generate content of connection default permissions page + * + * + */ + + function get() { + + $sort_type = 0; + $o = ''; + + if(! local_channel()) { + notice( t('Permission denied.') . EOL); + return login(); + } + + $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); + $channel = \App::get_channel(); + + $yes_no = array(t('No'),t('Yes')); + + $connect_perms = \Zotlabs\Access\Permissions::connect_perms(local_channel()); + + $o .= "\n"; + + if(\App::$poi) { + + $sections = []; + + $self = false; + + $tpl = get_markup_template('defperms.tpl'); + + + $perms = array(); + $channel = \App::get_channel(); + + $contact = \App::$poi; + + $global_perms = \Zotlabs\Access\Permissions::Perms(); + + $existing = get_all_perms(local_channel(),$contact['abook_xchan']); + $hidden_perms = []; + + foreach($global_perms as $k => $v) { + $thisperm = get_abconfig(local_channel(),$contact['abook_xchan'],'my_perms',$k); + + $checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$k); + + $inherited = (($checkinherited & PERMS_SPECIFIC) ? false : true); + + $perms[] = [ 'perms_' . $k, $v, intval($thisperm), '', $yes_no, (($inherited) ? ' disabled="disabled" ' : '') ]; + if($inherited) { + $hidden_perms[] = [ 'perms_' . $k, intval($thisperm) ]; + } + } + + $pcat = new \Zotlabs\Lib\Permcat(local_channel()); + $pcatlist = $pcat->listing(); + $permcats = []; + if($pcatlist) { + foreach($pcatlist as $pc) { + $permcats[$pc['name']] = $pc['localname']; + } + } + + $o .= replace_macros($tpl, [ + '$header' => t('Connection Default Permissions'), + '$autoperms' => array('autoperms',t('Apply these permissions automatically'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('If enabled, connection requests will be approved without your interaction'), $yes_no), + '$permcat' => [ 'permcat', t('Permission role'), '', '',$permcats ], + '$permcat_new' => t('Add permission role'), + '$permcat_enable' => feature_enabled(local_channel(),'permcats'), + '$section' => $section, + '$sections' => $sections, + '$autolbl' => t('The permissions indicated on this page will be applied to all new connections.'), + '$autoapprove' => t('Automatic approval settings'), + '$unapproved' => $unapproved, + '$inherited' => t('inherited'), + '$submit' => t('Submit'), + '$me' => t('My Settings'), + '$perms' => $perms, + '$hidden_perms' => $hidden_perms, + '$permlbl' => t('Individual Permissions'), + '$permnote_self' => t('Some individual permissions may have been preset or locked based on your channel type and privacy settings.'), + '$contact_id' => $contact['abook_id'], + '$name' => $contact['xchan_name'], + ]); + + $arr = array('contact' => $contact,'output' => $o); + + call_hooks('contact_edit', $arr); + + return $arr['output']; + + } + } +} diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index caf0190ae..b1552a694 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -64,6 +64,11 @@ class Directory extends \Zotlabs\Web\Controller { return; } + if(get_config('system','block_public_directory',false) && (! get_observer_hash())) { + notice( t('Public access denied.') . EOL); + return; + } + $observer = get_observer_hash(); $globaldir = get_directory_setting($observer, 'globaldir'); @@ -102,7 +107,7 @@ class Directory extends \Zotlabs\Web\Controller { $common = array(); $index = 0; foreach($r as $rr) { - $common[$rr['xchan_addr']] = $rr['total']; + $common[$rr['xchan_addr']] = ((intval($rr['total']) > 0) ? intval($rr['total']) - 1 : 0); $addresses[$rr['xchan_addr']] = $index++; } @@ -334,7 +339,7 @@ class Directory extends \Zotlabs\Web\Controller { 'ignlink' => $suggest ? z_root() . '/directory?ignore=' . $rr['hash'] : '', 'ignore_label' => t('Don\'t suggest'), 'common_friends' => (($common[$rr['address']]) ? intval($common[$rr['address']]) : ''), - 'common_label' => t('Common connections:'), + 'common_label' => t('Common connections (estimated):'), 'common_count' => intval($common[$rr['address']]), 'safe' => $safe_mode ); diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php index d5afdd787..6d895feb5 100644 --- a/Zotlabs/Module/Display.php +++ b/Zotlabs/Module/Display.php @@ -12,6 +12,15 @@ class Display extends \Zotlabs\Web\Controller { function get($update = 0, $load = false) { + $module_format = 'html'; + + + if(argc() > 1) { + $module_format = substr(argv(1),strrpos(argv(1),'.') + 1); + if(! in_array($module_format,['atom','zot','json'])) + $module_format = 'html'; + } + $checkjs = new \Zotlabs\Web\CheckJS(1); if($load) @@ -22,8 +31,12 @@ class Display extends \Zotlabs\Web\Controller { return; } - if(argc() > 1 && argv(1) !== 'load') + if(argc() > 1) { $item_hash = argv(1); + if($module_format !== 'html') { + $item_hash = substr($item_hash,0,strrpos($item_hash,'.')); + } + } if($_REQUEST['mid']) $item_hash = $_REQUEST['mid']; @@ -44,28 +57,28 @@ class Display extends \Zotlabs\Web\Controller { $channel_acl = array( 'allow_cid' => $channel['channel_allow_cid'], 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] ); $x = array( - 'is_owner' => true, - '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'), + 'is_owner' => true, + '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'), - 'acl' => populate_acl($channel_acl), - 'permissions' => $channel_acl, - 'bang' => '', - 'visitor' => true, - 'profile_uid' => local_channel(), - 'return_path' => 'channel/' . $channel['channel_address'], - 'expanded' => true, + 'acl' => populate_acl($channel_acl), + 'permissions' => $channel_acl, + 'bang' => '', + 'visitor' => true, + 'profile_uid' => local_channel(), + 'return_path' => 'channel/' . $channel['channel_address'], + 'expanded' => true, 'editor_autocomplete' => true, - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true, - 'jotnets' => true + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true ); $o = '