Merge pull request #7 from redmatrix/dev

Dev
This commit is contained in:
mrjive 2018-01-16 11:45:01 +01:00 committed by GitHub
commit e81949bb09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
569 changed files with 35630 additions and 20908 deletions

View file

@ -2,10 +2,19 @@
Run hubzilla-setup.sh for an unattended installation of hubzilla. Run hubzilla-setup.sh for an unattended installation of hubzilla.
The script is known to work with Debian 8.3 stable (Jessie) The script is known to work without adjustments with
+ Home-PC (Debian-8.3.0-amd64) + Hardware
+ DigitalOcean droplet (Debian 8.3 x64 / 512 MB Memory / 20 GB Disk / NYC3) - Mini-PC with Debian-9.2-amd64, or
- Rapberry 3 with Raspbian, Debian-9.3
+ DynDNS
- selfHOST.de
- freedns.afraid.org
## Disclaimers
- This script does work with Debian 9 only.
- This script has to be used on a fresh debian install only (it does not take account for a possibly already installed and configured webserver or sql implementation).
# Step-by-Step Overwiew # Step-by-Step Overwiew
@ -14,25 +23,28 @@ The script is known to work with Debian 8.3 stable (Jessie)
Hardware Hardware
+ Internet connection and router at home + Internet connection and router at home
+ Mini-pc connected to your router + Mini-pc connected to your router (a Raspberry 3 will do for very small Hubs)
+ USB drive for backups + USB drive for backups
Software Software
+ Fresh installation of Debian on your mini-pc + Fresh installation of Debian 9 (Stretch)
+ Router with open ports 80 and 443 for your Debian + Router with open ports 80 and 443 for your Hub
## The basic steps (quick overview) ## The basic steps (quick overview)
+ Register your own domain (for example at selfHOST) or a free subdomain (for example at freeDNS) + Register your own domain (for example at selfHOST) or a free subdomain (for example at freeDNS)
+ Log on to your new debian (server) + Log on to your fresh Debian
- apt-get install git - apt-get install git
- mkdir -p /var/www - mkdir -p /var/www
- cd /var/www - cd /var/www
- git clone https://github.com/redmatrix/hubzilla.git html - git clone https://github.com/redmatrix/hubzilla.git html
- cp .homeinstall/hubzilla-config.txt.template .homeinstall/hubzilla-config.txt - cd /html/.homeinstall
- nano .homeinstall/hubzilla-config.txt - cp hubzilla-config.txt.template hubzilla-config.txt
- Enter your values there: db pass, domain, values for dyn DNS - nano hubzilla-config.txt
- Read the comments carefully
- Enter your values: db pass, domain, values for dyn DNS
- Make sure your external drive (for backups) is mounted
- hubzilla-setup.sh as root - hubzilla-setup.sh as root
- ... wait, wait, wait until the script is finised - ... wait, wait, wait until the script is finised
- reboot - reboot
@ -46,23 +58,44 @@ Software
### Recommended: USB Drive for Backups ### Recommended: USB Drive for Backups
The installation will create a daily backup. The installation will create a daily backup written to an external drive.
If the backup process does not find an external device than the backup goes to The USB drive must be compatible with the filesystems
the internal disk.
The USB drive must be compatible with an encrpyted filesystem LUKS + ext4. - 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 ## Preparations Software
### Install Debian Linux on the Mini-PC ### Install Debian Linux on the Mini-PC
Download the stable Debian at https://www.debian.org/ Download the stable Debian at https://www.debian.org/
(Debian 8 is no longer supported.)
Create bootable USB drive with Debian on it. You could use the programm Create bootable USB drive with Debian on it.You could use
unetbootin, https://en.wikipedia.org/wiki/UNetbootin
Switch of your mini pc, plug in your USB drive and start the mini pc from the - unetbootin, https://en.wikipedia.org/wiki/UNetbootin
- or simply the linux command "dd"
Example for command dd...
su -
dd if=2017-11-29-raspbian-stretch.img of=/dev/mmcblk0
Do not forget to unmount the SD card before and check if unmounted like in this example...
su -
umount /dev/mmcblk0*
df -h
Switch off your mini pc, plug in your USB drive and start the mini pc from the
stick. Install Debian. Follow the instructions of the installation. stick. Install Debian. Follow the instructions of the installation.
### Configure your Router ### Configure your Router
@ -79,28 +112,20 @@ You can use subdomains as well
my.cooldomain.org my.cooldomain.org
There are two way to get a domain There are two ways to get a domain...
- buy a domain (recommended) or ### Method 1: Buy a Domain
- register a free subdomain
### Method 1: Get yourself an own Domain (recommended) ...for example buy at selfHOST.de
...for example at selfHOST.de The cost are around 10,- € once and 1,50 € per month (2017).
### Method 2 Register a (free) Subdomain ### Method 2 Register a (free) Subdomain
Register a free subdomain for example at ...for example register at freedns.afraid.org
- freeDNS Follow the instructions in .homeinstall/hubzilla-config.txt.
- selfHOST
WATCH THIS: A free subdomain is not the prefered way to get a domain name. Why?
Let's encrpyt issues a limited number of certificates each
day. Possibly other users of this domain will try to issue a certificate
at the same day as you do. So make sure you choose a domain with as less subdomains as
possible.
## Install Hubzilla on your Debian ## Install Hubzilla on your Debian
@ -135,10 +160,12 @@ Copy the template file
cp hubzilla-config.txt.template hubzilla-config.txt cp hubzilla-config.txt.template hubzilla-config.txt
Change the file "hubzilla-config.txt". Read the instructions there and enter your values. Modify the file "hubzilla-config.txt". Read the instructions there carefully and enter your values.
nano hubzilla-config.txt 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 Run the script
./hubzilla-setup.sh ./hubzilla-setup.sh
@ -146,7 +173,7 @@ Run the script
Wait... The script should not finish with an error message. Wait... The script should not finish with an error message.
In a webbrowser open your domain. In a webbrowser open your domain.
Expected: A test page of hubzilla is shown. All checks there shoulg be Expected: A test page of hubzilla is shown. All checks there should be
successfull. Go on... successfull. Go on...
Expected: A page for the Hubzilla server configuration shows up. Expected: A page for the Hubzilla server configuration shows up.
@ -162,3 +189,28 @@ Leave db type "MySQL" untouched.
Follow the instructions in the next pages. 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).
It is recommended to deinstall these programms to avoid endless updates. Use...
sudo apt-get purge wolfram-engine sonic-pi
sudo apt-get autoremove
It is recommended to run the Raspi without graphical frontend (X-Server). Use...
sudo raspi-config
to boot the Rapsi to the client console.
DO NOT FORGET TO CHANGE THE DEFAULT PASSWORD FOR USER PI!

View file

@ -70,15 +70,17 @@ selfhost_pass=
# freedns_key=U1Z6aGt2R0NzMFNPNWRjbWxxZGpsd093OjE1Mzg5NDE5 # freedns_key=U1Z6aGt2R0NzMFNPNWRjbWxxZGpsd093OjE1Mzg5NDE5
# #
# #
#freedns_key= freedns_key=
############################################### ###############################################
### OPTIONAL - Backup to external device ###### ### OPTIONAL - Backup to external device ######
# #
# The script can use an external device for the daily backup. # The script can use an external device for the daily backup.
# The file system of the device (USB stick for example) must be compatible # The file system of the device (USB stick for example) must be compatible with
# with encrypted LUKS + ext4 #
# - encrypted LUKS + ext4, or
# - ext4
# #
# You should test to mount the device befor you run the script # You should test to mount the device befor you run the script
# (hubzilla-setup.sh). # (hubzilla-setup.sh).
@ -113,27 +115,21 @@ selfhost_pass=
# lsof /media/hubzilla_backup # lsof /media/hubzilla_backup
# #
# If you leave the following parameters # If you leave the following parameters
#
# - "backup_device_name" and # - "backup_device_name" and
# - "backup_device_pass" # - "backup_device_pass"
#
# empty the script will create daily backups on the internal disk (which could # empty the script will create daily backups on the internal disk (which could
# save you as well). # save you as well).
# #
# Example: backup_device_name=/dev/sdc1 # Example: backup_device_name=/dev/sdc1
# #
# Leave "backup_device_pass=" empty if the external device is not encrypted.
#
backup_device_name= backup_device_name=
backup_device_pass= backup_device_pass=
###############################################
### OPTIONAL - Owncloud - deprecated ##########
#
# To install owncloud: owncloud=y
# Leave empty if you don't want to install owncloud
#
#owncloud=
############################################### ###############################################
### OPTIONAL - do not mess with things below ## ### OPTIONAL - do not mess with things below ##
# (...if you are not certain) # (...if you are not certain)
@ -160,18 +156,3 @@ mysqlpass=$db_pass
# Example: phpmyadminpass="aber hallo has blanks in it" # Example: phpmyadminpass="aber hallo has blanks in it"
phpmyadminpass=$db_pass phpmyadminpass=$db_pass
# TODO Prepare hubzilla for programmers
# - install eclipse and plugins
# - install xdebug to debug the php with eclipse
# - weaken permissions on /var/www/html
# - manual steps after this script
# * in eclipse: install plugins for php git hub
# * in eclipse: configure firefox (chrome,...) as browser to run with the php debuger
# * in eclipse: switch php debugger from zend to xdebug
# * in eclipse: add local hubzilla github repository
#
# Which user will use eclipse?
# Leave this empty if you do not want to prepare hubzilla for debugging
#
#developer_name=

View file

@ -114,7 +114,11 @@ function check_sanity {
fi fi
if [ ! -f /etc/debian_version ] if [ ! -f /etc/debian_version ]
then then
die "Ubuntu is not supported" die "Debian is supported only"
fi
if ! grep -q 'Linux 9' /etc/issue
then
die "Linux 9 (stretch) is supported only"x
fi fi
} }
@ -253,11 +257,11 @@ function install_sendmail {
} }
function install_php { function install_php {
# openssl and mbstring are included in libapache2-mod-php5 # openssl and mbstring are included in libapache2-mod-php
# to_to: php5-suhosin
print_info "installing php..." print_info "installing php..."
nocheck_install "libapache2-mod-php5 php5 php-pear php5-xcache php5-curl php5-mcrypt php5-gd" nocheck_install "libapache2-mod-php php php-pear php-curl php-mcrypt php-gd"
php5enmod mcrypt sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/7.0/apache2/php.ini
sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/7.0/apache2/php.ini
} }
function install_mysql { function install_mysql {
@ -277,18 +281,17 @@ function install_mysql {
# want to be prompted for it then this can be arranged by preseeding the # want to be prompted for it then this can be arranged by preseeding the
# DebConf database with the required information. # DebConf database with the required information.
# #
# echo mysql-server-5.5 mysql-server/root_password password xyzzy | debconf-set-selections # echo mysql-server mysql-server/root_password password xyzzy | debconf-set-selections
# echo mysql-server-5.5 mysql-server/root_password_again password xyzzy | debconf-set-selections # echo mysql-server mysql-server/root_password_again password xyzzy | debconf-set-selections
# #
print_info "installing mysql..." print_info "installing mysql..."
if [ -z "$mysqlpass" ] if [ -z "$mysqlpass" ]
then then
die "mysqlpass not set in $configfile" die "mysqlpass not set in $configfile"
fi fi
echo mysql-server-5.5 mysql-server/root_password password $mysqlpass | debconf-set-selections echo mysql-server mysql-server/root_password password $mysqlpass | debconf-set-selections
echo mysql-server-5.5 mysql-server/root_password_again password $mysqlpass | debconf-set-selections echo mysql-server mysql-server/root_password_again password $mysqlpass | debconf-set-selections
nocheck_install "php5-mysql mysql-server mysql-client" nocheck_install "php-mysql mysql-server mysql-client"
php5enmod mcrypt
} }
function install_phpmyadmin { function install_phpmyadmin {
@ -327,6 +330,7 @@ function install_phpmyadmin {
echo "Include /etc/phpmyadmin/apache.conf" >> /etc/apache2/apache2.conf echo "Include /etc/phpmyadmin/apache.conf" >> /etc/apache2/apache2.conf
fi fi
service apache2 restart service apache2 restart
/etc/init.d/mysql start
} }
function create_hubzilla_db { function create_hubzilla_db {
@ -455,11 +459,6 @@ function configure_cron_selfhost {
fi fi
} }
function install_git {
print_info "installing git..."
nocheck_install "git"
}
function install_letsencrypt { function install_letsencrypt {
print_info "installing let's encrypt ..." print_info "installing let's encrypt ..."
# check if user gave domain # check if user gave domain
@ -511,6 +510,8 @@ END
then then
die "Failed to load $url_http" die "Failed to load $url_http"
fi fi
# accept terms of service of letsencrypt
./dehydrated --register --accept-terms
# run script dehydrated # run script dehydrated
# #
./dehydrated --cron --config $le_dir/config.sh ./dehydrated --cron --config $le_dir/config.sh
@ -564,17 +565,13 @@ function check_https {
} }
function install_hubzilla { function install_hubzilla {
print_info "installing hubzilla..." print_info "installing hubzilla addons..."
# rm -R /var/www/html/ # for "stand alone" usage cd /var/www/html/
cd /var/www/ util/add_addon_repo https://github.com/redmatrix/hubzilla-addons.git hzaddons
# git clone https://github.com/redmatrix/hubzilla html # for "stand alone" usage
cd html/
git clone https://github.com/redmatrix/hubzilla-addons addon
mkdir -p "store/[data]/smarty3" mkdir -p "store/[data]/smarty3"
chmod -R 777 store chmod -R 777 store
touch .htconfig.php touch .htconfig.php
chmod ou+w .htconfig.php chmod ou+w .htconfig.php
install_hubzilla_plugins
cd /var/www/ cd /var/www/
chown -R www-data:www-data html chown -R www-data:www-data html
chown root:www-data /var/www/html/ chown root:www-data /var/www/html/
@ -589,73 +586,6 @@ function install_hubzilla {
print_info "installed 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
echo "https://gitlab.com/zot/hubzilla-chess.git chess" >> $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 { function rewrite_to_https {
print_info "configuring apache to redirect http to httpS ..." print_info "configuring apache to redirect http to httpS ..."
htaccessfile=/var/www/html/.htaccess htaccessfile=/var/www/html/.htaccess
@ -677,23 +607,17 @@ function install_rsnapshot {
nocheck_install "rsnapshot" nocheck_install "rsnapshot"
# internal disk # internal disk
cp -f /etc/rsnapshot.conf $snapshotconfig cp -f /etc/rsnapshot.conf $snapshotconfig
sed -i "/hourly/s/retain/#retain/" $snapshotconfig
sed -i "/monthly/s/#retain/retain/" $snapshotconfig
sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig
sed -i "s/^backup/#backup/" $snapshotconfig sed -i "s/^backup/#backup/" $snapshotconfig
if [ -z "`grep 'letsencrypt' $snapshotconfig`" ]
then
echo "backup /var/lib/mysql/ localhost/" >> $snapshotconfig echo "backup /var/lib/mysql/ localhost/" >> $snapshotconfig
echo "backup /var/www/html/ localhost/" >> $snapshotconfig echo "backup /var/www/html/ localhost/" >> $snapshotconfig
echo "backup /var/www/letsencrypt/ localhost/" >> $snapshotconfig echo "backup /var/www/letsencrypt/ localhost/" >> $snapshotconfig
fi
# external disk # external disk
if [ -n "$backup_device_name" ] && [ -n "$backup_device_pass" ] if [ -n "$backup_device_name" ]
then then
cp -f /etc/rsnapshot.conf $snapshotconfig_external_device cp -f /etc/rsnapshot.conf $snapshotconfig_external_device
sed -i "s#snapshot_root.*#snapshot_root $backup_mount_point#" $snapshotconfig_external_device sed -i "s#snapshot_root.*#snapshot_root $backup_mount_point#" $snapshotconfig_external_device
sed -i "/hourly/s/retain/#retain/" $snapshotconfig_external_device sed -i "/alpha/s/6/30/" $snapshotconfig_external_device
sed -i "/monthly/s/#retain/retain/" $snapshotconfig_external_device
sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig_external_device sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig_external_device
sed -i "s/^backup/#backup/" $snapshotconfig_external_device sed -i "s/^backup/#backup/" $snapshotconfig_external_device
if [ -z "`grep 'letsencrypt' $snapshotconfig_external_device`" ] if [ -z "`grep 'letsencrypt' $snapshotconfig_external_device`" ]
@ -767,9 +691,7 @@ echo " if mount $backup_device_name $backup_mount_point" >> /var/www/$hub
echo " then" >> /var/www/$hubzilladaily echo " then" >> /var/www/$hubzilladaily
echo " device_mounted=1" >> /var/www/$hubzilladaily echo " device_mounted=1" >> /var/www/$hubzilladaily
echo " echo \"device $backup_device_name is now mounted. Starting backup...\"" >> /var/www/$hubzilladaily echo " echo \"device $backup_device_name is now mounted. Starting backup...\"" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig_external_device daily" >> /var/www/$hubzilladaily echo " rsnapshot -c $snapshotconfig_external_device alpha" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig_external_device weekly" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig_external_device monthly" >> /var/www/$hubzilladaily
echo " echo \"\$(date) - disk sizes...\"" >> /var/www/$hubzilladaily echo " echo \"\$(date) - disk sizes...\"" >> /var/www/$hubzilladaily
echo " df -h" >> /var/www/$hubzilladaily echo " df -h" >> /var/www/$hubzilladaily
echo " echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily echo " echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily
@ -788,28 +710,22 @@ echo " fi" >> /var/www/$hubzilladaily
echo "fi" >> /var/www/$hubzilladaily echo "fi" >> /var/www/$hubzilladaily
echo "if [ \$device_mounted == 0 ]" >> /var/www/$hubzilladaily echo "if [ \$device_mounted == 0 ]" >> /var/www/$hubzilladaily
echo "then" >> /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 " echo \"device could not be mounted $backup_device_name. No backup written.\"" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig daily" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig weekly" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig monthly" >> /var/www/$hubzilladaily
echo "fi" >> /var/www/$hubzilladaily echo "fi" >> /var/www/$hubzilladaily
echo "#" >> /var/www/$hubzilladaily echo "#" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - db size...\"" >> /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 "#" >> /var/www/$hubzilladaily
echo "# update" >> /var/www/$hubzilladaily echo "# update" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - updating dehydrated...\"" >> /var/www/$hubzilladaily echo "echo \"\$(date) - updating dehydrated...\"" >> /var/www/$hubzilladaily
echo "git -C /var/www/letsencrypt/ pull" >> /var/www/$hubzilladaily echo "git -C /var/www/letsencrypt/ pull" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - updating hubhilla core...\"" >> /var/www/$hubzilladaily echo "echo \"\$(date) - updating hubhilla core...\"" >> /var/www/$hubzilladaily
echo "git -C /var/www/html/ pull" >> /var/www/$hubzilladaily echo "(cd /var/www/html/ ; util/udall)" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - updating hubhilla addons...\"" >> /var/www/$hubzilladaily
echo "git -C /var/www/html/addon/ pull" >> /var/www/$hubzilladaily
echo "bash /var/www/html/$plugins_update" >> /var/www/$hubzilladaily
echo "chown -R www-data:www-data /var/www/html/ # make all accessable for the webserver" >> /var/www/$hubzilladaily echo "chown -R www-data:www-data /var/www/html/ # make all accessable for the webserver" >> /var/www/$hubzilladaily
echo "chown root:www-data /var/www/html/.htaccess" >> /var/www/$hubzilladaily echo "chown root:www-data /var/www/html/.htaccess" >> /var/www/$hubzilladaily
echo "chmod 0644 /var/www/html/.htaccess # www-data can read but not write it" >> /var/www/$hubzilladaily echo "chmod 0644 /var/www/html/.htaccess # www-data can read but not write it" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - updating linux...\"" >> /var/www/$hubzilladaily echo "echo \"\$(date) - updating linux...\"" >> /var/www/$hubzilladaily
echo "apt-get -q -y update && apt-get -q -y dist-upgrade # update linux and upgrade" >> /var/www/$hubzilladaily echo "apt-get -q -y update && apt-get -q -y dist-upgrade && apt-get -q -y autoremove # update linux and upgrade" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - Backup hubzilla and update linux finished. Rebooting...\"" >> /var/www/$hubzilladaily echo "echo \"\$(date) - Backup hubzilla and update linux finished. Rebooting...\"" >> /var/www/$hubzilladaily
echo "#" >> /var/www/$hubzilladaily echo "#" >> /var/www/$hubzilladaily
echo "reboot" >> /var/www/$hubzilladaily echo "reboot" >> /var/www/$hubzilladaily
@ -894,7 +810,6 @@ install_run_selfhost
ping_domain ping_domain
configure_cron_freedns configure_cron_freedns
configure_cron_selfhost configure_cron_selfhost
install_git
install_letsencrypt install_letsencrypt
configure_apache_for_https configure_apache_for_https
check_https check_https

View file

@ -39,6 +39,7 @@ addons:
php: php:
- '7.0' - '7.0'
- '7.1' - '7.1'
- '7.2'
# HHVM does not fulfil PHPUnit platform requirements as being compatible with PHP7 yet # HHVM does not fulfil PHPUnit platform requirements as being compatible with PHP7 yet
#- 'hhvm' #- 'hhvm'
@ -69,7 +70,7 @@ matrix:
mariadb: '10.1' mariadb: '10.1'
# PHP7.1, PostgreSQL 9.6 # PHP7.1, PostgreSQL 9.6
- php: '7.1' - 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 # Use newer postgres than 9.2 default
addons: addons:
postgresql: '9.6' postgresql: '9.6'
@ -125,15 +126,19 @@ before_script:
- if [[ "$DB" == "pgsql" ]]; then ./tests/travis/prepare_pgsql.sh; fi - if [[ "$DB" == "pgsql" ]]; then ./tests/travis/prepare_pgsql.sh; fi
# omitting "script:" will default to phpunit # 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: 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 - ./tests/travis/gen_apidocs.sh
#after_failure: after_failure:
- cat tests/results/testdox.txt
# Deploying release and API documentation to GitHub # 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: deploy:
- provider: pages - provider: pages
skip_cleanup: true skip_cleanup: true
@ -152,6 +157,15 @@ deploy:
repo: redmatrix/hubzilla repo: redmatrix/hubzilla
tags: true tags: true
condition: '(-n "$GH_TOKEN") && ("$TRAVIS_JOB_NUMBER" == "${TRAVIS_BUILD_NUMBER}.1")' 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_deploy:
#after_script: #after_script:

144
CHANGELOG
View file

@ -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) Hubzilla 2.8.1 (2017-11-11)
- Rename channel app events to calendar and add nav_set_selected() to /cal - 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 - Load notifications links to /display via ajax if we are already in /display

View file

@ -3,10 +3,14 @@
namespace Zotlabs\Access; namespace Zotlabs\Access;
/** /**
* @brief AccessList class. * @brief AccessList class which represents individual content ACLs.
* *
* A class to hold an AccessList object with allowed and denied contacts and * A class to hold an AccessList object with allowed and denied contacts and
* groups. * groups.
*
* After evaluating @ref ::Zotlabs::Access::PermissionLimits "PermissionLimits"
* and @ref ::Zotlabs::Lib::Permcat "Permcat"s individual content ACLs are evaluated.
* These answer the question "Can Joe view *this* album/photo?".
*/ */
class AccessList { class AccessList {
/** /**
@ -103,7 +107,7 @@ class AccessList {
* @brief Return an array consisting of the current access list components * @brief Return an array consisting of the current access list components
* where the elements are directly storable. * where the elements are directly storable.
* *
* @return Associative array with: * @return array An associative array with:
* * \e string \b allow_cid => string of allowed cids * * \e string \b allow_cid => string of allowed cids
* * \e string \b allow_gid => string of allowed gids * * \e string \b allow_gid => string of allowed gids
* * \e string \b deny_cid => string of denied cids * * \e string \b deny_cid => string of denied cids

View file

@ -2,35 +2,90 @@
namespace Zotlabs\Access; namespace Zotlabs\Access;
use \Zotlabs\Lib as ZLib; use Zotlabs\Lib\PConfig;
/**
* @brief Permission limits.
*
* Permission limits are a very high level permission setting. They are hard
* limits by design.
* "Who can view my photos (at all)?"
* "Who can post photos in my albums (at all)?"
*
* For viewing permissions we generally set these to 'anybody' and for write
* permissions we generally set them to 'those I allow', though many people
* restrict the viewing permissions further for things like 'Can view my connections'.
*
* People get confused enough by permissions that we wanted a place to set their
* privacy expectations once and be done with it.
*
* Connection related permissions like "Can Joe view my photos?" are handled by
* @ref ::Zotlabs::Lib::Permcat "Permcat" and inherit from the channel's Permission
* limits.
*
* @see Permissions
*/
class PermissionLimits { class PermissionLimits {
/**
* @brief Get standard permission limits.
*
* Viewing permissions and post_comments permission are set to 'anybody',
* other permissions are set to 'those I allow'.
*
* The list of permissions comes from Permissions::Perms().
*
* @return array
*/
static public function Std_Limits() { static public function Std_Limits() {
$limits = [];
$perms = Permissions::Perms(); $perms = Permissions::Perms();
$limits = array();
foreach($perms as $k => $v) { foreach($perms as $k => $v) {
if(strstr($k, 'view') || $k === 'post_comments') if(strstr($k, 'view') || $k === 'post_comments')
$limits[$k] = PERMS_PUBLIC; $limits[$k] = PERMS_PUBLIC;
else else
$limits[$k] = PERMS_SPECIFIC; $limits[$k] = PERMS_SPECIFIC;
} }
return $limits; return $limits;
} }
/**
* @brief Sets a permission limit for a channel.
*
* @param int $channel_id
* @param string $perm
* @param int $perm_limit one of PERMS_* constants
*/
static public function Set($channel_id, $perm, $perm_limit) { static public function Set($channel_id, $perm, $perm_limit) {
ZLib\PConfig::Set($channel_id,'perm_limits',$perm,$perm_limit); PConfig::Set($channel_id, 'perm_limits', $perm, $perm_limit);
} }
/**
* @brief Get a channel's permission limits.
*
* Return a channel's permission limits from PConfig. If $perm is set just
* return this permission limit, if not set, return an array with all
* permission limits.
*
* @param int $channel_id
* @param string $perm (optional)
* @return
* * \b boolean false if no perm_limits set for this channel
* * \b int if $perm is set, return one of PERMS_* constants for this permission
* * \b array with all permission limits, if $perm is not set
*/
static public function Get($channel_id, $perm = '') { static public function Get($channel_id, $perm = '') {
if($perm) { if($perm) {
return Zlib\PConfig::Get($channel_id,'perm_limits',$perm); return PConfig::Get($channel_id, 'perm_limits', $perm);
} }
else {
Zlib\PConfig::Load($channel_id); PConfig::Load($channel_id);
if(array_key_exists($channel_id,\App::$config) && array_key_exists('perm_limits',\App::$config[$channel_id])) if(array_key_exists($channel_id, \App::$config)
&& array_key_exists('perm_limits', \App::$config[$channel_id]))
return \App::$config[$channel_id]['perm_limits']; return \App::$config[$channel_id]['perm_limits'];
return false; return false;
} }
} }
}

View file

@ -426,8 +426,10 @@ class Notifier {
logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG);
stringify_array_elms($recipients); stringify_array_elms($recipients);
if(! $recipients) if(! $recipients) {
logger('no recipients');
return; return;
}
// logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG); // logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG);

View file

@ -221,6 +221,7 @@ class Apps {
static public function translate_system_apps(&$arr) { static public function translate_system_apps(&$arr) {
$apps = array( $apps = array(
'Apps' => t('Apps'), 'Apps' => t('Apps'),
'Articles' => t('Articles'),
'Cards' => t('Cards'), 'Cards' => t('Cards'),
'Admin' => t('Site Admin'), 'Admin' => t('Site Admin'),
'Report Bug' => t('Report Bug'), 'Report Bug' => t('Report Bug'),
@ -352,7 +353,7 @@ class Apps {
break; break;
default: default:
if($config) if($config)
$unset = ((get_config('system', $require[0]) == $require[1]) ? false : true); $unset = ((get_config('system', $require[0]) === $require[1]) ? false : true);
else else
$unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true); $unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true);
if($unset) if($unset)

View file

@ -804,6 +804,8 @@ class Enotify {
'when' => relative_date($item['created']), 'when' => relative_date($item['created']),
'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'), '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'])), '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)) 'message' => strip_tags(bbcode($itemem_text))
); );

View file

@ -0,0 +1,122 @@
<?php
namespace Zotlabs\Lib;
class Img_filesize {
private $url;
function __construct($url) {
$this->url = $url;
}
function getSize() {
$size = null;
if(stripos($this->url,z_root() . '/photo') !== false) {
$size = self::getLocalFileSize($this->url);
}
if(! $size) {
$size = getRemoteFileSize($this->url);
}
return $size;
}
static function getLocalFileSize($url) {
$fname = basename($url);
$resolution = 0;
if(strpos($fname,'.') !== false)
$fname = substr($fname,0,strpos($fname,'.'));
if(substr($fname,-2,1) == '-') {
$resolution = intval(substr($fname,-1,1));
$fname = substr($fname,0,-2);
}
$r = q("SELECT filesize FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
dbesc($fname),
intval($resolution)
);
if($r) {
return $r[0]['filesize'];
}
return null;
}
}
/**
* Try to determine the size of a remote file by making an HTTP request for
* a byte range, or look for the content-length header in the response.
* The function aborts the transfer as soon as the size is found, or if no
* length headers are returned, it aborts the transfer.
*
* @return int|null null if size could not be determined, or length of content
*/
function getRemoteFileSize($url)
{
$ch = curl_init($url);
$headers = array(
'Range: bytes=0-1',
'Connection: close',
);
$in_headers = true;
$size = null;
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2450.0 Iron/46.0.2450.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_VERBOSE, 0); // set to 1 to debug
curl_setopt($ch, CURLOPT_STDERR, fopen('php://output', 'r'));
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $line) use (&$in_headers, &$size) {
$length = strlen($line);
if (trim($line) == '') {
$in_headers = false;
}
list($header, $content) = explode(':', $line, 2);
$header = strtolower(trim($header));
if ($header == 'content-range') {
// found a content-range header
list($rng, $s) = explode('/', $content, 2);
$size = (int)$s;
return 0; // aborts transfer
} else if ($header == 'content-length' && 206 != curl_getinfo($curl, CURLINFO_HTTP_CODE)) {
// found content-length header and this is not a 206 Partial Content response (range response)
$size = (int)$content;
return 0;
} else {
// continue
return $length;
}
});
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use ($in_headers) {
if (!$in_headers) {
// shouldn't be here unless we couldn't determine file size
// abort transfer
return 0;
}
// write function is also called when reading headers
return strlen($data);
});
curl_exec($ch);
curl_getinfo($ch);
curl_close($ch);
return $size;
}

View file

@ -3,11 +3,13 @@
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
/** /**
* MarkdownSoap * @brief MarkdownSoap class.
*
* Purify Markdown for storage * Purify Markdown for storage
* @code{.php}
* $x = new MarkdownSoap($string_to_be_cleansed); * $x = new MarkdownSoap($string_to_be_cleansed);
* $text = $x->clean(); * $text = $x->clean();
* * @endcode
* What this does: * What this does:
* 1. extracts code blocks and privately escapes them from processing * 1. extracts code blocks and privately escapes them from processing
* 2. Run html purifier on the content * 2. Run html purifier on the content
@ -15,24 +17,28 @@ namespace Zotlabs\Lib;
* 4. run htmlspecialchars on the entire content for safe storage * 4. run htmlspecialchars on the entire content for safe storage
* *
* At render time: * At render time:
* @code{.php}
* $markdown = \Zotlabs\Lib\MarkdownSoap::unescape($text); * $markdown = \Zotlabs\Lib\MarkdownSoap::unescape($text);
* $html = \Michelf\MarkdownExtra::DefaultTransform($markdown); * $html = \Michelf\MarkdownExtra::DefaultTransform($markdown);
* @endcode
*/ */
class MarkdownSoap { class MarkdownSoap {
/**
* @var string
*/
private $str;
/**
* @var string
*/
private $token; private $token;
private $str;
function __construct($s) { function __construct($s) {
$this->str = $s; $this->str = $s;
$this->token = random_string(20); $this->token = random_string(20);
} }
function clean() { function clean() {
$x = $this->extract_code($this->str); $x = $this->extract_code($this->str);
@ -46,6 +52,15 @@ class MarkdownSoap {
return $x; return $x;
} }
/**
* @brief Extracts code blocks and privately escapes them from processing.
*
* @see encode_code()
* @see putback_code()
*
* @param string $s
* @return string
*/
function extract_code($s) { function extract_code($s) {
$text = preg_replace_callback('{ $text = preg_replace_callback('{
@ -71,6 +86,15 @@ class MarkdownSoap {
return base64_decode($matches[1]); return base64_decode($matches[1]);
} }
/**
* @brief Put back the code blocks.
*
* @see extract_code()
* @see decode_code()
*
* @param string $s
* @return string
*/
function putback_code($s) { function putback_code($s) {
$text = preg_replace_callback('{' . $this->token . '\;(.*?)\;}xm', [ $this, 'decode_code' ], $s); $text = preg_replace_callback('{' . $this->token . '\;(.*?)\;}xm', [ $this, 'decode_code' ], $s);
return $text; return $text;
@ -90,13 +114,18 @@ class MarkdownSoap {
function unprotect_autolinks($s) { function unprotect_autolinks($s) {
return $s; return $s;
} }
function escape($s) { function escape($s) {
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false); return htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false);
} }
/**
* @brief Converts special HTML entities back to characters.
*
* @param string $s
* @return string
*/
static public function unescape($s) { static public function unescape($s) {
return htmlspecialchars_decode($s, ENT_QUOTES); return htmlspecialchars_decode($s, ENT_QUOTES);
} }

View file

@ -68,6 +68,9 @@ class NativeWikiPage {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); 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 // create an empty activity
$arr = []; $arr = [];
@ -351,6 +354,7 @@ class NativeWikiPage {
// fetch the most recently saved revision. // fetch the most recently saved revision.
$item = self::load_page($arr); $item = self::load_page($arr);
if(! $item) { if(! $item) {
return array('message' => t('Page not found'), 'success' => false); return array('message' => t('Page not found'), 'success' => false);
} }

View file

@ -2,12 +2,36 @@
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
use \Zotlabs\Access as Zaccess; use Zotlabs\Access\PermissionRoles;
use Zotlabs\Access\Permissions;
/**
* @brief Permission Categories. Permission rules for various classes of connections.
*
* Connection permissions answer the question "Can Joe view my photos?"
*
* Some permissions may be inherited from the channel's "privacy settings"
* (@ref ::Zotlabs::Access::PermissionLimits "PermissionLimits") "Who can view my
* photos (at all)?" which have higher priority than individual connection settings.
* We evaluate permission limits first, and then fall through to connection
* permissions if the permission limits didn't already make a definitive decision.
*
* After PermissionLimits and connection permissions are evaluated, individual
* content ACLs are evaluated (@ref ::Zotlabs::Access::AccessList "AccessList").
* These answer the question "Can Joe view *this* album/photo?".
*/
class Permcat { class Permcat {
/**
* @var array
*/
private $permcats = []; private $permcats = [];
/**
* @brief Permcat constructor.
*
* @param int $channel_id
*/
public function __construct($channel_id) { public function __construct($channel_id) {
$perms = []; $perms = [];
@ -16,16 +40,16 @@ class Permcat {
$role = get_pconfig($channel_id,'system','permissions_role'); $role = get_pconfig($channel_id,'system','permissions_role');
if($role) { if($role) {
$x = Zaccess\PermissionRoles::role_perms($role); $x = PermissionRoles::role_perms($role);
if($x['perms_connect']) { if($x['perms_connect']) {
$perms = Zaccess\Permissions::FilledPerms($x['perms_connect']); $perms = Permissions::FilledPerms($x['perms_connect']);
} }
} }
// if no role perms it may be a custom role, see if there any autoperms // if no role perms it may be a custom role, see if there any autoperms
if(! $perms) { if(! $perms) {
$perms = Zaccess\Permissions::FilledAutoPerms($channel_id); $perms = Permissions::FilledAutoPerms($channel_id);
} }
// if no autoperms it may be a custom role with manual perms // if no autoperms it may be a custom role with manual perms
@ -50,13 +74,13 @@ class Permcat {
// nothing was found - create a filled permission array where all permissions are 0 // nothing was found - create a filled permission array where all permissions are 0
if(! $perms) { if(! $perms) {
$perms = Zaccess\Permissions::FilledPerms([]); $perms = Permissions::FilledPerms([]);
} }
$this->permcats[] = [ $this->permcats[] = [
'name' => 'default', 'name' => 'default',
'localname' => t('default','permcat'), 'localname' => t('default','permcat'),
'perms' => Zaccess\Permissions::Operms($perms), 'perms' => Permissions::Operms($perms),
'system' => 1 'system' => 1
]; ];
@ -67,18 +91,30 @@ class Permcat {
$this->permcats[] = [ $this->permcats[] = [
'name' => $p[$x][0], 'name' => $p[$x][0],
'localname' => $p[$x][1], 'localname' => $p[$x][1],
'perms' => Zaccess\Permissions::Operms(Zaccess\Permissions::FilledPerms($p[$x][2])), 'perms' => Permissions::Operms(Permissions::FilledPerms($p[$x][2])),
'system' => intval($p[$x][3]) 'system' => intval($p[$x][3])
]; ];
} }
} }
} }
/**
* @brief Return array with permcats.
*
* @return array
*/
public function listing() { public function listing() {
return $this->permcats; return $this->permcats;
} }
/**
* @brief
*
* @param string $name
* @return array
* * \e array with permcats
* * \e bool \b error if $name not found in permcats true
*/
public function fetch($name) { public function fetch($name) {
if($name && $this->permcats) { if($name && $this->permcats) {
foreach($this->permcats as $permcat) { foreach($this->permcats as $permcat) {
@ -87,6 +123,7 @@ class Permcat {
} }
} }
} }
return ['error' => true]; return ['error' => true];
} }
@ -120,15 +157,19 @@ class Permcat {
} }
} }
/**
* @hooks permcats
* * \e array
*/
call_hooks('permcats', $permcats); call_hooks('permcats', $permcats);
return $permcats; return $permcats;
} }
static public function find_permcat($arr, $name) { static public function find_permcat($arr, $name) {
if((! $arr) || (! $name)) if((! $arr) || (! $name))
return false; return false;
foreach($arr as $p) foreach($arr as $p)
if($p['name'] == $name) if($p['name'] == $name)
return $p['value']; return $p['value'];
@ -142,5 +183,4 @@ class Permcat {
PConfig::Delete($channel_id, 'permcat', $name); PConfig::Delete($channel_id, 'permcat', $name);
} }
} }

View file

@ -38,7 +38,7 @@ class ThreadItem {
$this->toplevel = ($this->get_id() == $this->get_data_value('parent')); $this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
// Prepare the children // Prepare the children
if(count($data['children'])) { if($data['children']) {
foreach($data['children'] as $item) { foreach($data['children'] as $item) {
/* /*
@ -105,7 +105,17 @@ class ThreadItem {
$mode = $conv->get_mode(); $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']) if(local_channel() && $observer['xchan_hash'] === $item['author_xchan'])
$edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit')); $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_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'] : ''); $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); $like_list_part = array_slice($like_list, 0, MAX_LIKERS);
array_push($like_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); array_push($like_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
} else { } else {
@ -198,7 +208,7 @@ class ThreadItem {
$dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : ''); $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_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : '');
$dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun'); $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); $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS);
array_push($dislike_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); array_push($dislike_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
} else { } else {
@ -360,6 +370,7 @@ class ThreadItem {
'unverified' => $unverified, 'unverified' => $unverified,
'forged' => $forged, 'forged' => $forged,
'location' => $location, 'location' => $location,
'divider' => get_pconfig($conv->get_profile_owner(),'system','item_divider'),
'attend_label' => t('Attend'), 'attend_label' => t('Attend'),
'attend_title' => t('Attendance Options'), 'attend_title' => t('Attendance Options'),
'vote_label' => t('Vote'), 'vote_label' => t('Vote'),

View file

@ -54,6 +54,14 @@ class ThreadStream {
$this->profile_owner = local_channel(); $this->profile_owner = local_channel();
$this->writable = true; $this->writable = true;
break; 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': case 'channel':
$this->profile_owner = \App::$profile['profile_uid']; $this->profile_owner = \App::$profile['profile_uid'];
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); $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->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
$this->reload = $_SESSION['return_url']; $this->reload = $_SESSION['return_url'];
break; 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': case 'display':
// in this mode we set profile_owner after initialisation (from conversation()) and then // 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 // 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)); $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'); require_once('include/channel.php');
$item->set_conversation($this); $item->set_conversation($this);

View file

@ -1,22 +1,25 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
/* require_once 'include/acl_selectors.php';
* ACL selector json backend require_once 'include/group.php';
/**
* @brief ACL selector json backend.
*
* This module provides JSON lists of connections and local/remote channels * This module provides JSON lists of connections and local/remote channels
* (xchans) to populate various tools such as the ACL (AccessControlList) popup * (xchans) to populate various tools such as the ACL (AccessControlList) popup
* and various auto-complete functions (such as email recipients, search, and * and various auto-complete functions (such as email recipients, search, and
* mention targets. * mention targets.
*
* There are two primary output structural formats. One for the ACL widget and * There are two primary output structural formats. One for the ACL widget and
* the other for auto-completion. * the other for auto-completion.
* Many of the behaviour variations are triggered on the use of single character keys *
* however this functionality has grown in an ad-hoc manner and has gotten quite messy over time. * Many of the behaviour variations are triggered on the use of single character
* keys however this functionality has grown in an ad-hoc manner and has gotten
* quite messy over time.
*/ */
require_once("include/acl_selectors.php");
require_once("include/group.php");
class Acl extends \Zotlabs\Web\Controller { class Acl extends \Zotlabs\Web\Controller {
function init() { function init() {
@ -383,8 +386,6 @@ class Acl extends \Zotlabs\Web\Controller {
'items' => $items, 'items' => $items,
); );
echo json_encode($o); echo json_encode($o);
killme(); killme();

View file

@ -29,12 +29,14 @@ class Site {
$maximagesize = ((x($_POST,'maximagesize')) ? intval(trim($_POST['maximagesize'])) : 0); $maximagesize = ((x($_POST,'maximagesize')) ? intval(trim($_POST['maximagesize'])) : 0);
$register_policy = ((x($_POST,'register_policy')) ? intval(trim($_POST['register_policy'])) : 0); $register_policy = ((x($_POST,'register_policy')) ? intval(trim($_POST['register_policy'])) : 0);
$minimum_age = ((x($_POST,'minimum_age')) ? intval(trim($_POST['minimum_age'])) : 13);
$access_policy = ((x($_POST,'access_policy')) ? intval(trim($_POST['access_policy'])) : 0); $access_policy = ((x($_POST,'access_policy')) ? intval(trim($_POST['access_policy'])) : 0);
$invite_only = ((x($_POST,'invite_only')) ? True : False); $invite_only = ((x($_POST,'invite_only')) ? True : False);
$abandon_days = ((x($_POST,'abandon_days')) ? intval(trim($_POST['abandon_days'])) : 0); $abandon_days = ((x($_POST,'abandon_days')) ? intval(trim($_POST['abandon_days'])) : 0);
$register_text = ((x($_POST,'register_text')) ? notags(trim($_POST['register_text'])) : ''); $register_text = ((x($_POST,'register_text')) ? notags(trim($_POST['register_text'])) : '');
$site_sellpage = ((x($_POST,'site_sellpage')) ? notags(trim($_POST['site_sellpage'])) : '');
$site_location = ((x($_POST,'site_location')) ? notags(trim($_POST['site_location'])) : '');
$frontpage = ((x($_POST,'frontpage')) ? notags(trim($_POST['frontpage'])) : ''); $frontpage = ((x($_POST,'frontpage')) ? notags(trim($_POST['frontpage'])) : '');
$mirror_frontpage = ((x($_POST,'mirror_frontpage')) ? intval(trim($_POST['mirror_frontpage'])) : 0); $mirror_frontpage = ((x($_POST,'mirror_frontpage')) ? intval(trim($_POST['mirror_frontpage'])) : 0);
$directory_server = ((x($_POST,'directory_server')) ? trim($_POST['directory_server']) : ''); $directory_server = ((x($_POST,'directory_server')) ? trim($_POST['directory_server']) : '');
@ -76,6 +78,8 @@ class Site {
set_config('system', 'poll_interval', $poll_interval); set_config('system', 'poll_interval', $poll_interval);
set_config('system', 'maxloadavg', $maxloadavg); set_config('system', 'maxloadavg', $maxloadavg);
set_config('system', 'frontpage', $frontpage); set_config('system', 'frontpage', $frontpage);
set_config('system', 'sellpage', $site_sellpage);
set_config('system', 'site_location', $site_location);
set_config('system', 'mirror_frontpage', $mirror_frontpage); set_config('system', 'mirror_frontpage', $mirror_frontpage);
set_config('system', 'sitename', $sitename); set_config('system', 'sitename', $sitename);
set_config('system', 'login_on_homepage', $login_on_homepage); set_config('system', 'login_on_homepage', $login_on_homepage);
@ -123,6 +127,7 @@ class Site {
set_config('system','maximagesize', $maximagesize); set_config('system','maximagesize', $maximagesize);
set_config('system','register_policy', $register_policy); set_config('system','register_policy', $register_policy);
set_config('system','minimum_age', $minimum_age);
set_config('system','invitation_only', $invite_only); set_config('system','invitation_only', $invite_only);
set_config('system','access_policy', $access_policy); set_config('system','access_policy', $access_policy);
set_config('system','account_abandon_days', $abandon_days); set_config('system','account_abandon_days', $abandon_days);
@ -251,6 +256,7 @@ class Site {
); );
$discover_tab = get_config('system','disable_discover_tab'); $discover_tab = get_config('system','disable_discover_tab');
// $disable public streams by default // $disable public streams by default
if($discover_tab === false) if($discover_tab === false)
$discover_tab = 1; $discover_tab = 1;
@ -298,6 +304,7 @@ class Site {
'$maximagesize' => array('maximagesize', t("Maximum image size"), intval(get_config('system','maximagesize')), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")), '$maximagesize' => array('maximagesize', t("Maximum image size"), intval(get_config('system','maximagesize')), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")),
'$register_policy' => array('register_policy', t("Does this site allow new member registration?"), get_config('system','register_policy'), "", $register_choices), '$register_policy' => array('register_policy', t("Does this site allow new member registration?"), get_config('system','register_policy'), "", $register_choices),
'$invite_only' => array('invite_only', t("Invitation only"), get_config('system','invitation_only'), t("Only allow new member registrations with an invitation code. Above register policy must be set to Yes.")), '$invite_only' => array('invite_only', t("Invitation only"), get_config('system','invitation_only'), t("Only allow new member registrations with an invitation code. Above register policy must be set to Yes.")),
'$minimum_age' => array('minimum_age', t("Minimum age"), (x(get_config('system','minimum_age'))?get_config('system','minimum_age'):13), t("Minimum age (in years) for who may register on this site.")),
'$access_policy' => array('access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system','access_policy'), "This is displayed on the public server site list.", $access_choices), '$access_policy' => array('access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system','access_policy'), "This is displayed on the public server site list.", $access_choices),
'$register_text' => array('register_text', t("Register text"), htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.")), '$register_text' => array('register_text', t("Register text"), htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.")),
'$frontpage' => array('frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system','frontpage'), t("example: 'public' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file.")), '$frontpage' => array('frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system','frontpage'), t("example: 'public' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file.")),
@ -327,6 +334,12 @@ class Site {
'$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.")), '$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.")), '$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')), '$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')),
'$sellpage' => array('site_sellpage', t('Public servers: Optional landing (marketing) webpage for new registrants'), get_config('system','sellpage',''), sprintf( t('Create this page first. Default is %s/register'),z_root())),
'$location' => array('site_location', t('Optional: site location'), get_config('system','site_location',''), t('Region or country')),
'$form_security_token' => get_form_security_token("admin_site"), '$form_security_token' => get_form_security_token("admin_site"),
)); ));
} }

View file

@ -39,10 +39,12 @@ class Api extends \Zotlabs\Web\Controller {
// get consumer/client from request token // get consumer/client from request token
try { try {
$request = OAuth1Request::from_request(); $request = \OAuth1Request::from_request();
} }
catch(\Exception $e) { catch(\Exception $e) {
echo "<pre>"; var_dump($e); killme(); logger('OAuth exception: ' . print_r($e,true));
// echo "<pre>"; var_dump($e);
killme();
} }
@ -52,7 +54,7 @@ class Api extends \Zotlabs\Web\Controller {
if (is_null($app)) if (is_null($app))
return "Invalid request. Unknown token."; return "Invalid request. Unknown token.";
$consumer = new OAuth1Consumer($app['client_id'], $app['pw'], $app['redirect_uri']); $consumer = new \OAuth1Consumer($app['client_id'], $app['pw'], $app['redirect_uri']);
$verifier = md5($app['secret'] . local_channel()); $verifier = md5($app['secret'] . local_channel());
set_config('oauth', $verifier, local_channel()); set_config('oauth', $verifier, local_channel());
@ -63,7 +65,7 @@ class Api extends \Zotlabs\Web\Controller {
$glue = '?'; $glue = '?';
if(strstr($consumer->callback_url,$glue)) if(strstr($consumer->callback_url,$glue))
$glue = '?'; $glue = '?';
goaway($consumer->callback_url . $glue . "oauth_token=" . OAuth1Util::urlencode_rfc3986($params['oauth_token']) . "&oauth_verifier=" . OAuth1Util::urlencode_rfc3986($verifier)); goaway($consumer->callback_url . $glue . "oauth_token=" . \OAuth1Util::urlencode_rfc3986($params['oauth_token']) . "&oauth_verifier=" . \OAuth1Util::urlencode_rfc3986($verifier));
killme(); killme();
} }

View file

@ -22,7 +22,8 @@ class Apps extends \Zotlabs\Web\Controller {
if(local_channel()) { if(local_channel()) {
Zlib\Apps::import_system_apps(); Zlib\Apps::import_system_apps();
$syslist = array(); $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) { if($list) {
foreach($list as $x) { foreach($list as $x) {
$syslist[] = Zlib\Apps::app_encode($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( return replace_macros(get_markup_template('myapps.tpl'), array(
'$sitename' => get_config('system','sitename'), '$sitename' => get_config('system','sitename'),
'$cat' => ((array_key_exists('cat',$_GET) && $_GET['cat']) ? escape_tags($_GET['cat']) : ''), '$cat' => $cat,
'$title' => t('Apps'), '$title' => t('Apps'),
'$apps' => $apps, '$apps' => $apps,
'$authed' => ((local_channel()) ? true : false), '$authed' => ((local_channel()) ? true : false),

View file

@ -0,0 +1,138 @@
<?php
namespace Zotlabs\Module;
require_once('include/channel.php');
require_once('include/acl_selectors.php');
require_once('include/conversation.php');
class Article_edit extends \Zotlabs\Web\Controller {
function get() {
// Figure out which post we're editing
$post_id = ((argc() > 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;
}
}

188
Zotlabs/Module/Articles.php Normal file
View file

@ -0,0 +1,188 @@
<?php
namespace Zotlabs\Module;
require_once('include/channel.php');
require_once('include/conversation.php');
require_once('include/acl_selectors.php');
class Articles extends \Zotlabs\Web\Controller {
function init() {
if(argc() > 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('Articles'));
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,
'body' => '[summary][/summary]',
'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;
}
}

View file

@ -39,7 +39,7 @@ class Cdav extends \Zotlabs\Web\Controller {
$sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]);
if($sigblock) { if($sigblock) {
$keyId = $sigblock['keyId']; $keyId = str_replace('acct:','',$sigblock['keyId']);
if($keyId) { if($keyId) {
$r = q("select * from hubloc where hubloc_addr = '%s' limit 1", $r = q("select * from hubloc where hubloc_addr = '%s' limit 1",
dbesc($keyId) dbesc($keyId)
@ -1250,7 +1250,7 @@ class Cdav extends \Zotlabs\Web\Controller {
//create default addressbook //create default addressbook
$carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo);
$properties = ['{DAV:}displayname' => t('Default Addressbook')]; $properties = ['{DAV:}displayname' => t('Default Addressbook')];
$carddavBackend->createAddressBook($uri, $default, $properties); $carddavBackend->createAddressBook($uri, 'default', $properties);
} }
} }

View file

@ -87,6 +87,10 @@ class Cloud extends \Zotlabs\Web\Controller {
// $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth)); // $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth));
// over-ride the default XML output on thrown exceptions
$server->on('exception', [ $this, 'DAVException' ]);
// All we need to do now, is to fire up the server // All we need to do now, is to fire up the server
$server->exec(); $server->exec();
@ -97,4 +101,24 @@ class Cloud extends \Zotlabs\Web\Controller {
killme(); 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();
}
}

View file

@ -567,7 +567,7 @@ class Connedit extends \Zotlabs\Web\Controller {
$contact_id = \App::$poi['abook_id']; $contact_id = \App::$poi['abook_id'];
$contact = \App::$poi; $contact = \App::$poi;
$cn = q("SELECT abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 order by xchan_name", $cn = q("SELECT abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 order by xchan_name",
intval(local_channel()) intval(local_channel())
); );
@ -866,7 +866,7 @@ class Connedit extends \Zotlabs\Web\Controller {
$o .= replace_macros($tpl, [ $o .= replace_macros($tpl, [
'$header' => (($self) ? t('Connection Default Permissions') : sprintf( t('Connection: %s'),$contact['xchan_name'])), '$header' => (($self) ? t('Connection Default Permissions') : sprintf( t('Connection: %s'),$contact['xchan_name'])),
'$autoperms' => array('autoperms',t('Apply these permissions automatically'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('Connection requests will be approved without your interaction'), $yes_no), '$autoperms' => array('autoperms',t('Apply these permissions automatically'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('Connection requests will be approved without your interaction'), $yes_no),
'$permcat' => [ 'permcat', t('Permission role'), '', '',$permcats ], '$permcat' => [ 'permcat', t('Permission role'), '', '<span class="loading invisible">' . t('Loading') . '<span class="jumping-dots"><span class="dot-1">.</span><span class="dot-2">.</span><span class="dot-3">.</span></span></span>',$permcats ],
'$permcat_new' => t('Add permission role'), '$permcat_new' => t('Add permission role'),
'$permcat_enable' => feature_enabled(local_channel(),'permcats'), '$permcat_enable' => feature_enabled(local_channel(),'permcats'),
'$addr' => $contact['xchan_addr'], '$addr' => $contact['xchan_addr'],

View file

@ -48,7 +48,7 @@ class Dav extends \Zotlabs\Web\Controller {
$sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]);
if($sigblock) { if($sigblock) {
$keyId = $sigblock['keyId']; $keyId = str_replace('acct:','',$sigblock['keyId']);
if($keyId) { if($keyId) {
$r = q("select * from hubloc where hubloc_addr = '%s' limit 1", $r = q("select * from hubloc where hubloc_addr = '%s' limit 1",
dbesc($keyId) dbesc($keyId)

View file

@ -237,7 +237,7 @@ class Defperms extends \Zotlabs\Web\Controller {
$o .= replace_macros($tpl, [ $o .= replace_macros($tpl, [
'$header' => t('Connection Default Permissions'), '$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), '$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' => [ 'permcat', t('Permission role'), '', '<span class="loading invisible">' . t('Loading') . '<span class="jumping-dots"><span class="dot-1">.</span><span class="dot-2">.</span><span class="dot-3">.</span></span></span>',$permcats ],
'$permcat_new' => t('Add permission role'), '$permcat_new' => t('Add permission role'),
'$permcat_enable' => feature_enabled(local_channel(),'permcats'), '$permcat_enable' => feature_enabled(local_channel(),'permcats'),
'$section' => $section, '$section' => $section,

View file

@ -313,7 +313,7 @@ class Dirsearch extends \Zotlabs\Web\Controller {
$ret['results'] = $entries; $ret['results'] = $entries;
if($kw) { if($kw) {
$k = dir_tagadelic($kw); $k = dir_tagadelic($kw, $hub);
if($k) { if($k) {
$ret['keywords'] = array(); $ret['keywords'] = array();
foreach($k as $kv) { foreach($k as $kv) {

View file

@ -14,6 +14,7 @@ class Display extends \Zotlabs\Web\Controller {
$module_format = 'html'; $module_format = 'html';
if(argc() > 1) { if(argc() > 1) {
$module_format = substr(argv(1),strrpos(argv(1),'.') + 1); $module_format = substr(argv(1),strrpos(argv(1),'.') + 1);
if(! in_array($module_format,['atom','zot','json'])) if(! in_array($module_format,['atom','zot','json']))
@ -30,7 +31,7 @@ class Display extends \Zotlabs\Web\Controller {
return; return;
} }
if(argc() > 1 && argv(1) !== 'load') { if(argc() > 1) {
$item_hash = argv(1); $item_hash = argv(1);
if($module_format !== 'html') { if($module_format !== 'html') {
$item_hash = substr($item_hash,0,strrpos($item_hash,'.')); $item_hash = substr($item_hash,0,strrpos($item_hash,'.'));
@ -285,7 +286,6 @@ class Display extends \Zotlabs\Web\Controller {
// make that content unsearchable by ensuring the owner_xchan can't match // make that content unsearchable by ensuring the owner_xchan can't match
if(! perm_is_allowed($sysid,$observer_hash,'view_stream')) if(! perm_is_allowed($sysid,$observer_hash,'view_stream'))
$sysid = 0; $sysid = 0;
$r = q("SELECT item.parent AS item_id from item $r = q("SELECT item.parent AS item_id from item
WHERE parent_mid = '%s' WHERE parent_mid = '%s'
AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = ''
@ -315,7 +315,6 @@ class Display extends \Zotlabs\Web\Controller {
WHERE parent in ( %s ) $item_normal ", WHERE parent in ( %s ) $item_normal ",
dbesc($parents_str) dbesc($parents_str)
); );
xchan_query($items); xchan_query($items);
$items = fetch_post_tags($items,true); $items = fetch_post_tags($items,true);
$items = conv_sort($items,'created'); $items = conv_sort($items,'created');

View file

@ -83,7 +83,7 @@ class Embedphotos extends \Zotlabs\Web\Controller {
return ''; return '';
if($args['album']) if($args['album'])
$album = $args['album']; $album = (($args['album'] === '/') ? '' : $args['album']);
if($args['title']) if($args['title'])
$title = $args['title']; $title = $args['title'];

View file

@ -16,12 +16,15 @@ class Feed extends \Zotlabs\Web\Controller {
$params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml'); $params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml');
$params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0); $params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0);
$params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0); $params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0);
$params['start'] = ((x($params,'start')) ? intval($params['start']) : 0); $params['start'] = ((x($_REQUEST,'start')) ? intval($_REQUEST['start']) : 0);
$params['records'] = ((x($params,'records')) ? intval($params['records']) : 40); $params['records'] = ((x($_REQUEST,'records')) ? intval($_REQUEST['records']) : 40);
$params['direction'] = ((x($params,'direction')) ? dbesc($params['direction']) : 'desc'); $params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc');
$params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : ''); $params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : '');
$params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 0); $params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 0);
if(! in_array($params['direction'],['asc','desc'])) {
$params['direction'] = 'desc';
}
if(argc() > 1) { if(argc() > 1) {

View file

@ -10,7 +10,8 @@ class File_upload extends \Zotlabs\Web\Controller {
function post() { function post() {
// logger('file upload: ' . print_r($_REQUEST,true)); logger('file upload: ' . print_r($_REQUEST,true));
logger('file upload: ' . print_r($_FILES,true));
$channel = (($_REQUEST['channick']) ? channelx_by_nick($_REQUEST['channick']) : null); $channel = (($_REQUEST['channick']) ? channelx_by_nick($_REQUEST['channick']) : null);
@ -47,6 +48,51 @@ class File_upload extends \Zotlabs\Web\Controller {
} }
} }
else { else {
$matches = [];
$partial = false;
if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) {
$pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches);
if($pm) {
logger('Content-Range: ' . print_r($matches,true));
$partial = true;
}
}
if($partial) {
$x = save_chunk($channel,$matches[1],$matches[2],$matches[3]);
if($x['partial']) {
header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
json_return_and_die($result);
}
else {
header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0));
$_FILES['userfile'] = [
'name' => $x['name'],
'type' => $x['type'],
'tmp_name' => $x['tmp_name'],
'error' => $x['error'],
'size' => $x['size']
];
}
}
else {
if(! array_key_exists('userfile',$_FILES)) {
$_FILES['userfile'] = [
'name' => $_FILES['files']['name'],
'type' => $_FILES['files']['type'],
'tmp_name' => $_FILES['files']['tmp_name'],
'error' => $_FILES['files']['error'],
'size' => $_FILES['files']['size']
];
}
}
$r = attach_store($channel, get_observer_hash(), '', $_REQUEST); $r = attach_store($channel, get_observer_hash(), '', $_REQUEST);
if($r['success']) { if($r['success']) {
$sync = attach_export_data($channel,$r['data']['hash']); $sync = attach_export_data($channel,$r['data']['hash']);

View file

@ -103,6 +103,11 @@ class Filestorage extends \Zotlabs\Web\Controller {
attach_delete($owner, $f['hash']); attach_delete($owner, $f['hash']);
$sync = attach_export_data($channel, $f['hash'], true);
if($sync) {
build_sync_packet($channel['channel_id'], array('file' => array($sync)));
}
goaway(dirname($url)); goaway(dirname($url));
} }

View file

@ -28,7 +28,7 @@ class Getfile extends \Zotlabs\Web\Controller {
function post() { function post() {
logger('post: ' . print_r($_POST,true),LOGGER_DEBUG,LOG_INFO); $header_verified = false;
$hash = $_POST['hash']; $hash = $_POST['hash'];
$time = $_POST['time']; $time = $_POST['time'];
@ -40,6 +40,40 @@ class Getfile extends \Zotlabs\Web\Controller {
if(! $hash) if(! $hash)
killme(); killme();
foreach([ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $head) {
if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') {
if($head !== 'HTTP_AUTHORIZATION') {
$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head];
continue;
}
$sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]);
if($sigblock) {
$keyId = $sigblock['keyId'];
if($keyId) {
$r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash
where hubloc_addr = '%s' limit 1",
dbesc(str_replace('acct:','',$keyId))
);
if($r) {
$hubloc = $r[0];
$verified = \Zotlabs\Web\HTTPSig::verify('',$hubloc['xchan_pubkey']);
if($verified && $verified['header_signed'] && $verified['header_valid'] && $hash == $hubloc['hubloc_hash']) {
$header_verified = true;
}
}
}
}
}
}
logger('post: ' . print_r($_POST,true),LOGGER_DEBUG,LOG_INFO);
if($header_verified) {
logger('HTTPSig verified');
}
$channel = channelx_by_hash($hash); $channel = channelx_by_hash($hash);
if((! $channel) || (! $time) || (! $sig)) { if((! $channel) || (! $time) || (! $sig)) {
@ -59,6 +93,7 @@ class Getfile extends \Zotlabs\Web\Controller {
$d1 = datetime_convert('UTC','UTC',"now + $slop minutes"); $d1 = datetime_convert('UTC','UTC',"now + $slop minutes");
$d2 = datetime_convert('UTC','UTC',"now - $slop minutes"); $d2 = datetime_convert('UTC','UTC',"now - $slop minutes");
if(! $header_verified) {
if(($time > $d1) || ($time < $d2)) { if(($time > $d1) || ($time < $d2)) {
logger('time outside allowable range'); logger('time outside allowable range');
killme(); killme();
@ -68,7 +103,7 @@ class Getfile extends \Zotlabs\Web\Controller {
logger('verify failed.'); logger('verify failed.');
killme(); killme();
} }
}
if($resolution > 0) { if($resolution > 0) {
$r = q("select * from photo where resource_id = '%s' and uid = %d limit 1", $r = q("select * from photo where resource_id = '%s' and uid = %d limit 1",

View file

@ -10,6 +10,13 @@ require_once('include/items.php');
class Hq extends \Zotlabs\Web\Controller { class Hq extends \Zotlabs\Web\Controller {
function init() {
if(! local_channel())
return;
\App::$profile_uid = local_channel();
}
function post() { function post() {
if(!local_channel()) if(!local_channel())
@ -43,67 +50,26 @@ class Hq extends \Zotlabs\Web\Controller {
$item_normal_update = item_normal_update(); $item_normal_update = item_normal_update();
if(! $item_hash) { if(! $item_hash) {
$r = q("SELECT mid FROM item $r = q("SELECT mid FROM item
WHERE uid = %d WHERE uid = %d
AND item_thread_top = 1 AND mid = parent_mid
ORDER BY created DESC ORDER BY created DESC LIMIT 1",
limit 1",
intval(local_channel()) intval(local_channel())
); );
if(!$r[0]['mid']) { if($r[0]['mid']) {
\App::$error = 404;
notice( t('Item not found.') . EOL);
return;
}
$item_hash = 'b64.' . base64url_encode($r[0]['mid']); $item_hash = 'b64.' . base64url_encode($r[0]['mid']);
} }
}
if($item_hash) {
if(strpos($item_hash,'b64.') === 0) if(strpos($item_hash,'b64.') === 0)
$decoded = @base64url_decode(substr($item_hash,4)); $decoded = @base64url_decode(substr($item_hash,4));
if($decoded) if($decoded)
$item_hash = $decoded; $item_hash = $decoded;
$updateable = false;
if(! $update) {
$channel = \App::get_channel();
$channel_acl = [
'allow_cid' => $channel['channel_allow_cid'],
'allow_gid' => $channel['channel_allow_gid'],
'deny_cid' => $channel['channel_deny_cid'],
'deny_gid' => $channel['channel_deny_gid']
];
$x = [
'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,
'editor_autocomplete' => true,
'bbco_autocomplete' => 'bbcode',
'bbcode' => true,
'jotnets' => true
];
$o = '<div id="jot-popup">';
$o .= status_editor($a,$x);
$o .= '</div>';
}
$target_item = null; $target_item = null;
$r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid like '%s' limit 1", $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid like '%s' limit 1",
@ -125,32 +91,83 @@ class Hq extends \Zotlabs\Web\Controller {
if($update && $_SESSION['loadtime']) if($update && $_SESSION['loadtime'])
$simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) "; $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) ";
if($load)
$simple_update = '';
if($static && $simple_update) if($static && $simple_update)
$simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' ";
$sys = get_sys_channel();
$sql_extra = item_permissions_sql($sys['channel_id']);
$sys_item = false;
}
if(! $update) {
$channel = \App::get_channel();
$channel_acl = [
'allow_cid' => $channel['channel_allow_cid'],
'allow_gid' => $channel['channel_allow_gid'],
'deny_cid' => $channel['channel_deny_cid'],
'deny_gid' => $channel['channel_deny_gid']
];
$x = [
'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' => 'hq',
'expanded' => true,
'editor_autocomplete' => true,
'bbco_autocomplete' => 'bbcode',
'bbcode' => true,
'jotnets' => true
];
$o = replace_macros(get_markup_template("hq.tpl"),
[
'$no_messages' => (($target_item) ? false : true),
'$no_messages_label' => [ t('Welcome to Hubzilla!'), t('You have got no unseen posts...') ],
'$editor' => status_editor($a,$x)
]
);
}
if(! $update && ! $load) { if(! $update && ! $load) {
nav_set_selected('HQ');
$static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1);
if($target_item) {
// if the target item is not a post (eg a like) we want to address its thread parent // if the target item is not a post (eg a like) we want to address its thread parent
$mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); $mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']);
// if we got a decoded hash we must encode it again before handing to javascript // if we got a decoded hash we must encode it again before handing to javascript
if($decoded) if($decoded)
$mid = 'b64.' . base64url_encode($mid); $mid = 'b64.' . base64url_encode($mid);
}
else {
$mid = '';
}
$o .= '<div id="live-display"></div>' . "\r\n"; $o .= '<div id="live-hq"></div>' . "\r\n";
$o .= "<script> var profile_uid = " . local_channel() $o .= "<script> var profile_uid = " . local_channel()
. "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . ";</script>\r\n"; . "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . ";</script>\r\n";
\App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),[ \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),[
'$baseurl' => z_root(), '$baseurl' => z_root(),
'$pgtype' => 'display', '$pgtype' => 'hq',
'$uid' => '0', '$uid' => local_channel(),
'$gid' => '0', '$gid' => '0',
'$cid' => '0', '$cid' => '0',
'$cmin' => '0', '$cmin' => '0',
@ -177,71 +194,94 @@ class Hq extends \Zotlabs\Web\Controller {
'$net' => '', '$net' => '',
'$mid' => $mid '$mid' => $mid
]); ]);
} }
if($load) { $updateable = false;
if($load && $target_item) {
$r = null; $r = null;
$r = q("SELECT item.id as item_id from item $r = q("SELECT item.id AS item_id FROM item
WHERE uid = %d WHERE uid = %d
and mid = '%s' AND mid = '%s'
$item_normal $item_normal
limit 1", LIMIT 1",
intval(local_channel()), intval(local_channel()),
dbesc($target_item['parent_mid']) dbesc($target_item['parent_mid'])
); );
if($r) { if($r) {
$updateable = true; $updateable = true;
} }
} if(!$r) {
$sys_item = true;
elseif($update) { $r = q("SELECT item.id AS item_id FROM item
LEFT JOIN abook ON item.author_xchan = abook.abook_xchan
WHERE mid = '%s' AND item.uid = %d $item_normal
AND (abook.abook_blocked = 0 or abook.abook_flags is null)
$sql_extra LIMIT 1",
dbesc($target_item['parent_mid']),
intval($sys['channel_id'])
);
}
}
elseif($update && $target_item) {
$r = null; $r = null;
$r = q("SELECT item.parent AS item_id from item $r = q("SELECT item.parent AS item_id FROM item
WHERE uid = %d WHERE uid = %d
and parent_mid = '%s' AND parent_mid = '%s'
$item_normal_update $item_normal_update
$simple_update $simple_update
limit 1", LIMIT 1",
intval(local_channel()), intval(local_channel()),
dbesc($target_item['parent_mid']) dbesc($target_item['parent_mid'])
); );
if($r) { if($r) {
$updateable = true; $updateable = true;
} }
if(!$r) {
$sys_item = true;
$r = q("SELECT item.parent AS item_id FROM item
LEFT JOIN abook ON item.author_xchan = abook.abook_xchan
WHERE mid = '%s' AND item.uid = %d $item_normal_update $simple_update
AND (abook.abook_blocked = 0 or abook.abook_flags is null)
$sql_extra LIMIT 1",
dbesc($target_item['parent_mid']),
intval($sys['channel_id'])
);
}
$_SESSION['loadtime'] = datetime_convert(); $_SESSION['loadtime'] = datetime_convert();
} }
else { else {
$r = []; $r = [];
} }
if($r) { if($r) {
$parents_str = ids_to_querystr($r,'item_id');
if($parents_str) {
$items = q("SELECT item.*, item.id AS item_id $items = q("SELECT item.*, item.id AS item_id
FROM item FROM item
WHERE parent in ( %s ) $item_normal ", WHERE parent = '%s' $item_normal ",
dbesc($parents_str) dbesc($r[0]['item_id'])
); );
xchan_query($items); xchan_query($items,true,(($sys_item) ? local_channel() : 0));
$items = fetch_post_tags($items,true); $items = fetch_post_tags($items,true);
$items = conv_sort($items,'created'); $items = conv_sort($items,'created');
} }
}
else { else {
$items = []; $items = [];
} }
$o .= conversation($items, 'display', $update, 'client'); $o .= conversation($items, 'hq', $update, 'client');
if($updateable) { if($updateable) {
$x = q("UPDATE item SET item_unseen = 0 where item_unseen = 1 AND uid = %d and parent = %d ", $x = q("UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 AND uid = %d AND parent = %d ",
intval(local_channel()), intval(local_channel()),
intval($r[0]['item_id']) intval($r[0]['item_id'])
); );
@ -249,10 +289,6 @@ class Hq extends \Zotlabs\Web\Controller {
$o .= '<div id="content-complete"></div>'; $o .= '<div id="content-complete"></div>';
if(($update && $load) && (! $items)) {
notice( t('Something went wrong.') . EOL );
}
return $o; return $o;
} }

View file

@ -26,6 +26,8 @@ class Impel extends \Zotlabs\Web\Controller {
if(! $j) if(! $j)
json_return_and_die($ret); json_return_and_die($ret);
// logger('element: ' . print_r($j,true));
$channel = \App::get_channel(); $channel = \App::get_channel();
$arr = array(); $arr = array();

View file

@ -59,6 +59,7 @@ class Item extends \Zotlabs\Web\Controller {
$profile_uid = ((x($_REQUEST,'profile_uid')) ? intval($_REQUEST['profile_uid']) : 0); $profile_uid = ((x($_REQUEST,'profile_uid')) ? intval($_REQUEST['profile_uid']) : 0);
require_once('include/channel.php'); require_once('include/channel.php');
$sys = get_sys_channel(); $sys = get_sys_channel();
if($sys && $profile_uid && ($sys['channel_id'] == $profile_uid) && is_site_admin()) { if($sys && $profile_uid && ($sys['channel_id'] == $profile_uid) && is_site_admin()) {
$uid = intval($sys['channel_id']); $uid = intval($sys['channel_id']);
@ -155,7 +156,7 @@ class Item extends \Zotlabs\Web\Controller {
if(! x($_REQUEST,'type')) if(! x($_REQUEST,'type'))
$_REQUEST['type'] = 'net-comment'; $_REQUEST['type'] = 'net-comment';
if($obj_type == ACTIVITY_OBJ_POST) if($obj_type == ACTIVITY_OBJ_NOTE)
$obj_type = ACTIVITY_OBJ_COMMENT; $obj_type = ACTIVITY_OBJ_COMMENT;
if($parent) { if($parent) {
@ -171,7 +172,7 @@ class Item extends \Zotlabs\Web\Controller {
); );
} }
// if this isn't the real parent of the conversation, find it // if this isn't the real parent of the conversation, find it
if($r !== false && count($r)) { if($r) {
$parid = $r[0]['parent']; $parid = $r[0]['parent'];
$parent_mid = $r[0]['mid']; $parent_mid = $r[0]['mid'];
if($r[0]['id'] != $r[0]['parent']) { if($r[0]['id'] != $r[0]['parent']) {
@ -179,9 +180,16 @@ class Item extends \Zotlabs\Web\Controller {
intval($parid) intval($parid)
); );
} }
// if interacting with a pubstream item,
// create a copy of the parent in your stream
if($r[0]['uid'] === $sys['channel_id'] && local_channel()) {
$r = [ copy_of_pubitem(\App::get_channel(), $r[0]['mid']) ];
}
} }
if(($r === false) || (! count($r))) { if(! $r) {
notice( t('Unable to locate original post.') . EOL); notice( t('Unable to locate original post.') . EOL);
if($api_source) if($api_source)
return ( [ 'success' => false, 'message' => 'invalid post id' ] ); return ( [ 'success' => false, 'message' => 'invalid post id' ] );
@ -190,10 +198,7 @@ class Item extends \Zotlabs\Web\Controller {
killme(); killme();
} }
// can_comment_on_post() needs info from the following xchan_query xchan_query($r,true);
// This may be from the discover tab which means we need to correct the effective uid
xchan_query($r,true,(($r[0]['uid'] == local_channel()) ? 0 : local_channel()));
$parent_item = $r[0]; $parent_item = $r[0];
$parent = $r[0]['id']; $parent = $r[0]['id'];
@ -499,7 +504,12 @@ class Item extends \Zotlabs\Web\Controller {
$body = z_input_filter($body,$mimetype,$execflag); $body = z_input_filter($body,$mimetype,$execflag);
} }
// Verify ability to use html or php!!!
$arr = [ 'profile_uid' => $profile_uid, 'content' => $body, 'mimetype' => $mimetype ];
call_hooks('post_content',$arr);
$body = $arr['content'];
$mimetype = $arr['mimetype'];
$gacl = $acl->get(); $gacl = $acl->get();
$str_contact_allow = $gacl['allow_cid']; $str_contact_allow = $gacl['allow_cid'];
@ -511,13 +521,6 @@ class Item extends \Zotlabs\Web\Controller {
require_once('include/text.php'); require_once('include/text.php');
if($uid && $uid == $profile_uid && feature_enabled($uid,'markdown')) {
require_once('include/markdown.php');
$body = preg_replace_callback('/\[share(.*?)\]/ism','\share_shield',$body);
$body = markdown_to_bb($body,true,['preserve_lf' => true]);
$body = preg_replace_callback('/\[share(.*?)\]/ism','\share_unshield',$body);
}
// BBCODE alert: the following functions assume bbcode input // BBCODE alert: the following functions assume bbcode input
// and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.) // and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.)
@ -629,6 +632,9 @@ class Item extends \Zotlabs\Web\Controller {
if($webpage == ITEM_TYPE_CARD) { if($webpage == ITEM_TYPE_CARD) {
$catlink = z_root() . '/cards/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat)); $catlink = z_root() . '/cards/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat));
} }
elseif($webpage == ITEM_TYPE_ARTICLE) {
$catlink = z_root() . '/articles/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat));
}
else { else {
$catlink = $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)); $catlink = $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat));
} }
@ -733,6 +739,18 @@ class Item extends \Zotlabs\Web\Controller {
} }
} }
if($webpage == ITEM_TYPE_ARTICLE) {
$plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : substr($mid,0,16));
}
if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_ARTICLE)) {
$r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.iid = %d limit 1",
intval($parent_item['id'])
);
if($r) {
$plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . $r[0]['v'];
}
}
if ((! $plink) && ($item_thread_top)) { if ((! $plink) && ($item_thread_top)) {
$plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $mid; $plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $mid;
} }
@ -1120,6 +1138,28 @@ class Item extends \Zotlabs\Web\Controller {
return $ret; return $ret;
} }
// auto-upgrade beginner (techlevel 0) accounts - if they have at least two friends and ten posts
// and have uploaded something (like a profile photo), promote them to level 1.
$a = q("select account_id, account_level from account where account_id = (select channel_account_id from channel where channel_id = %d limit 1)",
intval($channel_id)
);
if((! intval($a[0]['account_level'])) && intval($r[0]['total']) > 10) {
$x = q("select count(abook_id) as total from abook where abook_channel = %d",
intval($channel_id)
);
if($x && intval($x[0]['total']) > 2) {
$y = q("select count(id) as total from attach where uid = %d",
intval($channel_id)
);
if($y && intval($y[0]['total']) > 1) {
q("update account set account_level = 1 where account_id = %d limit 1",
intval($a[0]['account_id'])
);
}
}
}
if (!$iswebpage) { if (!$iswebpage) {
$max = engr_units_to_bytes(service_class_fetch($channel_id,'total_items')); $max = engr_units_to_bytes(service_class_fetch($channel_id,'total_items'));
if(! service_class_allows($channel_id,'total_items',$r[0]['total'])) { if(! service_class_allows($channel_id,'total_items',$r[0]['total'])) {

View file

@ -162,12 +162,12 @@ class Layouts extends \Zotlabs\Web\Controller {
'created' => $rr['created'], 'created' => $rr['created'],
'edited' => $rr['edited'], 'edited' => $rr['edited'],
'mimetype' => $rr['mimetype'], 'mimetype' => $rr['mimetype'],
'pagetitle' => $rr['sid'], 'pagetitle' => urldecode($rr['v']),
'mid' => $rr['mid'] 'mid' => $rr['mid']
); );
$pages[$rr['iid']][] = array( $pages[$rr['iid']][] = array(
'url' => $rr['iid'], 'url' => $rr['iid'],
'title' => $rr['v'], 'title' => urldecode($rr['v']),
'descr' => $rr['title'], 'descr' => $rr['title'],
'mid' => $rr['mid'], 'mid' => $rr['mid'],
'created' => $rr['created'], 'created' => $rr['created'],

View file

@ -258,20 +258,27 @@ class Like extends \Zotlabs\Web\Controller {
// get the item. Allow linked photos (which are normally hidden) to be liked // get the item. Allow linked photos (which are normally hidden) to be liked
$r = q("SELECT * FROM item WHERE id = %d $r = q("SELECT * FROM item WHERE id = %d
and (item_type = 0 or item_type = 6) and item_deleted = 0 and item_unpublished = 0 and item_type in (0,6,7) and item_deleted = 0 and item_unpublished = 0
and item_delayed = 0 and item_pending_remove = 0 and item_blocked = 0 LIMIT 1", and item_delayed = 0 and item_pending_remove = 0 and item_blocked = 0 LIMIT 1",
intval($item_id) intval($item_id)
); );
// if interacting with a pubstream item,
// create a copy of the parent in your stream. If not the conversation
// parent, copy that as well.
if($r) {
if($r[0]['uid'] === $sys_channel['channel_id'] && local_channel()) {
$r = [ copy_of_pubitem(\App::get_channel(), $r[0]['mid']) ];
}
}
if(! $item_id || (! $r)) { if(! $item_id || (! $r)) {
logger('like: no item ' . $item_id); logger('like: no item ' . $item_id);
killme(); killme();
} }
// Use the $effective_uid option of xchan_query to sort out comment permission xchan_query($r,true);
// for public stream items
xchan_query($r,true,(($r[0]['uid'] == $sys_channel_id) ? local_channel() : 0));
$item = $r[0]; $item = $r[0];

View file

@ -35,8 +35,11 @@ class Network extends \Zotlabs\Web\Controller {
return login(false); return login(false);
} }
if($load) $o = '';
if($load) {
$_SESSION['loadtime'] = datetime_convert(); $_SESSION['loadtime'] = datetime_convert();
}
$arr = array('query' => \App::$query_string); $arr = array('query' => \App::$query_string);
@ -104,7 +107,9 @@ class Network extends \Zotlabs\Web\Controller {
$def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>'); $def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>');
} }
$o = '';
$default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0);
$default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99);
// if no tabs are selected, defaults to comments // if no tabs are selected, defaults to comments
@ -115,8 +120,8 @@ class Network extends \Zotlabs\Web\Controller {
$liked = ((x($_GET,'liked')) ? intval($_GET['liked']) : 0); $liked = ((x($_GET,'liked')) ? intval($_GET['liked']) : 0);
$conv = ((x($_GET,'conv')) ? intval($_GET['conv']) : 0); $conv = ((x($_GET,'conv')) ? intval($_GET['conv']) : 0);
$spam = ((x($_GET,'spam')) ? intval($_GET['spam']) : 0); $spam = ((x($_GET,'spam')) ? intval($_GET['spam']) : 0);
$cmin = ((x($_GET,'cmin')) ? intval($_GET['cmin']) : 0); $cmin = ((array_key_exists('cmin',$_GET)) ? intval($_GET['cmin']) : $default_cmin);
$cmax = ((x($_GET,'cmax')) ? intval($_GET['cmax']) : 99); $cmax = ((array_key_exists('cmax',$_GET)) ? intval($_GET['cmax']) : $default_cmax);
$file = ((x($_GET,'file')) ? $_GET['file'] : ''); $file = ((x($_GET,'file')) ? $_GET['file'] : '');
$xchan = ((x($_GET,'xchan')) ? $_GET['xchan'] : ''); $xchan = ((x($_GET,'xchan')) ? $_GET['xchan'] : '');
$net = ((x($_GET,'net')) ? $_GET['net'] : ''); $net = ((x($_GET,'net')) ? $_GET['net'] : '');
@ -404,7 +409,6 @@ class Network extends \Zotlabs\Web\Controller {
if($cmax == 99) if($cmax == 99)
$sql_nets .= " OR abook.abook_closeness IS NULL ) "; $sql_nets .= " OR abook.abook_closeness IS NULL ) ";
} }
$net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : '');
@ -473,7 +477,6 @@ class Network extends \Zotlabs\Web\Controller {
if($load) { if($load) {
// Fetch a page full of parent items for this page // Fetch a page full of parent items for this page
$r = q("SELECT distinct item.id AS item_id, $ordering FROM item $r = q("SELECT distinct item.id AS item_id, $ordering FROM item
left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids )
$net_query $net_query
@ -484,7 +487,6 @@ class Network extends \Zotlabs\Web\Controller {
$net_query2 $net_query2
ORDER BY $ordering DESC $pager_sql " ORDER BY $ordering DESC $pager_sql "
); );
} }
else { else {

View file

@ -45,6 +45,8 @@ class Oep extends \Zotlabs\Web\Controller {
$arr = $this->oep_profile_reply($_REQUEST); $arr = $this->oep_profile_reply($_REQUEST);
elseif(fnmatch('*/cards/*',$url)) elseif(fnmatch('*/cards/*',$url))
$arr = $this->oep_cards_reply($_REQUEST); $arr = $this->oep_cards_reply($_REQUEST);
elseif(fnmatch('*/articles/*',$url))
$arr = $this->oep_articles_reply($_REQUEST);
if($arr) { if($arr) {
if($html) { if($html) {
@ -232,6 +234,89 @@ class Oep extends \Zotlabs\Web\Controller {
} }
function oep_articles_reply($args) {
$ret = [];
$url = $args['url'];
$maxwidth = intval($args['maxwidth']);
$maxheight = intval($args['maxheight']);
if(preg_match('#//(.*?)/articles/(.*?)/(.*?)(&|\?|$)#',$url,$matches)) {
$nick = $matches[2];
$res = $matches[3];
}
if(! ($nick && $res))
return $ret;
$channel = channelx_by_nick($nick);
if(! $channel)
return $ret;
if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages'))
return $ret;
$sql_extra = item_permissions_sql($channel['channel_id'],get_observer_hash());
$r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.v = '%s' limit 1",
dbesc($res)
);
if($r) {
$sql_extra = "and item.id = " . intval($r[0]['iid']) . " ";
}
else {
return $ret;
}
$r = q("select * from item
where item.uid = %d and item_type = %d
$sql_extra order by item.created desc",
intval($channel['channel_id']),
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) {
xchan_query($r);
$p = fetch_post_tags($r, true);
}
$x = '2eGriplW^*Jmf4';
$o = "[share author='".urlencode($p[0]['author']['xchan_name']).
"' profile='".$p[0]['author']['xchan_url'] .
"' avatar='".$p[0]['author']['xchan_photo_s'].
"' link='".$p[0]['plink'].
"' posted='".$p[0]['created'].
"' message_id='".$p[0]['mid']."']";
if($p[0]['title'])
$o .= '[b]'.$p[0]['title'].'[/b]'."\r\n";
$o .= $x;
$o .= "[/share]";
$o = bbcode($o);
$o = str_replace($x,bbcode($p[0]['body']),$o);
$ret['type'] = 'rich';
$w = (($maxwidth) ? $maxwidth : 640);
$h = (($maxheight) ? $maxheight : intval($w * 2 / 3));
$ret['html'] = '<div style="width: ' . $w . '; height: ' . $h . '; font-family: sans-serif,arial,freesans;" >' . $o . '</div>';
$ret['width'] = $w;
$ret['height'] = $h;
return $ret;
}
function oep_mid_reply($args) { function oep_mid_reply($args) {

View file

@ -17,12 +17,15 @@ class Ofeed extends \Zotlabs\Web\Controller {
$params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml'); $params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml');
$params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0); $params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0);
$params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0); $params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0);
$params['start'] = ((x($params,'start')) ? intval($params['start']) : 0); $params['start'] = ((x($_REQUEST,'start')) ? intval($_REQUEST['start']) : 0);
$params['records'] = ((x($params,'records')) ? intval($params['records']) : 10); $params['records'] = ((x($_REQUEST,'records')) ? intval($_REQUEST['records']) : 10);
$params['direction'] = ((x($params,'direction')) ? dbesc($params['direction']) : 'desc'); $params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc');
$params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : ''); $params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : '');
$params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 1); $params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 1);
if(! in_array($params['direction'],['asc','desc'])) {
$params['direction'] = 'desc';
}
if(argc() > 1) { if(argc() > 1) {

View file

@ -31,19 +31,26 @@ class Owa extends \Zotlabs\Web\Controller {
if($keyId) { if($keyId) {
$r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash
where hubloc_addr = '%s' limit 1", where hubloc_addr = '%s' ",
dbesc(str_replace('acct:','',$keyId)) dbesc(str_replace('acct:','',$keyId))
); );
if($r) { if($r) {
$hubloc = $r[0]; foreach($r as $hubloc) {
$verified = \Zotlabs\Web\HTTPSig::verify('',$hubloc['xchan_pubkey']); $verified = \Zotlabs\Web\HTTPSig::verify('',$hubloc['xchan_pubkey']);
if($verified && $verified['header_signed'] && $verified['header_valid']) { if($verified && $verified['header_signed'] && $verified['header_valid']) {
logger('OWA header: ' . print_r($verified,true),LOGGER_DATA);
logger('OWA success: ' . $hubloc['hubloc_addr'],LOGGER_DATA);
$ret['success'] = true; $ret['success'] = true;
$token = random_string(32); $token = random_string(32);
\Zotlabs\Zot\Verify::create('owt',0,$token,$r[0]['hubloc_addr']); \Zotlabs\Zot\Verify::create('owt',0,$token,$hubloc['hubloc_addr']);
$result = ''; $result = '';
openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']); openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']);
$ret['encrypted_token'] = base64url_encode($result); $ret['encrypted_token'] = base64url_encode($result);
break;
}
else {
logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_addr']);
}
} }
} }
} }

View file

@ -202,6 +202,11 @@ class Photos extends \Zotlabs\Web\Controller {
); );
if(($m) && ($m[0]['folder'] != $_POST['move_to_album'])) { if(($m) && ($m[0]['folder'] != $_POST['move_to_album'])) {
attach_move($page_owner_uid,argv(2),$_POST['move_to_album']); attach_move($page_owner_uid,argv(2),$_POST['move_to_album']);
$sync = attach_export_data(\App::$data['channel'],argv(2),true);
if($sync)
build_sync_packet($page_owner_uid,array('file' => array($sync)));
if(! ($_POST['desc'] && $_POST['newtag'])) if(! ($_POST['desc'] && $_POST['newtag']))
goaway(z_root() . '/' . $_SESSION['photo_return']); goaway(z_root() . '/' . $_SESSION['photo_return']);
} }
@ -465,6 +470,51 @@ class Photos extends \Zotlabs\Web\Controller {
$_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']); $_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']);
} }
$matches = [];
$partial = false;
if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) {
$pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches);
if($pm) {
logger('Content-Range: ' . print_r($matches,true));
$partial = true;
}
}
if($partial) {
$x = save_chunk($channel,$matches[1],$matches[2],$matches[3]);
if($x['partial']) {
header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
json_return_and_die($result);
}
else {
header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0));
$_FILES['userfile'] = [
'name' => $x['name'],
'type' => $x['type'],
'tmp_name' => $x['tmp_name'],
'error' => $x['error'],
'size' => $x['size']
];
}
}
else {
if(! array_key_exists('userfile',$_FILES)) {
$_FILES['userfile'] = [
'name' => $_FILES['files']['name'],
'type' => $_FILES['files']['type'],
'tmp_name' => $_FILES['files']['tmp_name'],
'error' => $_FILES['files']['error'],
'size' => $_FILES['files']['size']
];
}
}
$r = attach_store($channel,get_observer_hash(), '', $_REQUEST); $r = attach_store($channel,get_observer_hash(), '', $_REQUEST);
if(! $r['success']) { if(! $r['success']) {
@ -557,7 +607,10 @@ class Photos extends \Zotlabs\Web\Controller {
nav_set_selected('Photos'); nav_set_selected('Photos');
$o = ""; $o = '<script src="library/blueimp_upload/js/vendor/jquery.ui.widget.js"></script>
<script src="library/blueimp_upload/js/jquery.iframe-transport.js"></script>
<script src="library/blueimp_upload/js/jquery.fileupload.js"></script>';
$o .= "<script> var profile_uid = " . \App::$profile['profile_uid'] $o .= "<script> var profile_uid = " . \App::$profile['profile_uid']
. "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . "; </script>\r\n"; . "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . "; </script>\r\n";
@ -656,7 +709,7 @@ class Photos extends \Zotlabs\Web\Controller {
'$uploader' => $ret['addon_text'], '$uploader' => $ret['addon_text'],
'$default' => (($ret['default_upload']) ? true : false), '$default' => (($ret['default_upload']) ? true : false),
'$uploadurl' => $ret['post_url'], '$uploadurl' => $ret['post_url'],
'$submit' => t('Submit') '$submit' => t('Upload')
)); ));
@ -1052,7 +1105,7 @@ class Photos extends \Zotlabs\Web\Controller {
} }
$comments = ''; $comments = '';
if(! count($r)) { if(! $r) {
if($observer && ($can_post || $can_comment)) { if($observer && ($can_post || $can_comment)) {
$commentbox = replace_macros($cmnt_tpl,array( $commentbox = replace_macros($cmnt_tpl,array(
'$return_path' => '', '$return_path' => '',

View file

@ -140,7 +140,7 @@ class Ping extends \Zotlabs\Web\Controller {
db_utcnow(), db_quoteinterval('3 MINUTE') db_utcnow(), db_quoteinterval('3 MINUTE')
); );
$discover_tab_on = ((get_config('system','disable_discover_tab') != 1) ? true : false); $discover_tab_on = ((get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false) ? false : true);
$notify_pubs = ((local_channel()) ? ($vnotify & VNOTIFY_PUBS) && $discover_tab_on : $discover_tab_on); $notify_pubs = ((local_channel()) ? ($vnotify & VNOTIFY_PUBS) && $discover_tab_on : $discover_tab_on);
if($notify_pubs) { if($notify_pubs) {
@ -279,8 +279,8 @@ class Ping extends \Zotlabs\Web\Controller {
'photo' => $tt['photo'], 'photo' => $tt['photo'],
'when' => relative_date($tt['created']), 'when' => relative_date($tt['created']),
'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'), 'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'),
'b64mid' => $b64mid, 'b64mid' => (($tt['otype'] == 'item') ? $b64mid : 'undefined'),
'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : ''), 'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : 'undefined'),
'message' => $message 'message' => $message
); );
} }
@ -496,7 +496,7 @@ class Ping extends \Zotlabs\Web\Controller {
$r = q("SELECT id, item_wall FROM item $r = q("SELECT id, item_wall FROM item
WHERE item_unseen = 1 and uid = %d WHERE item_unseen = 1 and uid = %d
$item_normal $item_normal
AND author_xchan != '%s' $sql_extra ", AND author_xchan != '%s'",
intval(local_channel()), intval(local_channel()),
dbesc($ob_hash) dbesc($ob_hash)
); );

View file

@ -109,7 +109,7 @@ class Profile extends \Zotlabs\Web\Controller {
'title' => 'oembed' 'title' => 'oembed'
]); ]);
$o .= advanced_profile($a); $o .= advanced_profile();
call_hooks('profile_advanced',$o); call_hooks('profile_advanced',$o);
return $o; return $o;

View file

@ -179,7 +179,10 @@ class Profile_photo extends \Zotlabs\Web\Controller {
); );
} }
profiles_build_sync(local_channel()); // set $send to false in profiles_build_sync() to return the data
// so that we only send one sync packet.
$sync_profiles = profiles_build_sync(local_channel(),false);
// We'll set the updated profile-photo timestamp even if it isn't the default profile, // We'll set the updated profile-photo timestamp even if it isn't the default profile,
// so that browsers will do a cache update unconditionally // so that browsers will do a cache update unconditionally
@ -201,7 +204,7 @@ class Profile_photo extends \Zotlabs\Web\Controller {
$sync = attach_export_data($channel,$base_image['resource_id']); $sync = attach_export_data($channel,$base_image['resource_id']);
if($sync) if($sync)
build_sync_packet($channel['channel_id'],array('file' => array($sync))); build_sync_packet($channel['channel_id'],array('file' => array($sync), 'profile' => $sync_profiles));
// Similarly, tell the nav bar to bypass the cache and update the avatar image. // Similarly, tell the nav bar to bypass the cache and update the avatar image.

View file

@ -162,15 +162,13 @@ class Pubstream extends \Zotlabs\Web\Controller {
$net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : '');
$simple_update = (($update) ? " and item.item_unseen = 1 " : ''); $simple_update = (($_SESSION['loadtime']) ? " AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' " : '');
if($update && $_SESSION['loadtime'])
$simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) ";
if($load) if($load)
$simple_update = ''; $simple_update = '';
if($static && $simple_update) if($static && $simple_update)
$simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; $simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' ";
//logger('update: ' . $update . ' load: ' . $load); //logger('update: ' . $update . ' load: ' . $load);
@ -214,17 +212,18 @@ class Pubstream extends \Zotlabs\Web\Controller {
); );
} }
else { else {
$r = q("SELECT distinct item.id AS item_id, $ordering FROM item $r = q("SELECT distinct parent AS item_id, $ordering FROM item
left join abook on item.author_xchan = abook.abook_xchan left join abook on item.author_xchan = abook.abook_xchan
$net_query $net_query
WHERE true $uids $item_normal_update WHERE true $uids $item_normal_update
AND item.parent = item.id $simple_update $simple_update
and (abook.abook_blocked = 0 or abook.abook_flags is null) and (abook.abook_blocked = 0 or abook.abook_flags is null)
$sql_extra3 $sql_extra $sql_nets $net_query2" $sql_extra3 $sql_extra $sql_nets $net_query2"
); );
} }
$_SESSION['loadtime'] = datetime_convert(); $_SESSION['loadtime'] = datetime_convert();
} }
// Then fetch all the children of the parents that are on this page // Then fetch all the children of the parents that are on this page
$parents_str = ''; $parents_str = '';
$update_unseen = ''; $update_unseen = '';
@ -254,7 +253,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
} }
// fake it // fake it
$mode = ('network'); $mode = ('pubstream');
$o .= conversation($items,$mode,$update,$page_mode); $o .= conversation($items,$mode,$update,$page_mode);

View file

@ -6,15 +6,21 @@ namespace Zotlabs\Module;
class React extends \Zotlabs\Web\Controller { class React extends \Zotlabs\Web\Controller {
function get() { function get() {
if(! local_channel()) if(! local_channel())
return; return;
$sys = get_sys_channel();
$channel = \App::get_channel();
$postid = $_REQUEST['postid']; $postid = $_REQUEST['postid'];
if(! $postid) if(! $postid)
return; return;
$emoji = $_REQUEST['emoji']; $emoji = $_REQUEST['emoji'];
if($_REQUEST['emoji']) { if($_REQUEST['emoji']) {
$i = q("select * from item where id = %d and uid = %d", $i = q("select * from item where id = %d and uid = %d",
@ -22,10 +28,22 @@ class React extends \Zotlabs\Web\Controller {
intval(local_channel()) intval(local_channel())
); );
if(! $i) if(! $i) {
return; $i = q("select * from item where id = %d and uid = %d",
intval($postid),
intval($sys['channel_id'])
);
if($i) {
$i = [ copy_of_pubitem($channel, $i[0]['mid']) ];
$postid = (($i) ? $i[0]['id'] : 0);
}
}
if(! $i) {
return;
}
$channel = \App::get_channel();
$n = array(); $n = array();
$n['aid'] = $channel['channel_account_id']; $n['aid'] = $channel['channel_account_id'];
@ -40,7 +58,6 @@ class React extends \Zotlabs\Web\Controller {
$x = item_store($n); $x = item_store($n);
if(local_channel())
retain_item($postid); retain_item($postid);
if($x['success']) { if($x['success']) {

View file

@ -234,7 +234,11 @@ class Register extends \Zotlabs\Web\Controller {
if(get_config('system','no_age_restriction')) if(get_config('system','no_age_restriction'))
$label_tos = sprintf( t('I accept the %s for this website'), $toslink); $label_tos = sprintf( t('I accept the %s for this website'), $toslink);
else else
$label_tos = sprintf( t('I am over 13 years of age and accept the %s for this website'), $toslink); $age = get_config('system','minimum_age');
if(!$age) {
$age = 13;
}
$label_tos = sprintf( t('I am over %s years of age and accept the %s for this website'), $age, $toslink);
$enable_tos = 1 - intval(get_config('system','no_termsofservice')); $enable_tos = 1 - intval(get_config('system','no_termsofservice'));

View file

@ -2,6 +2,8 @@
namespace Zotlabs\Module\Settings; namespace Zotlabs\Module\Settings;
require_once('include/selectors.php');
class Channel { class Channel {
@ -149,6 +151,7 @@ class Channel {
$cal_first_day = (((x($_POST,'first_day')) && (intval($_POST['first_day']) == 1)) ? 1: 0); $cal_first_day = (((x($_POST,'first_day')) && (intval($_POST['first_day']) == 1)) ? 1: 0);
$mailhost = ((array_key_exists('mailhost',$_POST)) ? notags(trim($_POST['mailhost'])) : ''); $mailhost = ((array_key_exists('mailhost',$_POST)) ? notags(trim($_POST['mailhost'])) : '');
$profile_assign = ((x($_POST,'profile_assign')) ? notags(trim($_POST['profile_assign'])) : '');
$pageflags = $channel['channel_pageflags']; $pageflags = $channel['channel_pageflags'];
@ -203,7 +206,7 @@ class Channel {
$vnotify += intval($_POST['vnotify11']); $vnotify += intval($_POST['vnotify11']);
if(x($_POST,'vnotify12')) if(x($_POST,'vnotify12'))
$vnotify += intval($_POST['vnotify12']); $vnotify += intval($_POST['vnotify12']);
if(x($_POST,'vnotify13') && (get_config('system', 'disable_discover_tab') != 1)) if(x($_POST,'vnotify13'))
$vnotify += intval($_POST['vnotify13']); $vnotify += intval($_POST['vnotify13']);
$always_show_in_notices = x($_POST,'always_show_in_notices') ? 1 : 0; $always_show_in_notices = x($_POST,'always_show_in_notices') ? 1 : 0;
@ -242,6 +245,7 @@ class Channel {
set_pconfig(local_channel(),'system','cal_first_day',$cal_first_day); set_pconfig(local_channel(),'system','cal_first_day',$cal_first_day);
set_pconfig(local_channel(),'system','default_permcat',$defpermcat); set_pconfig(local_channel(),'system','default_permcat',$defpermcat);
set_pconfig(local_channel(),'system','email_notify_host',$mailhost); set_pconfig(local_channel(),'system','email_notify_host',$mailhost);
set_pconfig(local_channel(),'system','profile_assign',$profile_assign);
$r = q("update channel set channel_name = '%s', channel_pageflags = %d, channel_timezone = '%s', channel_location = '%s', channel_notifyflags = %d, channel_max_anon_mail = %d, channel_max_friend_req = %d, channel_expire_days = %d $set_perms where channel_id = %d", $r = q("update channel set channel_name = '%s', channel_pageflags = %d, channel_timezone = '%s', channel_location = '%s', channel_notifyflags = %d, channel_max_anon_mail = %d, channel_max_friend_req = %d, channel_expire_days = %d $set_perms where channel_id = %d",
dbesc($username), dbesc($username),
@ -477,6 +481,8 @@ class Channel {
$plugin = [ 'basic' => '', 'security' => '', 'notify' => '', 'misc' => '' ]; $plugin = [ 'basic' => '', 'security' => '', 'notify' => '', 'misc' => '' ];
call_hooks('channel_settings',$plugin); call_hooks('channel_settings',$plugin);
$disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false;
$o .= replace_macros($stpl,array( $o .= replace_macros($stpl,array(
'$ptitle' => t('Channel Settings'), '$ptitle' => t('Channel Settings'),
@ -515,6 +521,9 @@ class Channel {
'$permissions' => t('Default Privacy Group'), '$permissions' => t('Default Privacy Group'),
'$permdesc' => t("\x28click to open/close\x29"), '$permdesc' => t("\x28click to open/close\x29"),
'$aclselect' => populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromDescription(t('Use my default audience setting for the type of object published'))), '$aclselect' => populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromDescription(t('Use my default audience setting for the type of object published'))),
'$profseltxt' => t('Profile to assign new connections'),
'$profselect' => ((feature_enabled(local_channel(),'multi_profiles')) ? contact_profile_assign(get_pconfig(local_channel(),'system','profile_assign','')) : ''),
'$allow_cid' => acl2json($perm_defaults['allow_cid']), '$allow_cid' => acl2json($perm_defaults['allow_cid']),
'$allow_gid' => acl2json($perm_defaults['allow_gid']), '$allow_gid' => acl2json($perm_defaults['allow_gid']),
'$deny_cid' => acl2json($perm_defaults['deny_cid']), '$deny_cid' => acl2json($perm_defaults['deny_cid']),
@ -563,7 +572,7 @@ class Channel {
'$vnotify10' => array('vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no), '$vnotify10' => array('vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no),
'$vnotify11' => array('vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no), '$vnotify11' => array('vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no),
'$vnotify12' => array('vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no), '$vnotify12' => array('vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no),
'$vnotify13' => ((get_config('system', 'disable_discover_tab') != 1) ? array('vnotify13', t('Unseen public activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no) : array()), '$vnotify13' => (($disable_discover_tab) ? array() : array('vnotify13', t('Unseen public activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no)),
'$mailhost' => [ 'mailhost', t('Email notification hub (hostname)'), get_pconfig(local_channel(),'system','email_notify_host',\App::get_hostname()), sprintf( t('If your channel is mirrored to multiple hubs, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'),\App::get_hostname()) ], '$mailhost' => [ 'mailhost', t('Email notification hub (hostname)'), get_pconfig(local_channel(),'system','email_notify_host',\App::get_hostname()), sprintf( t('If your channel is mirrored to multiple hubs, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'),\App::get_hostname()) ],
'$always_show_in_notices' => array('always_show_in_notices', t('Also show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no), '$always_show_in_notices' => array('always_show_in_notices', t('Also show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no),

View file

@ -11,15 +11,17 @@ class Featured {
call_hooks('feature_settings_post', $_POST); call_hooks('feature_settings_post', $_POST);
if($_POST['affinity_slider-submit']) { if($_POST['affinity_slider-submit']) {
if(intval($_POST['affinity_cmax'])) { $cmax = intval($_POST['affinity_cmax']);
set_pconfig(local_channel(),'affinity','cmax',intval($_POST['affinity_cmax'])); if($cmax < 0 || $cmax > 99)
} $cmax = 99;
if(intval($_POST['affinity_cmin'])) { $cmin = intval($_POST['affinity_cmin']);
set_pconfig(local_channel(),'affinity','cmin',intval($_POST['affinity_cmin'])); if($cmin < 0 || $cmin > 99)
} $cmin = 0;
if(intval($_POST['affinity_cmax']) || intval($_POST['affinity_cmin'])) { set_pconfig(local_channel(),'affinity','cmin',$cmin);
set_pconfig(local_channel(),'affinity','cmax',$cmax);
info( t('Affinity Slider settings updated.') . EOL); info( t('Affinity Slider settings updated.') . EOL);
}
} }
build_sync_packet(); build_sync_packet();
@ -40,12 +42,12 @@ class Featured {
$cmax = intval(get_pconfig(local_channel(),'affinity','cmax')); $cmax = intval(get_pconfig(local_channel(),'affinity','cmax'));
$cmax = (($cmax) ? $cmax : 99); $cmax = (($cmax) ? $cmax : 99);
$setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, '') '$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99'))
)); ));
$cmin = intval(get_pconfig(local_channel(),'affinity','cmin')); $cmin = intval(get_pconfig(local_channel(),'affinity','cmin'));
$cmin = (($cmin) ? $cmin : 0); $cmin = (($cmin) ? $cmin : 0);
$setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, '') '$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0'))
)); ));
$settings_addons .= replace_macros(get_markup_template('generic_addon_settings.tpl'), array( $settings_addons .= replace_macros(get_markup_template('generic_addon_settings.tpl'), array(

View file

@ -11,10 +11,13 @@ class Subthread extends \Zotlabs\Web\Controller {
function get() { function get() {
if((! local_channel()) && (! remote_channel())) { if(! local_channel()) {
return; return;
} }
$sys = get_sys_channel();
$channel = \App::get_channel();
$item_id = ((argc() > 2) ? notags(trim(argv(2))) : 0); $item_id = ((argc() > 2) ? notags(trim(argv(2))) : 0);
if(argv(1) === 'sub') if(argv(1) === 'sub')
@ -23,8 +26,29 @@ class Subthread extends \Zotlabs\Web\Controller {
$activity = ACTIVITY_UNFOLLOW; $activity = ACTIVITY_UNFOLLOW;
$r = q("SELECT parent FROM item WHERE id = '%s'", $i = q("select * from item where id = %d and uid = %d",
dbesc($item_id) intval($item_id),
intval(local_channel())
);
if(! $i) {
$i = q("select * from item where id = %d and uid = %d",
intval($postid),
intval($sys['channel_id'])
);
if($i) {
$i = [ copy_of_pubitem($channel, $i[0]['mid']) ];
$item_id = (($i) ? $i[0]['id'] : 0);
}
}
if(! $i) {
return;
}
$r = q("SELECT parent FROM item WHERE id = %d",
intval($item_id)
); );
if($r) { if($r) {

View file

@ -11,10 +11,12 @@ class Tagger extends \Zotlabs\Web\Controller {
function get() { function get() {
if(! local_channel() && ! remote_channel()) { if(! local_channel()) {
return; return;
} }
$sys = get_sys_channel();
$observer_hash = get_observer_hash(); $observer_hash = get_observer_hash();
//strip html-tags //strip html-tags
$term = notags(trim($_GET['term'])); $term = notags(trim($_GET['term']));
@ -26,9 +28,29 @@ class Tagger extends \Zotlabs\Web\Controller {
logger('tagger: tag ' . $term . ' item ' . $item_id); logger('tagger: tag ' . $term . ' item ' . $item_id);
$r = q("select * from item where id = %d and uid = %d limit 1",
intval($item_id),
intval(local_channel())
);
$r = q("SELECT * FROM item left join xchan on xchan_hash = author_xchan WHERE id = '%s' and uid = %d LIMIT 1", if(! $r) {
dbesc($item_id), $r = q("select * from item where id = %d and uid = %d limit 1",
intval($item_id),
intval($sys['channel_id'])
);
if($r) {
$r = [ copy_of_pubitem($channel, $i[0]['mid']) ];
$item_id = (($r) ? $r[0]['id'] : 0);
}
}
if(! $r) {
notice( t('Post not found.') . EOL);
return;
}
$r = q("SELECT * FROM item left join xchan on xchan_hash = author_xchan WHERE id = %d and uid = %d LIMIT 1",
intval($item_id),
intval(local_channel()) intval(local_channel())
); );

43
Zotlabs/Module/Update.php Normal file
View file

@ -0,0 +1,43 @@
<?php
namespace Zotlabs\Module;
class Update extends \Zotlabs\Web\Controller {
function get() {
$profile_uid = intval($_GET['p']);
// it's probably safe to do this for all modules and not just a limited subset,
// but it needs to be verified.
if((! $profile_uid) && in_array(argv(1),['display','search','pubstream','home']))
$profile_uid = (-1);
if(argc() < 2) {
killme();
}
// These modules don't have a completely working liveUpdate implementation currently
if(in_array(strtolower(argv(1)),['articles','cards']))
killme();
$module = "\\Zotlabs\\Module\\" . ucfirst(argv(1));
$load = (((argc() > 2) && (argv(2) == 'load')) ? 1 : 0);
$mod = new $module;
header("Content-type: text/html");
\App::$argv = [ argv(1) ];
\App::$argc = 1;
echo "<!DOCTYPE html><html><body><section>\r\n";
echo $mod->get($profile_uid, $load);
echo "</section></body></html>\r\n";
killme();
}
}

View file

@ -1,39 +0,0 @@
<?php
namespace Zotlabs\Module;
/**
* Module: update_profile
* Purpose: AJAX synchronisation of profile page
*
*/
class Update_cards extends \Zotlabs\Web\Controller {
function get() {
$profile_uid = intval($_GET['p']);
$load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0);
header("Content-type: text/html");
echo "<!DOCTYPE html><html><body><section></section></body></html>\r\n";
killme();
$mod = new Cards();
$text = $mod->get($profile_uid,$load);
/**
* reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well
*/
echo str_replace("\t",' ',$text);
echo (($_GET['msie'] == 1) ? '</div>' : '</section>');
echo "</body></html>\r\n";
killme();
}
}

View file

@ -1,70 +0,0 @@
<?php
namespace Zotlabs\Module;
/**
* Module: update_profile
* Purpose: AJAX synchronisation of profile page
*
*/
class Update_channel extends \Zotlabs\Web\Controller {
function get() {
$profile_uid = intval($_GET['p']);
$load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0);
header("Content-type: text/html");
echo "<!DOCTYPE html><html><body>\r\n";
/**
* We can remove this hack once Internet Explorer recognises HTML5 natively
*/
echo (($_GET['msie'] == 1) ? '<div>' : '<section>');
/**
*
* Grab the page inner contents by calling the content function from the profile module directly,
* but move any image src attributes to another attribute name. This is because
* some browsers will prefetch all the images for the page even if we don't need them.
* The only ones we need to fetch are those for new page additions, which we'll discover
* on the client side and then swap the image back.
*
*/
$mod = new Channel();
$text = $mod->get($profile_uid,$load);
$pattern = "/<img([^>]*) src=\"([^\"]*)\"/";
$replace = "<img\${1} dst=\"\${2}\"";
// $text = preg_replace($pattern, $replace, $text);
/*
if(! $load) {
$replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />';
$pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i";
$text = preg_replace($pattern, $replace, $text);
}
*/
/**
* reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well
*/
echo str_replace("\t",' ',$text);
echo (($_GET['msie'] == 1) ? '</div>' : '</section>');
echo "</body></html>\r\n";
killme();
}
}

View file

@ -1,32 +0,0 @@
<?php
namespace Zotlabs\Module;
// See update_profile.php for documentation
require_once('include/group.php');
class Update_display extends \Zotlabs\Web\Controller {
function get() {
$profile_uid = intval($_GET['p']);
if(! $profile_uid)
$profile_uid = (-1);
$load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0);
header("Content-type: text/html");
echo "<!DOCTYPE html><html><body>\r\n";
echo (($_GET['msie'] == 1) ? '<div>' : '<section>');
$mod = new Display();
$text = $mod->get($profile_uid, $load);
echo str_replace("\t",' ',$text);
echo (($_GET['msie'] == 1) ? '</div>' : '</section>');
echo "</body></html>\r\n";
killme();
}
}

View file

@ -1,42 +0,0 @@
<?php
namespace Zotlabs\Module;
// See update_profile.php for documentation
class Update_home extends \Zotlabs\Web\Controller {
function get() {
$profile_uid = ((intval($_GET['p'])) ? intval($_GET['p']) : (-1));
$load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0);
header("Content-type: text/html");
echo "<!DOCTYPE html><html><body>\r\n";
echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '<div>' : '<section>');
$mod = new Home();
$text = $mod->get($profile_uid, $load);
$pattern = "/<img([^>]*) src=\"([^\"]*)\"/";
$replace = "<img\${1} dst=\"\${2}\"";
// $text = preg_replace($pattern, $replace, $text);
/*
if(! $load) {
$replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />';
$pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i";
$text = preg_replace($pattern, $replace, $text);
}
*/
echo str_replace("\t",' ',$text);
echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '</div>' : '</section>');
echo "</body></html>\r\n";
// logger('update_home: ' . $text);
killme();
}
}

View file

@ -1,44 +0,0 @@
<?php
namespace Zotlabs\Module;
// See update_profile.php for documentation
require_once('include/group.php');
class Update_network extends \Zotlabs\Web\Controller {
function get() {
$profile_uid = intval($_GET['p']);
$load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0);
header("Content-type: text/html");
echo "<!DOCTYPE html><html><body>\r\n";
echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '<div>' : '<section>');
$mod = new Network();
$text = $mod->get($profile_uid, $load);
$pattern = "/<img([^>]*) src=\"([^\"]*)\"/";
$replace = "<img\${1} dst=\"\${2}\"";
// $text = preg_replace($pattern, $replace, $text);
/*
if(! $load) {
$replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />';
$pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i";
$text = preg_replace($pattern, $replace, $text);
}
*/
echo str_replace("\t",' ',$text);
echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '</div>' : '</section>');
echo "</body></html>\r\n";
// logger('update_network: ' . $text);
killme();
}
}

View file

@ -1,42 +0,0 @@
<?php
namespace Zotlabs\Module;
// See update_profile.php for documentation
class Update_pubstream extends \Zotlabs\Web\Controller {
function get() {
$profile_uid = ((intval($_GET['p'])) ? intval($_GET['p']) : (-1));
$load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0);
header("Content-type: text/html");
echo "<!DOCTYPE html><html><body>\r\n";
echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '<div>' : '<section>');
$mod = new Pubstream();
$text = $mod->get($profile_uid, $load);
$pattern = "/<img([^>]*) src=\"([^\"]*)\"/";
$replace = "<img\${1} dst=\"\${2}\"";
// $text = preg_replace($pattern, $replace, $text);
/*
if(! $load) {
$replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />';
$pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i";
$text = preg_replace($pattern, $replace, $text);
}
*/
echo str_replace("\t",' ',$text);
echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '</div>' : '</section>');
echo "</body></html>\r\n";
killme();
}
}

View file

@ -1,69 +0,0 @@
<?php
namespace Zotlabs\Module;
/**
* Module: update_profile
* Purpose: AJAX synchronisation of search page
*
*/
class Update_search extends \Zotlabs\Web\Controller {
function get() {
$profile_uid = intval($_GET['p']);
if(! $profile_uid)
$profile_uid = (-1);
$load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0);
header("Content-type: text/html");
echo "<!DOCTYPE html><html><body>\r\n";
/**
* We can remove this hack once Internet Explorer recognises HTML5 natively
*/
echo (($_GET['msie'] == 1) ? '<div>' : '<section>');
/**
*
* Grab the page inner contents by calling the content function from the profile module directly,
* but move any image src attributes to another attribute name. This is because
* some browsers will prefetch all the images for the page even if we don't need them.
* The only ones we need to fetch are those for new page additions, which we'll discover
* on the client side and then swap the image back.
*
*/
$mod = new Search();
$text = $mod->get($profile_uid,$load);
$pattern = "/<img([^>]*) src=\"([^\"]*)\"/";
$replace = "<img\${1} dst=\"\${2}\"";
// $text = preg_replace($pattern, $replace, $text);
/*
if(! $load) {
$replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />';
$pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i";
$text = preg_replace($pattern, $replace, $text);
}
*/
/**
* reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well
*/
echo str_replace("\t",' ',$text);
echo (($_GET['msie'] == 1) ? '</div>' : '</section>');
echo "</body></html>\r\n";
killme();
}
}

View file

@ -41,11 +41,13 @@ class Wall_attach extends \Zotlabs\Web\Controller {
$matches = []; $matches = [];
$partial = false; $partial = false;
$x = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) {
if($x) { $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches);
if($pm) {
// logger('Content-Range: ' . print_r($matches,true)); // logger('Content-Range: ' . print_r($matches,true));
$partial = true; $partial = true;
} }
}
if($partial) { if($partial) {
$x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]);

View file

@ -293,10 +293,21 @@ class Wiki extends \Zotlabs\Web\Controller {
$p = Zlib\NativeWikiPage::get_page_content(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); $p = Zlib\NativeWikiPage::get_page_content(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName));
} }
if(! ($p && $p['success'])) { if(! ($p && $p['success'])) {
notice( t('Error retrieving page content') . EOL); $x = new \Zotlabs\Widget\Wiki_pages();
goaway(z_root() . '/' . argv(0) . '/' . argv(1) );
}
$html = $x->create_missing_page([
'resource_id' => $resource_id,
'channel_id' => $owner['channel_id'],
'channel_address' => $owner['channel_address'],
'refresh' => true
]);
//json_return_and_die(array('pages' => $page_list_html, 'message' => '', 'success' => true));
notice( t('Error retrieving page content') . EOL);
//goaway(z_root() . '/' . argv(0) . '/' . argv(1) );
$renderedContent = Zlib\NativeWikiPage::convert_links($html, argv(0) . '/' . argv(1) . '/' . $wikiUrlName);
$showPageControls = $wiki_editor;
}
else {
$mimeType = $p['pageMimeType']; $mimeType = $p['pageMimeType'];
$sampleContent = (($mimeType == 'text/bbcode') ? '[h3]' . t('New page') . '[/h3]' : '### ' . t('New page')); $sampleContent = (($mimeType == 'text/bbcode') ? '[h3]' . t('New page') . '[/h3]' : '### ' . t('New page'));
@ -318,6 +329,7 @@ class Wiki extends \Zotlabs\Web\Controller {
$renderedContent = Zlib\NativeWikiPage::convert_links($html, argv(0) . '/' . argv(1) . '/' . $wikiUrlName); $renderedContent = Zlib\NativeWikiPage::convert_links($html, argv(0) . '/' . argv(1) . '/' . $wikiUrlName);
} }
$showPageControls = $wiki_editor; $showPageControls = $wiki_editor;
}
break; break;
// default: // Strip the extraneous URL components // default: // Strip the extraneous URL components
// goaway('/' . argv(0) . '/' . argv(1) . '/' . $wikiUrlName . '/' . $pageUrlName); // goaway('/' . argv(0) . '/' . argv(1) . '/' . $wikiUrlName . '/' . $pageUrlName);
@ -430,11 +442,15 @@ class Wiki extends \Zotlabs\Web\Controller {
goaway('/' . argv(0) . '/' . $nick . '/'); goaway('/' . argv(0) . '/' . $nick . '/');
} }
$wiki = array(); $wiki = array();
// backslashes won't work well in the javascript functions
$name = str_replace('\\','',$_POST['wikiName']);
// Generate new wiki info from input name // Generate new wiki info from input name
$wiki['postVisible'] = ((intval($_POST['postVisible'])) ? 1 : 0); $wiki['postVisible'] = ((intval($_POST['postVisible'])) ? 1 : 0);
$wiki['rawName'] = $_POST['wikiName']; $wiki['rawName'] = $name;
$wiki['htmlName'] = escape_tags($_POST['wikiName']); $wiki['htmlName'] = escape_tags($name);
$wiki['urlName'] = urlencode(urlencode($_POST['wikiName'])); $wiki['urlName'] = urlencode(urlencode($name));
$wiki['mimeType'] = $_POST['mimeType']; $wiki['mimeType'] = $_POST['mimeType'];
$wiki['typelock'] = $_POST['typelock']; $wiki['typelock'] = $_POST['typelock'];
@ -555,9 +571,14 @@ class Wiki extends \Zotlabs\Web\Controller {
} }
$name = $_POST['pageName']; //Get new page name $name = $_POST['pageName']; //Get new page name
if(urlencode(escape_tags($_POST['pageName'])) === '') {
json_return_and_die(array('message' => 'Error creating page. Invalid name.', 'success' => false)); // backslashes won't work well in the javascript functions
$name = str_replace('\\','',$name);
if(urlencode(escape_tags($name)) === '') {
json_return_and_die(array('message' => 'Error creating page. Invalid name (' . print_r($_POST,true) . ').', 'success' => false));
} }
$page = Zlib\NativeWikiPage::create_page($owner['channel_id'],$observer_hash, $name, $resource_id, $mimetype); $page = Zlib\NativeWikiPage::create_page($owner['channel_id'],$observer_hash, $name, $resource_id, $mimetype);
if($page['item_id']) { if($page['item_id']) {
@ -758,7 +779,7 @@ class Wiki extends \Zotlabs\Web\Controller {
if ((argc() === 4) && (argv(2) === 'rename') && (argv(3) === 'page')) { if ((argc() === 4) && (argv(2) === 'rename') && (argv(3) === 'page')) {
$resource_id = $_POST['resource_id']; $resource_id = $_POST['resource_id'];
$pageUrlName = $_POST['oldName']; $pageUrlName = $_POST['oldName'];
$pageNewName = $_POST['newName']; $pageNewName = str_replace('\\','',$_POST['newName']);
if ($pageUrlName === 'Home') { if ($pageUrlName === 'Home') {
json_return_and_die(array('message' => 'Cannot rename Home','success' => false)); json_return_and_die(array('message' => 'Cannot rename Home','success' => false));
} }

View file

@ -534,7 +534,12 @@ class Comanche {
require_once('widget/' . $clsname . '/' . $clsname . '.php'); require_once('widget/' . $clsname . '/' . $clsname . '.php');
elseif(file_exists('Zotlabs/Widget/' . $clsname . '.php')) elseif(file_exists('Zotlabs/Widget/' . $clsname . '.php'))
require_once('Zotlabs/Widget/' . $clsname . '.php'); require_once('Zotlabs/Widget/' . $clsname . '.php');
else {
$pth = theme_include($clsname . '.php');
if($pth) {
require_once($pth);
}
}
if(class_exists($nsname)) { if(class_exists($nsname)) {
$x = new $nsname; $x = new $nsname;
$f = 'widget'; $f = 'widget';
@ -550,12 +555,14 @@ class Comanche {
require_once('widget/' . trim($name) . '.php'); require_once('widget/' . trim($name) . '.php');
elseif(file_exists('widget/' . trim($name) . '/' . trim($name) . '.php')) elseif(file_exists('widget/' . trim($name) . '/' . trim($name) . '.php'))
require_once('widget/' . trim($name) . '/' . trim($name) . '.php'); require_once('widget/' . trim($name) . '/' . trim($name) . '.php');
}
else { if(! function_exists($func)) {
$theme_widget = $func . '.php'; $theme_widget = $func . '.php';
if((! function_exists($func)) && theme_include($theme_widget)) if(theme_include($theme_widget)) {
require_once(theme_include($theme_widget)); require_once(theme_include($theme_widget));
} }
}
}
if(function_exists($func)) if(function_exists($func))
return $func($vars); return $func($vars);

View file

@ -2,8 +2,6 @@
namespace Zotlabs\Render; namespace Zotlabs\Render;
require_once('library/Smarty/libs/Smarty.class.php');
class SmartyInterface extends \Smarty { class SmartyInterface extends \Smarty {
public $filename; public $filename;

View file

@ -12,7 +12,7 @@ use Sabre\DAV;
* *
* @extends \\Sabre\\DAV\\Browser\\Plugin * @extends \\Sabre\\DAV\\Browser\\Plugin
* *
* @link http://github.com/friendica/red * @link http://github.com/redmatrix/hubzilla
* @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT)
*/ */
class Browser extends DAV\Browser\Plugin { class Browser extends DAV\Browser\Plugin {
@ -373,8 +373,6 @@ class Browser extends DAV\Browser\Plugin {
if(strpos($path,$special) === 0) if(strpos($path,$special) === 0)
$path = trim(substr($path,$count),'/'); $path = trim(substr($path,$count),'/');
$info = t('Please use DAV to upload large (video, audio) files.<br>See <a class="zrl" href="help/member/member_guide#Cloud_Desktop_Clients">Cloud Desktop Clients</a>');
$output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array( $output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array(
'$folder_header' => t('Create new folder'), '$folder_header' => t('Create new folder'),
@ -382,7 +380,6 @@ class Browser extends DAV\Browser\Plugin {
'$upload_header' => t('Upload file'), '$upload_header' => t('Upload file'),
'$upload_submit' => t('Upload'), '$upload_submit' => t('Upload'),
'$quota' => $quota, '$quota' => $quota,
'$info' => $info,
'$channick' => $this->auth->owner_nick, '$channick' => $this->auth->owner_nick,
'$aclselect' => $aclselect, '$aclselect' => $aclselect,
'$allow_cid' => acl2json($channel_acl['allow_cid']), '$allow_cid' => acl2json($channel_acl['allow_cid']),

View file

@ -16,7 +16,7 @@ use Sabre\DAV;
* @link http://github.com/friendica/red * @link http://github.com/friendica/red
* @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT)
*/ */
class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMoveTarget {
/** /**
* @brief The path inside /cloud * @brief The path inside /cloud
@ -457,6 +457,22 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
return false; return false;
} }
public function moveInto($targetName,$sourcePath, DAV\INode $sourceNode) {
if(! $this->auth->owner_id) {
return false;
}
if(! ($sourceNode->data && $sourceNode->data->hash)) {
return false;
}
return attach_move($this->auth->owner_id, $sourceNode->data->hash, $this->folder_hash);
}
/** /**
* @todo add description of what this function does. * @todo add description of what this function does.
* *
@ -675,7 +691,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
} }
$prefix = ''; $prefix = '';
$suffix = ''; $suffix = ' order by is_dir desc, filename asc ';
$r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix", $r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix",
dbesc($folder), dbesc($folder),

View file

@ -2,19 +2,37 @@
namespace Zotlabs\Thumbs; namespace Zotlabs\Thumbs;
require_once('library/epub-meta/epub.php'); require_once 'library/epub-meta/epub.php';
/**
* @brief Thumbnail creation for epub files.
*
*/
class Epubthumb { class Epubthumb {
/**
* @brief Match for application/epub+zip.
*
* @param string $type MimeType
* @return boolean
*/
function Match($type) { function Match($type) {
return(($type === 'application/epub+zip') ? true : false ); return(($type === 'application/epub+zip') ? true : false );
} }
/**
* @brief
*
* @param array $attach
* @param number $preview_style unused
* @param number $height (optional) default 300
* @param number $width (optional) default 300
*/
function Thumb($attach, $preview_style, $height = 300, $width = 300) { function Thumb($attach, $preview_style, $height = 300, $width = 300) {
$photo = false; $photo = false;
$ep = new \Epub(dbunescbin($attach['content'])); $ep = new \EPub(dbunescbin($attach['content']));
$data = $ep->Cover(); $data = $ep->Cover();
if($data['found']) { if($data['found']) {

View file

@ -32,12 +32,25 @@ class Video {
fclose($ostream); fclose($ostream);
} }
/*
* Note: imagick convert may try to call 'ffmpeg' (or other conversion utilities) under
* the covers for this particular operation. If this is not installed or not in the path
* for the web server user, errors may be reported in the web server logs.
*/
$ffmpeg = trim(shell_exec('which ffmpeg'));
if($ffmpeg) {
logger('ffmpeg not found in path. Video thumbnails may fail.');
}
$imagick_path = get_config('system','imagick_convert_path'); $imagick_path = get_config('system','imagick_convert_path');
if($imagick_path && @file_exists($imagick_path)) { if($imagick_path && @file_exists($imagick_path)) {
$cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -thumbnail ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile); $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -thumbnail ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile);
// logger('imagick thumbnail command: ' . $cmd); // logger('imagick thumbnail command: ' . $cmd);
exec($cmd); /** @scrutinizer ignore-unhandled */
@exec($cmd);
if(! file_exists($outfile)) { if(! file_exists($outfile)) {
logger('imagick scale failed.'); logger('imagick scale failed.');

View file

@ -117,7 +117,7 @@ class HTTPSig {
logger('verified: ' . $x, LOGGER_DEBUG); logger('verified: ' . $x, LOGGER_DEBUG);
if($x === false) if(! $x)
return $result; return $result;
if(! $spoofable) if(! $spoofable)
@ -237,7 +237,7 @@ class HTTPSig {
$fields = '(request-target)'; $fields = '(request-target)';
} }
if(head) { if($head) {
foreach($head as $k => $v) { foreach($head as $k => $v) {
$headers .= strtolower($k) . ': ' . trim($v) . "\n"; $headers .= strtolower($k) . ': ' . trim($v) . "\n";
if($fields) if($fields)

View file

@ -265,7 +265,7 @@ class Router {
if(! \App::$error) { if(! \App::$error) {
$arr = array('content' => \App::$page['content'], 'replace' => false); $arr = array('content' => \App::$page['content'], 'replace' => false);
call_hooks(\App::$module . '_mod_content', $arr); call_hooks(\App::$module . '_mod_content', $arr);
\App::$page['content'] = $arr['content'];
if(! $arr['replace']) { if(! $arr['replace']) {
if($this->controller && method_exists($this->controller,'get')) { if($this->controller && method_exists($this->controller,'get')) {
$arr = array('content' => $this->controller->get()); $arr = array('content' => $this->controller->get());
@ -276,7 +276,7 @@ class Router {
} }
} }
call_hooks(\App::$module . '_mod_aftercontent', $arr); call_hooks(\App::$module . '_mod_aftercontent', $arr);
\App::$page['content'] .= $arr['content']; \App::$page['content'] = (($arr['replace']) ? $arr['content'] : \App::$page['content'] . $arr['content']);
} }
} }
} }

View file

@ -9,15 +9,11 @@ class Affinity {
if(! local_channel()) if(! local_channel())
return ''; return '';
// Get default cmin value from pconfig, but allow GET parameter to override $default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0);
$cmin = intval(get_pconfig(local_channel(),'affinity','cmin')); $default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99);
$cmin = (($cmin) ? $cmin : 0);
$cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $cmin);
// Get default cmax value from pconfig, but allow GET parameter to override $cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $default_cmin);
$cmax = intval(get_pconfig(local_channel(),'affinity','cmax')); $cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $default_cmax);
$cmax = (($cmax) ? $cmax : 99);
$cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $cmax);
if(feature_enabled(local_channel(),'affinity')) { if(feature_enabled(local_channel(),'affinity')) {

View file

@ -13,8 +13,14 @@ class Categories {
if(($cards) && (! feature_enabled(\App::$profile['profile_uid'],'cards'))) if(($cards) && (! feature_enabled(\App::$profile['profile_uid'],'cards')))
return ''; return '';
$articles = ((array_key_exists('articles',$arr) && $arr['articles']) ? true : false);
if(($articles) && (! feature_enabled(\App::$profile['profile_uid'],'articles')))
return '';
if((! \App::$profile['profile_uid']) if((! \App::$profile['profile_uid'])
|| (! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),(($cards) ? 'view_pages' : 'view_stream')))) { || (! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),(($cards || $articles) ? 'view_pages' : 'view_stream')))) {
return ''; return '';
} }
@ -25,6 +31,8 @@ class Categories {
if($cards) if($cards)
return cardcategories_widget($srchurl, $cat); return cardcategories_widget($srchurl, $cat);
elseif($articles)
return articlecategories_widget($srchurl, $cat);
else else
return categories_widget($srchurl, $cat); return categories_widget($srchurl, $cat);

View file

@ -63,9 +63,10 @@ class Cdav {
$sharees = []; $sharees = [];
$share_displayname = []; $share_displayname = [];
foreach($invites as $invite) { foreach($invites as $invite) {
if(strpos($invite->href, 'mailto:') !== false) { if(strpos($invite->href, 'mailto:') !== false) {
$sharee = channelx_by_hash(substr($invite->href, 7)); $sharee = channelx_by_nick(substr($invite->principal, 11));
$sharees[] = [ $sharees[] = [
'name' => $sharee['channel_name'], 'name' => $sharee['channel_name'],
'access' => (($invite->access == 3) ? ' (RW)' : ' (R)'), 'access' => (($invite->access == 3) ? ' (RW)' : ' (R)'),

View file

@ -11,8 +11,8 @@ class Forums {
$o = ''; $o = '';
if(is_array($arr) && array_key_exists('limit',$arr)) if(is_array($arr) && array_key_exists('limit',$arr) && intval($arr['limit']) >= 0)
$limit = " limit " . intval($limit) . " "; $limit = " limit " . intval($arr['limit']) . " ";
else else
$limit = ''; $limit = '';

View file

@ -0,0 +1,26 @@
<?php
namespace Zotlabs\Widget;
class Hq_controls {
function widget($arr) {
if (! local_channel())
return;
return replace_macros(get_markup_template('hq_controls.tpl'),
[
'$title' => t('HQ Control Panel'),
'$menu' => [
'create' => [
'label' => t('Create a new post'),
'id' => 'jot-toggle',
'href' => '#',
'class' => ''
]
]
]
);
}
}

View file

@ -20,8 +20,10 @@ class Notifications {
'label' => t('View your network activity') 'label' => t('View your network activity')
], ],
'markall' => [ 'markall' => [
'url' => '#',
'label' => t('Mark all notifications read') 'label' => t('Mark all notifications read')
],
'filter' => [
'label' => t('Show new posts only')
] ]
]; ];
@ -36,8 +38,10 @@ class Notifications {
'label' => t('View your home activity') 'label' => t('View your home activity')
], ],
'markall' => [ 'markall' => [
'url' => '#',
'label' => t('Mark all notifications seen') 'label' => t('Mark all notifications seen')
],
'filter' => [
'label' => t('Show new posts only')
] ]
]; ];
@ -52,7 +56,6 @@ class Notifications {
'label' => t('View your private mails') 'label' => t('View your private mails')
], ],
'markall' => [ 'markall' => [
'url' => '#',
'label' => t('Mark all messages seen') 'label' => t('Mark all messages seen')
] ]
]; ];
@ -68,7 +71,6 @@ class Notifications {
'label' => t('View events') 'label' => t('View events')
], ],
'markall' => [ 'markall' => [
'url' => '#',
'label' => t('Mark all events seen') 'label' => t('Mark all events seen')
] ]
]; ];
@ -104,7 +106,6 @@ class Notifications {
'label' => t('View all notices') 'label' => t('View all notices')
], ],
'markall' => [ 'markall' => [
'url' => '#',
'label' => t('Mark all notices seen') 'label' => t('Mark all notices seen')
] ]
]; ];
@ -132,8 +133,10 @@ class Notifications {
'label' => t('View the public stream') 'label' => t('View the public stream')
], ],
'markall' => [ 'markall' => [
'url' => '#',
'label' => t('Mark all notifications seen') 'label' => t('Mark all notifications seen')
],
'filter' => [
'label' => t('Show new posts only')
] ]
]; ];
} }
@ -141,7 +144,8 @@ class Notifications {
$o = replace_macros(get_markup_template('notifications_widget.tpl'), array( $o = replace_macros(get_markup_template('notifications_widget.tpl'), array(
'$module' => \App::$module, '$module' => \App::$module,
'$notifications' => $notifications, '$notifications' => $notifications,
'$loading' => t('Loading...') '$no_notifications' => t('Sorry, you have got no notifications at the moment'),
'$loading' => t('Loading')
)); ));
return $o; return $o;

View file

@ -5,6 +5,42 @@ namespace Zotlabs\Widget;
class Wiki_pages { class Wiki_pages {
function create_missing_page($arr) {
if(argc() < 4)
return;
$c = channelx_by_nick(argv(1));
$w = \Zotlabs\Lib\NativeWiki::exists_by_name($c['channel_id'],urldecode(argv(2)));
$arr = array(
'resource_id' => $w['resource_id'],
'channel_id' => $c['channel_id'],
'channel_address' => $c['channel_address'],
'refresh' => false
);
$can_create = perm_is_allowed(\App::$profile['uid'],get_observer_hash(),'write_wiki');
$can_delete = ((local_channel() && (local_channel() == \App::$profile['uid'])) ? true : false);
$pageName = addslashes(escape_tags(urldecode(argv(3))));
return replace_macros(get_markup_template('wiki_page_not_found.tpl'), array(
'$resource_id' => $arr['resource_id'],
'$channel_address' => $arr['channel_address'],
'$wikiname' => $wikiname,
'$canadd' => $can_create,
'$candel' => $can_delete,
'$addnew' => t('Add new page'),
'$typelock' => $typelock,
'$lockedtype' => $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'],
'$options' => t('Options'),
'$submit' => t('Submit')
));
}
function widget($arr) { function widget($arr) {
if(argc() < 3) if(argc() < 3)

6
app/articles.apd Normal file
View file

@ -0,0 +1,6 @@
version: 1.2
url: $baseurl/articles/$nick
name: Articles
requires: local_channel, articles
photo: icon:file-text-o
categories: nav_featured_app, Productivity

View file

@ -3,4 +3,4 @@ url: $baseurl/cards/$nick
name: Cards name: Cards
requires: local_channel, cards requires: local_channel, cards
photo: icon:list photo: icon:list
categories: Productivity categories: nav_featured_app, Productivity

View file

@ -3,4 +3,4 @@ url: $baseurl/webpages/$nick
requires: local_channel, webpages requires: local_channel, webpages
name: Webpages name: Webpages
photo: icon:newspaper-o photo: icon:newspaper-o
categories: Productivity categories: nav_featured_app, Productivity

View file

@ -3,4 +3,4 @@ url: $baseurl/wiki/$nick
requires: local_channel, wiki requires: local_channel, wiki
name: Wiki name: Wiki
photo: icon:pencil-square-o photo: icon:pencil-square-o
categories: Productivity categories: nav_featured_app, Productivity

View file

@ -48,9 +48,10 @@ require_once('include/zid.php');
require_once('include/xchan.php'); require_once('include/xchan.php');
require_once('include/hubloc.php'); require_once('include/hubloc.php');
require_once('include/attach.php'); require_once('include/attach.php');
require_once('include/bbcode.php');
define ( 'PLATFORM_NAME', 'hubzilla' ); define ( 'PLATFORM_NAME', 'hubzilla' );
define ( 'STD_VERSION', '2.9.1' ); define ( 'STD_VERSION', '3.1.1' );
define ( 'ZOT_REVISION', '1.3' ); define ( 'ZOT_REVISION', '1.3' );
define ( 'DB_UPDATE_VERSION', 1198 ); define ( 'DB_UPDATE_VERSION', 1198 );
@ -560,6 +561,7 @@ define ( 'ITEM_BUG', 0x0400); // Is a bug, can be used by the internal bug tr
define ( 'ITEM_PENDING_REMOVE', 0x0800); // deleted, notification period has lapsed define ( 'ITEM_PENDING_REMOVE', 0x0800); // deleted, notification period has lapsed
define ( 'ITEM_DOC', 0x1000); // hubzilla only, define here so that item import does the right thing define ( 'ITEM_DOC', 0x1000); // hubzilla only, define here so that item import does the right thing
define ( 'ITEM_CARD', 0x2000); define ( 'ITEM_CARD', 0x2000);
define ( 'ITEM_ARTICLE', 0x4000);
define ( 'ITEM_TYPE_POST', 0 ); define ( 'ITEM_TYPE_POST', 0 );
@ -569,6 +571,7 @@ define ( 'ITEM_TYPE_WEBPAGE', 3 );
define ( 'ITEM_TYPE_BUG', 4 ); define ( 'ITEM_TYPE_BUG', 4 );
define ( 'ITEM_TYPE_DOC', 5 ); define ( 'ITEM_TYPE_DOC', 5 );
define ( 'ITEM_TYPE_CARD', 6 ); define ( 'ITEM_TYPE_CARD', 6 );
define ( 'ITEM_TYPE_ARTICLE', 7 );
define ( 'ITEM_IS_STICKY', 1000 ); define ( 'ITEM_IS_STICKY', 1000 );
@ -2007,7 +2010,7 @@ function build_querystring($params, $name = null) {
} }
/* /**
* @brief Much better way of dealing with c-style args. * @brief Much better way of dealing with c-style args.
*/ */
function argc() { function argc() {
@ -2028,6 +2031,8 @@ function dba_timer() {
/** /**
* @brief Returns xchan_hash from the observer. * @brief Returns xchan_hash from the observer.
* *
* Observer can be a local or remote channel.
*
* @return string xchan_hash from observer, otherwise empty string if no observer * @return string xchan_hash from observer, otherwise empty string if no observer
*/ */
function get_observer_hash() { function get_observer_hash() {
@ -2038,7 +2043,6 @@ function get_observer_hash() {
return ''; return '';
} }
/** /**
* @brief Returns the complete URL of the current page, e.g.: http(s)://something.com/network * @brief Returns the complete URL of the current page, e.g.: http(s)://something.com/network
* *

View file

@ -36,15 +36,17 @@
"league/html-to-markdown": "^4.4", "league/html-to-markdown": "^4.4",
"pear/text_languagedetect": "^1.0", "pear/text_languagedetect": "^1.0",
"commerceguys/intl": "~0.7", "commerceguys/intl": "~0.7",
"lukasreschke/id3parser": "^0.0.1" "lukasreschke/id3parser": "^0.0.1",
"smarty/smarty": "~3.1"
}, },
"require-dev" : { "require-dev" : {
"php" : ">=7.0", "php" : ">=7.0",
"phpunit/phpunit" : "^6.1", "phpunit/phpunit" : "~6.4.4",
"behat/behat" : "@stable", "behat/behat" : "@stable",
"behat/mink-extension": "@stable", "behat/mink-extension": "@stable",
"behat/mink-goutte-driver": "@stable", "behat/mink-goutte-driver": "@stable",
"php-mock/php-mock-phpunit": "^2.0" "php-mock/php-mock-phpunit": "^2.0",
"phpunit/dbunit": "^3.0"
}, },
"autoload" : { "autoload" : {
"psr-4" : { "psr-4" : {

618
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -168,21 +168,19 @@ any time that the documentation is updated :
util/importdoc util/importdoc
### Automated installation via the .homeinstall shell script ### Automated installation via the .homeinstall shell script
There is a shell script in (``.homeinstall/hubzilla-setup.sh``) that will install $Projectname and its dependencies on a fresh installation of Debian 8.3 stable (Jessie). It should work on similar Linux systems but your results may vary. There is a shell script in (``.homeinstall/hubzilla-setup.sh``) that will install $Projectname and its dependencies on a fresh installation of Debian 9 stable (Stetch). It should work on similar Linux systems but your results may vary.
#### Requirements #### Requirements
The installation script was originally designed for a small hardware server behind your home router. However, it has been tested on several systems running Debian 8.3: The installation script was originally designed for a small hardware server behind your home router. However, it has been tested on several systems running Debian 9:
* Home-PC (Debian-8.3.0-amd64) * Home-PC (Debian-9.2-amd64) and Rapberry-Pi 3 (Rasbian = Debian 9.3)
* Internet connection and router at home * Internet connection and router at home
* Mini-pc connected to your router * Mini-PC / Raspi connected to your router
* USB drive for backups * USB drive for backups
* Fresh installation of Debian on your mini-pc * Fresh installation of Debian on your mini-pc
* Router with open ports 80 and 443 for your Debian * Router with open ports 80 and 443 for your Debian
* DigitalOcean droplet (Debian 8.3 x64 / 512 MB Memory / 20 GB Disk / NYC3)
#### Overview of installation steps #### Overview of installation steps
1. `apt-get install git` 1. `apt-get install git`
1. `mkdir -p /var/www/html` 1. `mkdir -p /var/www/html`
@ -191,8 +189,6 @@ The installation script was originally designed for a small hardware server behi
1. `nano .homeinstall/hubzilla-config.txt` 1. `nano .homeinstall/hubzilla-config.txt`
1. `cd .homeinstall/` 1. `cd .homeinstall/`
1. `./hubzilla-setup.sh` 1. `./hubzilla-setup.sh`
1. `sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php5/apache2/php.ini`
1. `sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php5/apache2/php.ini`
1. `service apache2 reload` 1. `service apache2 reload`
1. Open your domain with a browser and step throught the initial configuration of $Projectname. 1. Open your domain with a browser and step throught the initial configuration of $Projectname.

View file

@ -1,5 +1,9 @@
[h3]Zot API[/h3] [h3]Zot API[/h3]
Many existing social applications and tools can interface directly using the Twitter/StatusNet API, which is available using the 'twitter_api' addon.
This document describes the native API; which allows direct programmatic access to several internal data structures and libraries extending beyond the basic social interface.
The API endpoints detailed below are relative to [code]api/z/1.0[/code], meaning that if an API is listed as [code]channel/stream[/code] the full API URL is [code][baseurl]/api/z/1.0/channel/stream[/code]. The API endpoints detailed below are relative to [code]api/z/1.0[/code], meaning that if an API is listed as [code]channel/stream[/code] the full API URL is [code][baseurl]/api/z/1.0/channel/stream[/code].
[h3]channel/export/basic[/h3] [h3]channel/export/basic[/h3]

View file

@ -52,6 +52,7 @@ function api_login(&$a){
/* Signature authentication */ /* Signature authentication */
if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') { if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') {
if($head !== 'HTTP_AUTHORIZATION') { if($head !== 'HTTP_AUTHORIZATION') {
$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head]; $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head];
continue; continue;
@ -59,7 +60,7 @@ function api_login(&$a){
$sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]);
if($sigblock) { if($sigblock) {
$keyId = $sigblock['keyId']; $keyId = str_replace('acct:','',$sigblock['keyId']);
if($keyId) { if($keyId) {
$r = q("select * from hubloc where hubloc_addr = '%s' limit 1", $r = q("select * from hubloc where hubloc_addr = '%s' limit 1",
dbesc($keyId) dbesc($keyId)

View file

@ -150,7 +150,11 @@
$start = ((array_key_exists('start',$_REQUEST)) ? intval($_REQUEST['start']) : 0); $start = ((array_key_exists('start',$_REQUEST)) ? intval($_REQUEST['start']) : 0);
$records = ((array_key_exists('records',$_REQUEST)) ? intval($_REQUEST['records']) : 0); $records = ((array_key_exists('records',$_REQUEST)) ? intval($_REQUEST['records']) : 0);
$x = attach_list_files(api_user(),get_observer_hash(),$hash,$filename,$filetype,'created asc',$start,$records); $since = ((array_key_exists('since',$_REQUEST)) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['since']) : NULL_DATE);
$until = ((array_key_exists('until',$_REQUEST)) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['until']) : datetime_convert());
$x = attach_list_files(api_user(),get_observer_hash(),$hash,$filename,$filetype,'created asc',$start,$records, $since, $until);
if($start || $records) { if($start || $records) {
$x['start'] = $start; $x['start'] = $start;
$x['records'] = count($x['results']); $x['records'] = count($x['results']);
@ -226,7 +230,10 @@
if(! $_REQUEST['file_id']) if(! $_REQUEST['file_id'])
return false; return false;
$ret = attach_export_data(api_user(),$_REQUEST['file_id']); $channel = channelx_by_n(api_user());
$ret = attach_export_data($channel,$_REQUEST['file_id']);
if($ret) { if($ret) {
json_return_and_die($ret); json_return_and_die($ret);
} }

View file

@ -189,7 +189,7 @@ function attach_count_files($channel_id, $observer, $hash = '', $filename = '',
* * \e array|boolean \b results array with results, or false * * \e array|boolean \b results array with results, or false
* * \e string \b message with error messages if any * * \e string \b message with error messages if any
*/ */
function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '', $orderby = 'created desc', $start = 0, $entries = 0) { function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '', $orderby = 'created desc', $start = 0, $entries = 0, $since = '', $until = '') {
$ret = array('success' => false); $ret = array('success' => false);
@ -198,6 +198,7 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $
return $ret; return $ret;
} }
require_once('include/security.php'); require_once('include/security.php');
$sql_extra = permissions_sql($channel_id); $sql_extra = permissions_sql($channel_id);
@ -213,14 +214,22 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $
if($entries) if($entries)
$limit = " limit " . intval($start) . ", " . intval($entries) . " "; $limit = " limit " . intval($start) . ", " . intval($entries) . " ";
// Retrieve all columns except 'data' if(! $since)
$since = NULL_DATE;
if(! $until)
$until = datetime_convert();
$sql_extra .= " and created >= '" . dbesc($since) . "' and created <= '" . dbesc($until) . "' ";
// Retrieve all columns except 'content'
$r = q("select id, aid, uid, hash, filename, filetype, filesize, revision, folder, os_path, display_path, os_storage, is_dir, is_photo, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d $sql_extra ORDER BY $orderby $limit", $r = q("select id, aid, uid, hash, filename, filetype, filesize, revision, folder, os_path, display_path, os_storage, is_dir, is_photo, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d $sql_extra ORDER BY $orderby $limit",
intval($channel_id) intval($channel_id)
); );
$ret['success'] = ((is_array($r)) ? true : false); $ret['success'] = ((is_array($r)) ? true : false);
$ret['results'] = ((is_array($r)) ? $r : false); $ret['results'] = ((is_array($r)) ? $r : []);
return $ret; return $ret;
} }
@ -276,6 +285,8 @@ function attach_by_hash($hash, $observer_hash, $rev = 0) {
return $ret; return $ret;
} }
$r[0]['content'] = dbunescbin($r[0]['content']);
if($r[0]['folder']) { if($r[0]['folder']) {
$x = attach_can_view_folder($r[0]['uid'],$observer_hash,$r[0]['folder']); $x = attach_can_view_folder($r[0]['uid'],$observer_hash,$r[0]['folder']);
if(! $x) { if(! $x) {
@ -297,6 +308,11 @@ function attach_can_view_folder($uid,$ob_hash,$folder_hash) {
$hash = $folder_hash; $hash = $folder_hash;
$result = false; $result = false;
if(! $folder_hash) {
return perm_is_allowed($uid,$ob_hash,'view_storage');
}
do { do {
$r = q("select folder from attach where hash = '%s' and uid = %d $sql_extra", $r = q("select folder from attach where hash = '%s' and uid = %d $sql_extra",
dbesc($hash), dbesc($hash),
@ -534,6 +550,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
$type = $f['type']; $type = $f['type'];
} else { } else {
if(! x($_FILES,'userfile')) { if(! x($_FILES,'userfile')) {
logger('No source file');
$ret['message'] = t('No source file.'); $ret['message'] = t('No source file.');
return $ret; return $ret;
} }
@ -574,6 +591,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
intval($channel_id) intval($channel_id)
); );
if(! $x) { if(! $x) {
logger('update file source not found');
$ret['message'] = t('Cannot locate file to revise/update'); $ret['message'] = t('Cannot locate file to revise/update');
return $ret; return $ret;
} }
@ -715,6 +733,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
$maxfilesize = get_config('system','maxfilesize'); $maxfilesize = get_config('system','maxfilesize');
if(($maxfilesize) && ($filesize > $maxfilesize)) { if(($maxfilesize) && ($filesize > $maxfilesize)) {
logger('quota_exceeded');
$ret['message'] = sprintf( t('File exceeds size limit of %d'), $maxfilesize); $ret['message'] = sprintf( t('File exceeds size limit of %d'), $maxfilesize);
if($remove_when_processed) if($remove_when_processed)
@unlink($src); @unlink($src);
@ -735,6 +754,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
intval($channel['channel_account_id']) intval($channel['channel_account_id'])
); );
if(($r) && (($r[0]['total'] + $filesize) > ($limit - $existing_size))) { if(($r) && (($r[0]['total'] + $filesize) > ($limit - $existing_size))) {
logger('service_class limit exceeded');
$ret['message'] = upgrade_message(true) . sprintf(t("You have reached your limit of %1$.0f Mbytes attachment storage."), $limit / 1024000); $ret['message'] = upgrade_message(true) . sprintf(t("You have reached your limit of %1$.0f Mbytes attachment storage."), $limit / 1024000);
if($remove_when_processed) if($remove_when_processed)
@unlink($src); @unlink($src);
@ -767,8 +787,15 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
$os_path = $os_relpath; $os_path = $os_relpath;
$display_path = ltrim($pathname . '/' . $filename,'/'); $display_path = ltrim($pathname . '/' . $filename,'/');
if($src) if($src) {
@file_put_contents($os_basepath . $os_relpath,@file_get_contents($src)); $istream = @fopen($src,'rb');
$ostream = @fopen($os_basepath . $os_relpath,'wb');
if($istream && $ostream) {
pipe_streams($istream,$ostream,65535);
fclose($istream);
fclose($ostream);
}
}
if(array_key_exists('created', $arr)) if(array_key_exists('created', $arr))
$created = $arr['created']; $created = $arr['created'];
@ -1639,7 +1666,7 @@ function find_filename_by_hash($channel_id, $attachHash) {
* @param int $bufsize size of chunk, default 16384 * @param int $bufsize size of chunk, default 16384
* @return number with the size * @return number with the size
*/ */
function pipe_streams($in, $out, $bufize = 16384) { function pipe_streams($in, $out, $bufsize = 16384) {
$size = 0; $size = 0;
while (!feof($in)) while (!feof($in))
$size += fwrite($out, fread($in, $bufsize)); $size += fwrite($out, fread($in, $bufsize));
@ -2037,6 +2064,7 @@ function attach_export_data($channel, $resource_id, $deleted = false) {
if($hash_ptr === $resource_id) { if($hash_ptr === $resource_id) {
$attach_ptr = $r[0]; $attach_ptr = $r[0];
} }
$r[0]['content'] = dbunescbin($r[0]['content']);
$hash_ptr = $r[0]['folder']; $hash_ptr = $r[0]['folder'];
$paths[] = $r[0]; $paths[] = $r[0];
@ -2049,6 +2077,9 @@ function attach_export_data($channel, $resource_id, $deleted = false) {
if($attach_ptr['is_photo']) { if($attach_ptr['is_photo']) {
// This query could potentially result in a few megabytes of data use.
$r = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale asc", $r = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale asc",
dbesc($resource_id), dbesc($resource_id),
intval($channel['channel_id']) intval($channel['channel_id'])
@ -2060,6 +2091,17 @@ function attach_export_data($channel, $resource_id, $deleted = false) {
$ret['photo'] = $r; $ret['photo'] = $r;
} }
// This query can be used instead in memory starved environments. There will be a corresponding
// performance hit during sync because the data will need to be fetched over the network.
// $r = q("select aid,uid,xchan,resource_id,created,edited,title,description,album,filename,mimetype,height,width,filesize,imgscale,photo_usage,profile,is_nsfw,os_storage,display_path,photo_flags,allow_cid,allow_gid,deny_cid,deny_gid from photo where resource_id = '%s' and uid = %d order by imgscale asc",
// dbesc($resource_id),
// intval($channel['channel_id'])
// );
// if($r) {
// $ret['photo'] = $r;
// }
$r = q("select * from item where resource_id = '%s' and resource_type = 'photo' and uid = %d ", $r = q("select * from item where resource_id = '%s' and resource_type = 'photo' and uid = %d ",
dbesc($resource_id), dbesc($resource_id),
intval($channel['channel_id']) intval($channel['channel_id'])
@ -2222,7 +2264,6 @@ function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpat
* the attach.hash of the new parent folder, which must already exist. If $new_folder_hash is blank or empty, * the attach.hash of the new parent folder, which must already exist. If $new_folder_hash is blank or empty,
* the file is relocated to the root of the channel's storage area. * the file is relocated to the root of the channel's storage area.
* *
* @fixme: this operation is currently not synced to clones !!
* *
* @param int $channel_id * @param int $channel_id
* @param int $resource_id * @param int $resource_id
@ -2232,7 +2273,7 @@ function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpat
function attach_move($channel_id, $resource_id, $new_folder_hash) { function attach_move($channel_id, $resource_id, $new_folder_hash) {
$c = channelx_by_n($channel_id); $c = channelx_by_n($channel_id);
if(! $c) if(! ($c && $resource_id))
return false; return false;
$r = q("select * from attach where hash = '%s' and uid = %d limit 1", $r = q("select * from attach where hash = '%s' and uid = %d limit 1",
@ -2244,13 +2285,32 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) {
$oldstorepath = dbunescbin($r[0]['content']); $oldstorepath = dbunescbin($r[0]['content']);
if($r[0]['is_dir']) {
$move_success = true;
$x = q("select hash from attach where folder = '%s' and uid = %d",
dbesc($r[0]['hash']),
intval($channel_id)
);
if($x) {
foreach($x as $xv) {
$rs = attach_move($channel_id,$xv['hash'],$r[0]['hash']);
if(! $rs) {
$move_success = false;
break;
}
}
}
return $move_success;
}
if($new_folder_hash) { if($new_folder_hash) {
$n = q("select * from attach where hash = '%s' and uid = %d limit 1", $n = q("select * from attach where hash = '%s' and uid = %d and is_dir = 1 limit 1",
dbesc($new_folder_hash), dbesc($new_folder_hash),
intval($channel_id) intval($channel_id)
); );
if(! $n) if(! $n)
return; return false;
$newdirname = $n[0]['filename']; $newdirname = $n[0]['filename'];
$newstorepath = dbunescbin($n[0]['content']) . '/' . $resource_id; $newstorepath = dbunescbin($n[0]['content']) . '/' . $resource_id;
@ -2506,7 +2566,7 @@ function save_chunk($channel,$start,$end,$len) {
} }
if(($len - 1) == $end) { if(($len - 1) == $end) {
unlink($tmp_path); unlink($tmp_path);
$result['name'] = $_FILES['files']['tmp_name']; $result['name'] = $_FILES['files']['name'];
$result['type'] = $_FILES['files']['type']; $result['type'] = $_FILES['files']['type'];
$result['tmp_name'] = $new_path; $result['tmp_name'] = $new_path;
$result['error'] = 0; $result['error'] = 0;

View file

@ -246,8 +246,8 @@ function bb_parse_element($match) {
$j = json_decode(base64url_decode($match[1]),true); $j = json_decode(base64url_decode($match[1]),true);
if ($j && local_channel()) { if ($j && local_channel()) {
$text = sprintf( t('Install %s element: '), translate_design_element($j['type'])) . $j['pagetitle']; $text = sprintf( t('Install %1$s element %2$s'), translate_design_element($j['type']), $j['pagetitle']);
$o = EOL . '<a href="#" onclick="importElement(\'' . $match[1] . '\'); return false;" >' . $text . '</a>' . EOL; $o = EOL . '<button class="btn btn-primary" onclick="importElement(\'' . $match[1] . '\'); return false;" >' . $text . '</button>' . EOL;
} }
else { else {
$text = sprintf( t('This post contains an installable %s element, however you lack permissions to install it on this site.' ), translate_design_element($j['type'])) . $j['pagetitle']; $text = sprintf( t('This post contains an installable %s element, however you lack permissions to install it on this site.' ), translate_design_element($j['type'])) . $j['pagetitle'];
@ -329,6 +329,8 @@ function bb_ShareAttributes($match) {
if(strpos($link,'/cards/')) if(strpos($link,'/cards/'))
$type = t('card'); $type = t('card');
elseif(strpos($link,'/articles/'))
$type = t('article');
else else
$type = t('post'); $type = t('post');
@ -426,6 +428,16 @@ function bb_spoilertag($match) {
return '<div onclick="openClose(\'opendiv-' . $rnd . '\'); return false;" class="fakelink">' . $openclose . '</div><blockquote id="opendiv-' . $rnd . '" style="display: none;">' . $text . '</blockquote>'; return '<div onclick="openClose(\'opendiv-' . $rnd . '\'); return false;" class="fakelink">' . $openclose . '</div><blockquote id="opendiv-' . $rnd . '" style="display: none;">' . $text . '</blockquote>';
} }
function bb_summary($match) {
$rnd1 = mt_rand();
$rnd2 = mt_rand();
$rnd3 = mt_rand();
$rnd4 = mt_rand();
return $match[1] . '<div style="display: block;" id="opendiv-' . $rnd2 . '">' . $match[2] . '</div><div style="display: block;" id="opendiv-' . $rnd3 . '" onclick="openClose(\'opendiv-' . $rnd1 . '\'); openClose(\'opendiv-' . $rnd2 . '\'); openClose(\'opendiv-' . $rnd3 . '\'); openClose(\'opendiv-' . $rnd4 . '\'); return false;" class="fakelink">' . t('View article') . '</div><div style="display: none;" id="opendiv-' . $rnd4 . '" onclick="openClose(\'opendiv-' . $rnd1 . '\'); openClose(\'opendiv-' . $rnd2 . '\'); openClose(\'opendiv-' . $rnd3 . '\'); openClose(\'opendiv-' . $rnd4 . '\'); return false;" class="fakelink">' . t('View summary') . '</div><div id="opendiv-' . $rnd1 . '" style="display: none;">' . $match[3] . '</div>';
}
function bb_definitionList($match) { function bb_definitionList($match) {
// $match[1] is the markup styles for the "terms" in the definition list. // $match[1] is the markup styles for the "terms" in the definition list.
// $match[2] is the content between the [dl]...[/dl] tags // $match[2] is the content between the [dl]...[/dl] tags
@ -705,10 +717,12 @@ function parseIdentityAwareHTML($Text) {
return $Text; return $Text;
} }
// BBcode 2 HTML was written by WAY2WEB.net
// extended to work with Mistpark/Friendica/Redmatrix/Hubzilla - Mike Macgirvin
function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) { function bbcode($Text, $options = []) {
$preserve_nl = ((array_key_exists('preserve_nl',$options)) ? $options['preserve_nl'] : false);
$tryoembed = ((array_key_exists('tryoembed',$options)) ? $options['tryoembed'] : true);
$cache = ((array_key_exists('cache',$options)) ? $options['cache'] : false);
call_hooks('bbcode_filter', $Text); call_hooks('bbcode_filter', $Text);
@ -937,27 +951,34 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false)
// Check for h1 // Check for h1
if (strpos($Text,'[h1]') !== false) { if (strpos($Text,'[h1]') !== false) {
$Text = preg_replace("(\[h1\](.*?)\[\/h1\])ism",'<h1>$1</h1>',$Text); $Text = preg_replace("(\[h1\](.*?)\[\/h1\])ism",'<h1>$1</h1>',$Text);
$Text = str_replace('</h1><br />', '</h1>', $Text);
} }
// Check for h2 // Check for h2
if (strpos($Text,'[h2]') !== false) { if (strpos($Text,'[h2]') !== false) {
$Text = preg_replace("(\[h2\](.*?)\[\/h2\])ism",'<h2>$1</h2>',$Text); $Text = preg_replace("(\[h2\](.*?)\[\/h2\])ism",'<h2>$1</h2>',$Text);
$Text = str_replace('</h2><br />', '</h2>', $Text);
} }
// Check for h3 // Check for h3
if (strpos($Text,'[h3]') !== false) { if (strpos($Text,'[h3]') !== false) {
$Text = preg_replace("(\[h3\](.*?)\[\/h3\])ism",'<h3>$1</h3>',$Text); $Text = preg_replace("(\[h3\](.*?)\[\/h3\])ism",'<h3>$1</h3>',$Text);
$Text = str_replace('</h3><br />', '</h3>', $Text);
} }
// Check for h4 // Check for h4
if (strpos($Text,'[h4]') !== false) { if (strpos($Text,'[h4]') !== false) {
$Text = preg_replace("(\[h4\](.*?)\[\/h4\])ism",'<h4>$1</h4>',$Text); $Text = preg_replace("(\[h4\](.*?)\[\/h4\])ism",'<h4>$1</h4>',$Text);
$Text = str_replace('</h4><br />', '</h4>', $Text);
} }
// Check for h5 // Check for h5
if (strpos($Text,'[h5]') !== false) { if (strpos($Text,'[h5]') !== false) {
$Text = preg_replace("(\[h5\](.*?)\[\/h5\])ism",'<h5>$1</h5>',$Text); $Text = preg_replace("(\[h5\](.*?)\[\/h5\])ism",'<h5>$1</h5>',$Text);
$Text = str_replace('</h5><br />', '</h5>', $Text);
} }
// Check for h6 // Check for h6
if (strpos($Text,'[h6]') !== false) { if (strpos($Text,'[h6]') !== false) {
$Text = preg_replace("(\[h6\](.*?)\[\/h6\])ism",'<h6>$1</h6>',$Text); $Text = preg_replace("(\[h6\](.*?)\[\/h6\])ism",'<h6>$1</h6>',$Text);
$Text = str_replace('</h6><br />', '</h6>', $Text);
} }
// Check for table of content without params // Check for table of content without params
while(strpos($Text,'[toc]') !== false) { while(strpos($Text,'[toc]') !== false) {
$toc_id = 'toc-' . random_string(10); $toc_id = 'toc-' . random_string(10);
@ -1051,6 +1072,11 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false)
$Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_options', $Text); $Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_options', $Text);
} }
if(strpos($Text,'[/summary]') !== false) {
$Text = preg_replace_callback("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/ism", 'bb_summary', $Text);
}
// Check for [spoiler] text // Check for [spoiler] text
$endlessloop = 0; $endlessloop = 0;
while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler]") !== false) and (++$endlessloop < 20)) { while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler]") !== false) and (++$endlessloop < 20)) {

View file

@ -325,7 +325,7 @@ function create_identity($arr) {
'hubloc_guid_sig' => $sig, 'hubloc_guid_sig' => $sig,
'hubloc_hash' => $hash, 'hubloc_hash' => $hash,
'hubloc_addr' => channel_reddress($ret['channel']), 'hubloc_addr' => channel_reddress($ret['channel']),
'hubloc_primary' => $primary, 'hubloc_primary' => intval($primary),
'hubloc_url' => z_root(), 'hubloc_url' => z_root(),
'hubloc_url_sig' => base64url_encode(rsa_sign(z_root(),$ret['channel']['channel_prvkey'])), 'hubloc_url_sig' => base64url_encode(rsa_sign(z_root(),$ret['channel']['channel_prvkey'])),
'hubloc_host' => App::get_hostname(), 'hubloc_host' => App::get_hostname(),
@ -1490,7 +1490,7 @@ function gender_icon($gender) {
} }
function advanced_profile(&$a) { function advanced_profile() {
require_once('include/text.php'); require_once('include/text.php');
if(! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),'view_profile')) if(! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),'view_profile'))
return ''; return '';
@ -1982,13 +1982,18 @@ function get_channel_default_perms($uid) {
} }
function profiles_build_sync($channel_id) { function profiles_build_sync($channel_id,$send = true) {
$r = q("select * from profile where uid = %d", $r = q("select * from profile where uid = %d",
intval($channel_id) intval($channel_id)
); );
if($r) { if($r) {
if($send) {
build_sync_packet($channel_id,array('profile' => $r)); build_sync_packet($channel_id,array('profile' => $r));
} }
else {
return $r;
}
}
} }

View file

@ -72,21 +72,22 @@ function categories_widget($baseurl,$selected = '') {
$item_normal = item_normal(); $item_normal = item_normal();
$terms = array(); $terms = array();
$r = q("select distinct(term.term) $r = q("select distinct(term.term) from term join item on term.oid = item.id
from term join item on term.oid = item.id
where item.uid = %d where item.uid = %d
and term.uid = item.uid and term.uid = item.uid
and term.ttype = %d and term.ttype = %d
and term.otype = %d and term.otype = %d
and item.owner_xchan = '%s' and item.owner_xchan = '%s'
and item.item_wall = 1 and item.item_wall = 1
and item.verb != '%s'
$item_normal $item_normal
$sql_extra $sql_extra
order by term.term asc", order by term.term asc",
intval(App::$profile['profile_uid']), intval(App::$profile['profile_uid']),
intval(TERM_CATEGORY), intval(TERM_CATEGORY),
intval(TERM_OBJ_POST), intval(TERM_OBJ_POST),
dbesc(App::$profile['channel_hash']) dbesc(App::$profile['channel_hash']),
dbesc(ACTIVITY_UPDATE)
); );
if($r && count($r)) { if($r && count($r)) {
foreach($r as $rr) foreach($r as $rr)
@ -150,6 +151,54 @@ function cardcategories_widget($baseurl,$selected = '') {
} }
function articlecategories_widget($baseurl,$selected = '') {
if(! feature_enabled(App::$profile['profile_uid'],'categories'))
return '';
$sql_extra = item_permissions_sql(App::$profile['profile_uid']);
$item_normal = "and item.item_hidden = 0 and item.item_type = 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 ";
$terms = array();
$r = q("select distinct(term.term)
from term join item on term.oid = item.id
where item.uid = %d
and term.uid = item.uid
and term.ttype = %d
and term.otype = %d
and item.owner_xchan = '%s'
$item_normal
$sql_extra
order by term.term asc",
intval(App::$profile['profile_uid']),
intval(TERM_CATEGORY),
intval(TERM_OBJ_POST),
dbesc(App::$profile['channel_hash'])
);
if($r && count($r)) {
foreach($r as $rr)
$terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : ''));
return replace_macros(get_markup_template('categories_widget.tpl'),array(
'$title' => t('Categories'),
'$desc' => '',
'$sel_all' => (($selected == '') ? 'selected' : ''),
'$all' => t('Everything'),
'$terms' => $terms,
'$base' => $baseurl,
));
}
return '';
}
function common_friends_visitor_widget($profile_uid,$cnt = 25) { function common_friends_visitor_widget($profile_uid,$cnt = 25) {

View file

@ -482,10 +482,10 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
$previewing = (($preview) ? ' preview ' : ''); $previewing = (($preview) ? ' preview ' : '');
$preview_lbl = t('This is an unsaved preview'); $preview_lbl = t('This is an unsaved preview');
if ($mode === 'network') { if (in_array($mode, [ 'network', 'pubstream'])) {
$profile_owner = local_channel(); $profile_owner = local_channel();
$page_writeable = true; $page_writeable = ((local_channel()) ? true : false);
if (!$update) { if (!$update) {
// The special div is needed for liveUpdate to kick in for this page. // The special div is needed for liveUpdate to kick in for this page.
@ -513,6 +513,12 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
} }
} }
elseif ($mode === 'hq') {
$profile_owner = local_channel();
$page_writeable = true;
$live_update_div = '<div id="live-hq"></div>' . "\r\n";
}
elseif ($mode === 'channel') { elseif ($mode === 'channel') {
$profile_owner = App::$profile['profile_uid']; $profile_owner = App::$profile['profile_uid'];
$page_writeable = ($profile_owner == local_channel()); $page_writeable = ($profile_owner == local_channel());
@ -539,6 +545,15 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
$jsreload = $_SESSION['return_url']; $jsreload = $_SESSION['return_url'];
} }
elseif ($mode === 'articles') {
$profile_owner = App::$profile['profile_uid'];
$page_writeable = ($profile_owner == local_channel());
$live_update_div = '<div id="live-articles"></div>' . "\r\n"
. "<script> var profile_uid = " . App::$profile['profile_uid']
. "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n";
$jsreload = $_SESSION['return_url'];
}
elseif ($mode === 'display') { elseif ($mode === 'display') {
$profile_owner = local_channel(); $profile_owner = local_channel();
@ -619,13 +634,14 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
// "New Item View" on network page or search page results // "New Item View" on network page or search page results
// - just loop through the items and format them minimally for display // - just loop through the items and format them minimally for display
//$tpl = get_markup_template('search_item.tpl');
$tpl = 'search_item.tpl'; $tpl = 'search_item.tpl';
foreach($items as $item) { foreach($items as $item) {
$x = [ 'mode' => $mode, 'item' => $item ]; $x = [
'mode' => $mode,
'item' => $item
];
call_hooks('stream_item',$x); call_hooks('stream_item',$x);
if($x['item']['blocked']) if($x['item']['blocked'])
@ -646,14 +662,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE)))
&& ($item['id'] != $item['parent'])) && ($item['id'] != $item['parent']))
continue; continue;
// $nickname = $item['nickname'];
} }
// else
// $nickname = App::$user['nickname'];
// $profile_name = ((strlen($item['author-name'])) ? $item['author-name'] : $item['name']);
// if($item['author-link'] && (! $item['author-name']))
// $profile_name = $item['author-link'];
$sp = false; $sp = false;
$profile_link = best_link_url($item,$sp); $profile_link = best_link_url($item,$sp);
@ -662,8 +671,6 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
else else
$profile_link = zid($profile_link); $profile_link = zid($profile_link);
// $normalised = normalise_link((strlen($item['author-link'])) ? $item['author-link'] : $item['url']);
$profile_name = $item['author']['xchan_name']; $profile_name = $item['author']['xchan_name'];
$profile_link = $item['author']['xchan_url']; $profile_link = $item['author']['xchan_url'];
$profile_avatar = $item['author']['xchan_photo_m']; $profile_avatar = $item['author']['xchan_photo_m'];
@ -714,7 +721,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
$conv_link_mid = (($mode == 'moderate') ? $item['parent_mid'] : $item['mid']); $conv_link_mid = (($mode == 'moderate') ? $item['parent_mid'] : $item['mid']);
$conv_link = (($item['item_type'] == ITEM_TYPE_CARD) ? $item['plink'] : z_root() . '/display/' . gen_link_id($conv_link_mid)); $conv_link = ((in_array($item['item_type'],[ ITEM_TYPE_CARD, ITEM_TYPE_ARTICLE] )) ? $item['plink'] : z_root() . '/display/' . gen_link_id($conv_link_mid));
$tmp_item = array( $tmp_item = array(
@ -748,8 +755,8 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
'forged' => $forged, 'forged' => $forged,
'txt_cats' => t('Categories:'), 'txt_cats' => t('Categories:'),
'txt_folders' => t('Filed under:'), 'txt_folders' => t('Filed under:'),
'has_cats' => ((count($body['categories'])) ? 'true' : ''), 'has_cats' => (($body['categories']) ? 'true' : ''),
'has_folders' => ((count($body['folders'])) ? 'true' : ''), 'has_folders' => (($body['folders']) ? 'true' : ''),
'text' => strip_tags($body['html']), 'text' => strip_tags($body['html']),
'ago' => relative_date($item['created']), 'ago' => relative_date($item['created']),
'app' => $item['app'], 'app' => $item['app'],
@ -759,6 +766,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), 'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''),
'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''), 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''),
'location' => $location, 'location' => $location,
'divider' => false,
'indent' => '', 'indent' => '',
'owner_name' => $owner_name, 'owner_name' => $owner_name,
'owner_url' => $owner_url, 'owner_url' => $owner_url,
@ -1465,7 +1473,7 @@ function sort_item_children($items) {
$result = $items; $result = $items;
usort($result,'sort_thr_created_rev'); usort($result,'sort_thr_created_rev');
foreach($result as $k => $i) { foreach($result as $k => $i) {
if(count($result[$k]['children'])) { if($result[$k]['children']) {
$result[$k]['children'] = sort_item_children($result[$k]['children']); $result[$k]['children'] = sort_item_children($result[$k]['children']);
} }
} }
@ -1475,7 +1483,7 @@ function sort_item_children($items) {
function add_children_to_list($children, &$arr) { function add_children_to_list($children, &$arr) {
foreach($children as $y) { foreach($children as $y) {
$arr[] = $y; $arr[] = $y;
if(count($y['children'])) if($y['children'])
add_children_to_list($y['children'], $arr); add_children_to_list($y['children'], $arr);
} }
} }
@ -1888,6 +1896,17 @@ function profile_tabs($a, $is_owner = false, $nickname = null){
); );
} }
if(feature_enabled($uid,'articles')) {
$tabs[] = array(
'label' => t('articles'),
'url' => z_root() . '/articles/' . $nickname,
'sel' => ((argv(0) == 'articles') ? 'active' : ''),
'title' => t('View Articles'),
'id' => 'articles-tab',
'icon' => 'file-text-o'
);
}
if($has_webpages && feature_enabled($uid,'webpages')) { if($has_webpages && feature_enabled($uid,'webpages')) {
$tabs[] = array( $tabs[] = array(
'label' => t('Webpages'), 'label' => t('Webpages'),

View file

@ -22,13 +22,13 @@ function rsa_verify($data,$sig,$key,$alg = 'sha256') {
$alg = OPENSSL_ALGO_SHA256; $alg = OPENSSL_ALGO_SHA256;
$verify = @openssl_verify($data,$sig,$key,$alg); $verify = @openssl_verify($data,$sig,$key,$alg);
if(! $verify) { if($verify === (-1)) {
while($msg = openssl_error_string()) while($msg = openssl_error_string())
logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR); logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR);
btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR);
} }
return $verify; return (($verify > 0) ? true : false);
} }
function pkcs5_pad ($text, $blocksize) function pkcs5_pad ($text, $blocksize)

View file

@ -6,6 +6,8 @@
use Sabre\VObject; use Sabre\VObject;
require_once('include/bbcode.php');
/** /**
* @brief Returns an event as HTML. * @brief Returns an event as HTML.
* *
@ -14,7 +16,6 @@ use Sabre\VObject;
*/ */
function format_event_html($ev) { function format_event_html($ev) {
require_once('include/bbcode.php');
if(! ((is_array($ev)) && count($ev))) if(! ((is_array($ev)) && count($ev)))
return ''; return '';
@ -192,7 +193,7 @@ function format_todo_ical($ev) {
function format_ical_text($s) { function format_ical_text($s) {
require_once('include/bbcode.php');
require_once('include/html2plain.php'); require_once('include/html2plain.php');
$s = html2plain(bbcode($s)); $s = html2plain(bbcode($s));
@ -983,7 +984,6 @@ function event_store_item($arr, $event) {
require_once('include/datetime.php'); require_once('include/datetime.php');
require_once('include/items.php'); require_once('include/items.php');
require_once('include/bbcode.php');
$item = null; $item = null;

View file

@ -126,6 +126,16 @@ function get_features($filtered = true) {
feature_level('cards',1), feature_level('cards',1),
], ],
[
'articles',
t('Articles'),
t('Create interactive articles'),
false,
get_config('feature_lock','articles'),
feature_level('articles',1),
],
[ [
'nav_channel_select', 'nav_channel_select',
t('Navigation Channel Select'), t('Navigation Channel Select'),
@ -363,15 +373,6 @@ function get_features($filtered = true) {
t('Post/Comment Tools'), t('Post/Comment Tools'),
[
'markdown',
t('Markdown'),
t('Use markdown for editing posts'),
false,
get_config('feature_lock','markdown'),
feature_level('markdown',2),
],
[ [
'commtag', 'commtag',
t('Community Tagging'), t('Community Tagging'),
@ -480,6 +481,8 @@ function get_features($filtered = true) {
else { else {
$narr = $arr; $narr = $arr;
} }
call_hooks('get_features',$narr);
return $narr; $x = [ 'features' => $narr, 'filtered' => $filtered, 'techlevel' => $techlevel ];
call_hooks('get_features',$x);
return $x['features'];
} }

View file

@ -65,16 +65,35 @@ function get_feed_for($channel, $observer_hash, $params) {
if(! $channel) if(! $channel)
http_status_exit(401); http_status_exit(401);
if($params['pages']) {
if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_pages'))
http_status_exit(403);
} else {
if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_stream'))
http_status_exit(403);
}
// logger('params: ' . print_r($params,true)); // logger('params: ' . print_r($params,true));
$interactive = ((is_array($params) && array_key_exists('interactive',$params)) ? intval($params['interactive']) : 0);
if($params['pages']) {
if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_pages')) {
if($interactive) {
return '';
}
else {
http_status_exit(403);
}
}
}
else {
if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_stream')) {
if($interactive) {
return '';
}
else {
http_status_exit(403);
}
}
}
$feed_template = get_markup_template('atom_feed.tpl'); $feed_template = get_markup_template('atom_feed.tpl');
$atom = ''; $atom = '';
@ -1286,7 +1305,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
// allow likes of comments // allow likes of comments
if($item_parent_mid && activity_match($datarray['verb'],ACTVITY_LIKE)) { if($item_parent_mid && activity_match($datarray['verb'],ACTIVITY_LIKE)) {
$datarray['thr_parent'] = $item_parent_mid[0]['parent_mid']; $datarray['thr_parent'] = $item_parent_mid[0]['parent_mid'];
} }
@ -1782,12 +1801,17 @@ function compat_photos_list($s) {
if($found) { if($found) {
foreach($matches as $match) { foreach($matches as $match) {
$ret[] = [ $entry = [
'href' => $match[2], 'href' => $match[2],
'length' => 0,
'type' => guess_image_type($match[2]) 'type' => guess_image_type($match[2])
]; ];
$sizer = new \Zotlabs\Lib\Img_filesize($match[2]);
$size = $sizer->getSize();
if(intval($size)) {
$entry['length'] = intval($size);
}
$ret[] = $entry;
} }
} }

View file

@ -226,6 +226,8 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
} }
$profile_assign = get_pconfig($uid,'system','profile_assign','');
$r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook $r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook
where abook_xchan = '%s' and abook_channel = %d limit 1", where abook_xchan = '%s' and abook_channel = %d limit 1",
@ -265,6 +267,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
'abook_channel' => intval($uid), 'abook_channel' => intval($uid),
'abook_closeness' => intval($closeness), 'abook_closeness' => intval($closeness),
'abook_xchan' => $xchan_hash, 'abook_xchan' => $xchan_hash,
'abook_profile' => $profile_assign,
'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0), 'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0),
'abook_created' => datetime_convert(), 'abook_created' => datetime_convert(),
'abook_updated' => datetime_convert(), 'abook_updated' => datetime_convert(),

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