diff options
author | zotlabs <mike@macgirvin.com> | 2019-04-24 16:21:13 -0700 |
---|---|---|
committer | zotlabs <mike@macgirvin.com> | 2019-04-24 16:21:13 -0700 |
commit | 831b9d443330cb806871ab06aaa977adafbe661e (patch) | |
tree | 4c067a3b701ea56f10070c386b26a30f93666eb0 | |
parent | e56c0a6251e8361ba65702151473a0560506db77 (diff) | |
parent | e937e8fff96a935b14a479c062f6b0d00b90b99d (diff) | |
download | volse-hubzilla-831b9d443330cb806871ab06aaa977adafbe661e.tar.gz volse-hubzilla-831b9d443330cb806871ab06aaa977adafbe661e.tar.bz2 volse-hubzilla-831b9d443330cb806871ab06aaa977adafbe661e.zip |
Merge branch 'dev' of https://framagit.org/hubzilla/core into dev
-rw-r--r-- | .homeinstall/README.md | 186 | ||||
-rw-r--r-- | .homeinstall/hubzilla-config.txt.template | 11 | ||||
-rw-r--r--[-rwxr-xr-x] | .homeinstall/hubzilla-setup.sh | 291 | ||||
-rw-r--r-- | Zotlabs/Module/Cdav.php | 43 | ||||
-rw-r--r-- | Zotlabs/Module/Channel_calendar.php | 620 | ||||
-rw-r--r-- | Zotlabs/Module/Editpost.php | 3 | ||||
-rw-r--r-- | Zotlabs/Module/Photo.php | 72 | ||||
-rw-r--r-- | Zotlabs/Module/Ping.php | 2 | ||||
-rw-r--r-- | Zotlabs/Module/Profile_photo.php | 34 | ||||
-rw-r--r-- | Zotlabs/Widget/Cdav.php | 18 | ||||
-rw-r--r-- | doc/admin/administrator_guide.md | 269 | ||||
-rwxr-xr-x | include/dba/dba_pdo.php | 18 | ||||
-rw-r--r-- | include/event.php | 4 | ||||
-rw-r--r-- | view/css/cdav_calendar.css | 6 | ||||
-rw-r--r-- | view/theme/redbasic/css/style.css | 11 | ||||
-rw-r--r-- | view/tpl/cdav_calendar.tpl | 223 | ||||
-rw-r--r-- | view/tpl/cdav_widget_calendar.tpl | 14 | ||||
-rwxr-xr-x | view/tpl/connection_template.tpl | 2 | ||||
-rwxr-xr-x | view/tpl/jot-header.tpl | 2 | ||||
-rwxr-xr-x | view/tpl/profile_photo.tpl | 5 |
20 files changed, 1242 insertions, 592 deletions
diff --git a/.homeinstall/README.md b/.homeinstall/README.md index d63931a84..45e1ba0e6 100644 --- a/.homeinstall/README.md +++ b/.homeinstall/README.md @@ -1,16 +1,43 @@ # Hubzilla at Home next to your Router -Run hubzilla-setup.sh for an unattended installation of hubzilla. +This readme will show you how to install and run Hubzilla or Zap at home. + +The installation is done by a script. + +What the script will do for you... + ++ install everything required by Zap/Hubzilla, basically a web server (Apache), PHP, a database (MySQL), certbot,... ++ create a database ++ run certbot to have everything for a secure connection (httpS) ++ create a script for daily maintenance + - backup to external disk (certificates, database, /var/www/) + - renew certfificate (letsencrypt) + - update of Zap/Hubzilla + - update of Debian + - restart ++ create cron jobs for + - DynDNS (selfHOST.de or freedns.afraid.org) every 5 minutes + - Master.php for Zap/Hubzilla every 10 minutes + - daily maintenance script every day at 05:30 The script is known to work without adjustments with + Hardware - - Mini-PC with Debian-9.5-amd64, or - - Rapberry 3 with Raspbian, Debian-9.5 + - Mini-PC with Debian 9 (stretch), or + - Rapberry 3 with Raspbian, Debian 9 + DynDNS - selfHOST.de - freedns.afraid.org +The script can install both [Hubzilla](https://zotlabs.org/page/hubzilla/hubzilla-project) and [Zap](https://zotlabs.com/zap/). Make sure to use the correct GIT repositories. + ++ Hubzilla + - core: git clone https://framagit.org/hubzilla/core.git html (in this readme) + - addons: util/add_addon_repo https://framagit.org/hubzilla/addons.git hzaddons (in hubzilla-setup.sh) ++ Zap + - core: git clone https://framagit.org/zot/zap.git html (in this readme) + - addons: util/add_addon_repo https://framagit.org/zot/zap-addons.git zaddons (in hubzilla-setup.sh) + ## Disclaimers - This script does work with Debian 9 only. @@ -29,7 +56,7 @@ Hardware Software + Fresh installation of Debian 9 (Stretch) -+ Router with open ports 80 and 443 for your Hub ++ Router with open ports 80 and 443 for your web server ## The basic steps (quick overview) @@ -44,10 +71,9 @@ Software - 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 + - Prepare your external disk for backups - hubzilla-setup.sh as root - ... wait, wait, wait until the script is finised - - reboot + Open your domain with a browser and step throught the initial configuration of hubzilla. ## Troubleshooting @@ -66,57 +92,27 @@ In Admin settings of hubzilla or via terminal # Step-by-Step in Detail -## Preparations Hardware - -### Mini-PC - -### Recommended: USB Drive for Backups - -The installation will create a daily backup written to an external drive. - -The USB drive must be compatible with the filesystems - -- ext4 (if you do not want to encrypt the USB) -- LUKS + ext4 (if you want to encrypt the USB) - -The backup includes - -- Hubzilla DB -- Hubzilla installation /var/www/html -- Certificates for letsencrypt - ## Preparations Software -### Install Debian Linux on the Mini-PC - -Download the stable Debian at https://www.debian.org/ -(Debian 8 is no longer supported.) +## Install Debian 9 -Create bootable USB drive with Debian on it.You could use +Provided you use a Raspberry Pi 3... -- unetbootin, https://en.wikipedia.org/wiki/UNetbootin -- or simply the linux command "dd" +Download the OS Raspbian from https://www.raspberrypi.org/downloads/raspbian/ -Example for command dd... +Follow the installation instruction there. - su - - dd if=2018-10-09-raspbian-stretch.img of=/dev/mmcblk0 +## Configure your Router -Do not forget to unmount the SD card before and check if unmounted like in this example... +Your web has to be visible in the internet. - su - - umount /dev/mmcblk0* - df -h +Open the ports 80 and 443 on your router for your Debian. Make sure your web server is marked as "exposed host". +## Preparations Dynamic IP Address -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. - -### Configure your Router - -Open the ports 80 and 443 on your router for your Debian +Follow the instructions in .homeinstall/hubzilla-config.txt. -## Preparations Dynamic IP Address +In short... Your Hubzilla must be reachable by a domain that you can type in your browser @@ -132,105 +128,15 @@ There are two ways to get a domain... ...for example buy at selfHOST.de -The cost are around 10,- € once and 1,50 € per month (2017). +The cost is 1,50 € per month (2019). ### Method 2: Register a free subdomain ...for example register at freedns.afraid.org -Follow the instructions in .homeinstall/hubzilla-config.txt. - - -## Install Hubzilla on your Debian - -Login to your debian -(Provided your username is "you" and the name of the mini pc is "debian". You -could take the IP address instead of "debian") - - ssh -X you@debian - -Change to root user - - su -l - -Install git - - apt-get install git - -Make the directory for apache and change diretory to it - - mkdir /var/www - cd /var/www/ - -Clone hubzilla from git ("git pull" will update it later) - - git clone https://framagit.org/hubzilla/core.git html - -Change to the install script - - cd html/.homeinstall/ - -Copy the template file - - cp hubzilla-config.txt.template hubzilla-config.txt - -Modify the file "hubzilla-config.txt". Read the instructions there carefully and enter your values. - - nano hubzilla-config.txt - -Make sure your external drive (for backups) is plugged in and can be mounted as configured in "hubzilla-config.txt". Otherwise the daily backups will not work. - -Run the script - - ./hubzilla-setup.sh - -Wait... The script should not finish with an error message. - -In a webbrowser open your domain. -Expected: A test page of hubzilla is shown. All checks there should be -successfull. Go on... -Expected: A page for the Hubzilla server configuration shows up. - -Leave db server name "127.0.0.1" and port "0" untouched. - -Enter - -- DB user name = hubzilla -- DB pass word = This is the password you entered in "hubzilla-config.txt" -- DB name = hubzilla - -Leave db type "MySQL" untouched. - -Follow the instructions in the next pages. - -Recommended: Set path to imagemagick - -- in admin settings of hubzilla or -- via terminal - - util/config system.imagick_convert_path /usr/bin/convert - -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 - - -## Install Hubzilla in a Virtual Machine for Test Purposes - -Modify the file "hubzilla-config.txt". - - nano hubzilla-config.txt - -There use - - le_domain=localhost - -## Note for the Rasperry +## Note on Rasperry -The script was tested with an Raspberry 3 under Raspian (Debian 9.5, 2018-10-09-raspbian-stretch.img). +The script was tested with an Raspberry 3 under Raspian, Debian 9. It is recommended to run the Raspi without graphical frontend (X-Server). Use... @@ -240,7 +146,7 @@ to boot the Rapsi to the client console. DO NOT FORGET TO CHANGE THE DEFAULT PASSWORD FOR USER PI! -If the validation of the mail address fails for the very first registered user... +On a Raspian Stretch (Debian 9) the validation of the mail address fails for the very first user. This used to happen on some *bsd distros but there was some work to fix that a year ago (2017). So if your system isn't registered in DNS or DNS isn't active do diff --git a/.homeinstall/hubzilla-config.txt.template b/.homeinstall/hubzilla-config.txt.template index e42da0e4e..f0bf6121c 100644 --- a/.homeinstall/hubzilla-config.txt.template +++ b/.homeinstall/hubzilla-config.txt.template @@ -2,8 +2,8 @@ ### MANDATORY - database password ############# # # Please give your database password +# It is better to not use blanks inside the password. # Example: db_pass=pass_word_with_no_blanks_in_it -# Example: db_pass="this password has blanks in it" db_pass= ############################################### @@ -18,9 +18,12 @@ db_pass= # Example: my.cooldomain.org # Example: cooldomain.org # -# Example: localhost (test installation without certificates for httpS) +# You might use "localhost" for a LOCAL TEST installation. +# This is usefull if you want to debug the server inside a VM. # -# Email is optional +# Example: localhost +# +# Email is optional if you use "localhost". # # le_domain= @@ -30,7 +33,7 @@ le_email= ### OPTIONAL - selfHOST - dynamic IP address ## # # 1. Register a domain at selfhost.de -# - choose offer "DOMAIN dynamisch" 1,50€/mon at 08.01.2016 +# - choose offer "DOMAIN dynamisch" 1,50€/mon at 04/2019 # 2. Get your configuration for dynamic IP update # - Log in at selfhost.de # - go to "DynDNS Accounte" diff --git a/.homeinstall/hubzilla-setup.sh b/.homeinstall/hubzilla-setup.sh index 1f3ad5db5..023ef7afc 100755..100644 --- a/.homeinstall/hubzilla-setup.sh +++ b/.homeinstall/hubzilla-setup.sh @@ -3,7 +3,10 @@ # How to use # ---------- # -# This file automates the installation of hubzilla under Debian Linux +# This file automates the installation of +# - hubzilla: https://zotlabs.org/page/hubzilla/hubzilla-project and +# - zap: https://zotlabs.com/zap/ +# under Debian Linux # # 1) Copy the file "hubzilla-config.txt.template" to "hubzilla-config.txt" # Follow the instuctions there @@ -25,16 +28,14 @@ # * php, # * mysql - the database for hubzilla, # * phpmyadmin, -# * git to download and update hubzilla itself +# * git to download and update hubzilla addon # - download hubzilla core and addons # - configure cron -# * "poller.php" for regular background prozesses of hubzilla -# * to_do "apt-get update" and "apt-get dist-upgrade" to keep linux -# up-to-date -# * to_do backup hubzillas database and files (rsnapshot) -# - configure dynamic ip with cron -# - to_do letsencrypt -# - to_do redirection to https +# * "Master.php" for regular background prozesses of hubzilla +# * "apt-get update" and "apt-get dist-upgrade" and "apt-get autoremove" to keep linux up-to-date +# * run command to keep the IP up-to-date > DynDNS provided by selfHOST.de or freedns.afraid.org +# * backup hubzillas database and files (rsync) +# - letsencrypt # # # Discussion @@ -43,26 +44,11 @@ # Security - password is the same for mysql-server, phpmyadmin and hubzilla db # - The script runs into installation errors for phpmyadmin if it uses # different passwords. For the sake of simplicity one singel password. -# -# Security - suhosin for PHP -# - The script does not install suhosin. -# - Is the security package suhosin usefull or not usefull? # # Hubzilla - email verification # - The script switches off email verification off in all htconfig.tpl. # Example: /var/www/html/view/en/htconfig.tpl # - Is this a silly idea or not? -# -# -# Remove Hubzilla (for a fresh start using the script) -# ---------------------------------------------------- -# -# You could use /var/www/hubzilla-remove.sh -# that is created by hubzilla-setup.sh. -# -# The script will remove (almost everything) what was installed by the script. -# After the removal you could run the script again to have a fresh install -# of all applications including hubzilla and its database. # # How to restore from backup # -------------------------- @@ -76,18 +62,10 @@ # # hubzilla-daily.sh makes a (daily) backup of all relevant files # - /var/lib/mysql/ > hubzilla database -# - /var/www/html/ > hubzilla from github -# - /var/www/letsencrypt/ > certificates -# -# hubzilla-daily.sh writes the backup -# - either to an external disk compatible to LUKS+ext4 (see hubzilla-config.txt) -# - or to /var/cache/rsnapshot in case the external disk is not plugged in +# - /var/www/ > hubzilla/zap from github +# - /etc/letsencrypt/ > certificates # -# Restore backup -# - - - - - - - -# -# This was not tested yet. -# Bacically you can copy the files from the backup to the server. +# hubzilla-daily.sh writes the backup to an external disk compatible to LUKS+ext4 (see hubzilla-config.txt) # # Credits # ------- @@ -136,11 +114,11 @@ function check_config { # backup is important and should be checked if [ -n "$backup_device_name" ] then - if [ ! -d "$backup_mount_point" ] - then - mkdir "$backup_mount_point" - fi - device_mounted=0 + if [ ! -d "$backup_mount_point" ] + then + mkdir "$backup_mount_point" + fi + device_mounted=0 if fdisk -l | grep -i "$backup_device_name.*linux" then print_info "ok - filesystem of external device is linux" @@ -264,7 +242,7 @@ function install_sendmail { function install_php { # openssl and mbstring are included in libapache2-mod-php print_info "installing php..." - nocheck_install "libapache2-mod-php php php-pear php-curl php-mcrypt php-gd" + nocheck_install "libapache2-mod-php php php-pear php-curl php-mcrypt php-gd php-mysqli php-mbstring php-xml" 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 } @@ -449,11 +427,11 @@ function configure_cron_selfhost { print_info "configure cron for selfhost..." if [ -z "$selfhost_user" ] then - print_info "freedns is not configured because freedns_key is empty in $configfile" + print_info "selfhost is not configured because selfhost_key is empty in $configfile" else # Use cron for dynamich ip update # - at reboot - # - every 30 minutes + # - every 5 minutes if [ -z "`grep 'selfhost-updater.sh' /etc/crontab`" ] then echo "@reboot root bash /etc/selfhost/selfhost-updater.sh update > /dev/null 2>&1" >> /etc/crontab @@ -471,89 +449,24 @@ function install_letsencrypt { then die "Failed to install let's encrypt: 'le_domain' is empty in $configfile" fi - # configure apache - apache_le_conf=/etc/apache2/sites-available/le-default.conf - if [ -f $apache_le_conf ] - then - print_info "$apache_le_conf exist already" - else - cat > $apache_le_conf <<END -# letsencrypt default Apache configuration -Alias /.well-known/acme-challenge /var/www/letsencrypt - -<Directory /var/www/letsencrypt> - Options FollowSymLinks - Allow from all -</Directory> -END - a2ensite le-default.conf - service apache2 restart - fi - # download the shell script - if [ -d $le_dir ] - then - print_info "letsenrypt exists already (nothing downloaded > no certificate created and registered)" - return 0 - fi - git clone https://github.com/lukas2511/dehydrated $le_dir - cd $le_dir - # create config file for letsencrypt.sh - echo "WELLKNOWN=$le_dir" > $le_dir/config.sh - if [ -n "$le_email" ] - then - echo "CONTACT_EMAIL=$le_email" >> $le_dir/config.sh - fi - # create domain file for letsencrypt.sh - # WATCH THIS: - # - It did not work wit "sub.domain.org www.sub.domain.org". - # - So just use "sub.domain.org" only! - echo "$le_domain" > $le_dir/domains.txt - # test apache config for letsencrpyt - url_http=http://$le_domain/.well-known/acme-challenge/domains.txt - wget_output=$(wget -nv --spider --max-redirect 0 $url_http) - if [ $? -ne 0 ] - then - die "Failed to load $url_http" - fi - # accept terms of service of letsencrypt - ./dehydrated --register --accept-terms - # run script dehydrated - # - ./dehydrated --cron --config $le_dir/config.sh -} - -function configure_apache_for_https { - print_info "configuring apache to use httpS ..." - # letsencrypt.sh - # - # "${BASEDIR}/certs/${domain}/privkey.pem" - # "${BASEDIR}/certs/${domain}/cert.pem" - # "${BASEDIR}/certs/${domain}/fullchain.pem" - # - SSLCertificateFile=${le_dir}/certs/${le_domain}/cert.pem - SSLCertificateKeyFile=${le_dir}/certs/${le_domain}/privkey.pem - SSLCertificateChainFile=${le_dir}/certs/${le_domain}/fullchain.pem - if [ ! -f $SSLCertificateFile ] + # check if user gave mail address + if [ -z "$le_email" ] then - print_warn "Failed to configure apache for httpS: Missing certificate file $SSLCertificateFile" - return 0 + die "Failed to install let's encrypt: 'le_domain' is empty in $configfile" fi - # make sure that the ssl mode is enabled - print_info "...configuring apache to use httpS - a2enmod ssl ..." - a2enmod ssl - # modify apach' ssl conf file - if grep -i "ServerName" $sslconf + nocheck_install "apt-transport-https" + # add backports to your sources.list + backports_list=/etc/apt/sources.list.d/backports.list + if [ -f $backports_list ] then - print_info "seems that apache was already configered to use httpS with $sslconf" + print_info "$backports_list exist already" else - sed -i "s/ServerAdmin.*$/ServerAdmin webmaster@localhost\\n ServerName ${le_domain}/" $sslconf - fi - sed -i s#/etc/ssl/certs/ssl-cert-snakeoil.pem#$SSLCertificateFile# $sslconf - sed -i s#/etc/ssl/private/ssl-cert-snakeoil.key#$SSLCertificateKeyFile# $sslconf - sed -i s#/etc/apache2/ssl.crt/server-ca.crt#$SSLCertificateChainFile# $sslconf - sed -i s/#SSLCertificateChainFile/SSLCertificateChainFile/ $sslconf - # apply changes - a2ensite default-ssl.conf + echo "deb https://deb.debian.org/debian stretch-backports main" > $backports_list + fi + apt-get -y update + DEBIAN_FRONTEND=noninteractive apt-get -q -y -t stretch-backports install certbot python-certbot-apache + print_info "run certbot ..." + certbot --apache -w /var/www/html -d $le_domain -m $le_email --agree-tos --non-interactive --redirect --hsts --uir service apache2 restart } @@ -572,7 +485,10 @@ function check_https { function install_hubzilla { print_info "installing hubzilla addons..." cd /var/www/html/ - util/add_addon_repo https://framagit.org/hubzilla/addons.git hzaddons + # if you install Hubzilla + util/add_addon_repo https://framagit.org/hubzilla/addons hzaddons + # if you install ZAP + #util/add_addon_repo https://framagit.org/zot/zap-addons.git zaddons mkdir -p "store/[data]/smarty3" chmod -R 777 store touch .htconfig.php @@ -582,7 +498,7 @@ function install_hubzilla { chown root:www-data /var/www/html/ chown root:www-data /var/www/html/.htaccess chmod 0644 /var/www/html/.htaccess - # try to switch off email registration + print_info "try to switch off email registration..." sed -i "s/verify_email.*1/verify_email'] = 0/" /var/www/html/view/*/ht* if [ -n "`grep -r 'verify_email.*1' /var/www/html/view/`" ] then @@ -591,49 +507,9 @@ function install_hubzilla { print_info "installed hubzilla" } -function rewrite_to_https { - print_info "configuring apache to redirect http to httpS ..." - htaccessfile=/var/www/html/.htaccess - if grep -i "https" $htaccessfile - then - print_info "...configuring apache to redirect http to httpS was already done in $htaccessfile" - else - sed -i "s#QSA]#QSA]\\n RewriteCond %{SERVER_PORT} !^443$\\n RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]#" $htaccessfile - fi - service apache2 restart -} - -# This will allways overwrite both config files -# - internal disk -# - external disk (LUKS + ext4) -# of rsnapshot for hubzilla -function install_rsnapshot { - print_info "installing rsnapshot..." - nocheck_install "rsnapshot" - # internal disk - cp -f /etc/rsnapshot.conf $snapshotconfig - sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig - sed -i "s/^backup/#backup/" $snapshotconfig - echo "backup /var/lib/mysql/ localhost/" >> $snapshotconfig - echo "backup /var/www/html/ localhost/" >> $snapshotconfig - echo "backup /var/www/letsencrypt/ localhost/" >> $snapshotconfig - # external disk - if [ -n "$backup_device_name" ] - then - cp -f /etc/rsnapshot.conf $snapshotconfig_external_device - sed -i "s#snapshot_root.*#snapshot_root $backup_mount_point#" $snapshotconfig_external_device - sed -i "/alpha/s/6/30/" $snapshotconfig_external_device - sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig_external_device - sed -i "s/^backup/#backup/" $snapshotconfig_external_device - if [ -z "`grep 'letsencrypt' $snapshotconfig_external_device`" ] - then - echo "backup /var/lib/mysql/ localhost/" >> $snapshotconfig_external_device - echo "backup /var/www/html/ localhost/" >> $snapshotconfig_external_device - echo "backup /var/www/letsencrypt/ localhost/" >> $snapshotconfig_external_device - fi - else - print_info "No backup configuration (rsnapshot) for external device configured. Reason: backup_device_name and/or backup_device_pass not given in $configfile" - fi +function install_rsync { + print_info "installing rsync..." + nocheck_install "rsync" } function install_cryptosetup { @@ -644,28 +520,28 @@ function install_cryptosetup { function configure_cron_daily { print_info "configuring cron..." # every 10 min for poller.php - if [ -z "`grep 'poller.php' /etc/crontab`" ] + if [ -z "`grep 'Master.php' /etc/crontab`" ] then echo "*/10 * * * * www-data cd /var/www/html; php Zotlabs/Daemon/Master.php Cron >> /dev/null 2>&1" >> /etc/crontab fi # Run external script daily at 05:30 # - stop apache and mysql-server - # - backup hubzilla + # - renew the certificate of letsencrypt + # - backup db, files (/var/www/html), certificates if letsencrypt # - update hubzilla core and addon # - update and upgrade linux - # - reboot + # - reboot is done by "shutdown -h now" because "reboot" hangs sometimes depending on the system echo "#!/bin/sh" > /var/www/$hubzilladaily echo "#" >> /var/www/$hubzilladaily echo "echo \" \"" >> /var/www/$hubzilladaily echo "echo \"+++ \$(date) +++\"" >> /var/www/$hubzilladaily echo "echo \" \"" >> /var/www/$hubzilladaily echo "echo \"\$(date) - renew certificate...\"" >> /var/www/$hubzilladaily -echo "bash $le_dir/dehydrated --cron --config $le_dir/config.sh" >> /var/www/$hubzilladaily +echo "certbot renew --noninteractive" >> /var/www/$hubzilladaily echo "#" >> /var/www/$hubzilladaily -echo "# stop hubzilla" >> /var/www/$hubzilladaily -echo "echo \"\$(date) - stoping apache and mysql...\"" >> /var/www/$hubzilladaily +echo "echo \"\$(date) - stopping apache and mysql...\"" >> /var/www/$hubzilladaily echo "service apache2 stop" >> /var/www/$hubzilladaily -echo "/etc/init.d/mysql stop # to avoid inconsistancies" >> /var/www/$hubzilladaily +echo "/etc/init.d/mysql stop # to avoid inconsistencies" >> /var/www/$hubzilladaily echo "#" >> /var/www/$hubzilladaily echo "# backup" >> /var/www/$hubzilladaily echo "echo \"\$(date) - try to mount external device for backup...\"" >> /var/www/$hubzilladaily @@ -696,11 +572,13 @@ echo " if mount $backup_device_name $backup_mount_point" >> /var/www/$hub echo " then" >> /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 " rsnapshot -c $snapshotconfig_external_device alpha" >> /var/www/$hubzilladaily -echo " echo \"\$(date) - disk sizes...\"" >> /var/www/$hubzilladaily -echo " df -h" >> /var/www/$hubzilladaily -echo " echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily -echo " du -h $backup_mount_point | grep mysql/hubzilla" >> /var/www/$hubzilladaily +echo " rsync -a --delete /var/lib/mysql/ /media/hubzilla_backup/mysql" >> /var/www/$hubzilladaily +echo " rsync -a --delete /var/www/ /media/hubzilla_backup/www" >> /var/www/$hubzilladaily +echo " rsync -a --delete /etc/letsencrypt/ /media/hubzilla_backup/letsencrypt" >> /var/www/$hubzilladaily +echo " echo \"\$(date) - disk sizes...\"" >> /var/www/$hubzilladaily +echo " df -h" >> /var/www/$hubzilladaily +echo " echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily +echo " du -h $backup_mount_point | grep mysql/hubzilla" >> /var/www/$hubzilladaily echo " echo \"unmounting backup device...\"" >> /var/www/$hubzilladaily echo " umount $backup_mount_point" >> /var/www/$hubzilladaily echo " else" >> /var/www/$hubzilladaily @@ -722,18 +600,16 @@ echo "echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily echo "du -h /var/lib/mysql/ | grep mysql/hubzilla" >> /var/www/$hubzilladaily echo "#" >> /var/www/$hubzilladaily echo "# update" >> /var/www/$hubzilladaily -echo "echo \"\$(date) - updating dehydrated...\"" >> /var/www/$hubzilladaily -echo "git -C /var/www/letsencrypt/ pull" >> /var/www/$hubzilladaily -echo "echo \"\$(date) - updating hubhilla core...\"" >> /var/www/$hubzilladaily +echo "echo \"\$(date) - updating core and addons...\"" >> /var/www/$hubzilladaily echo "(cd /var/www/html/ ; util/udall)" >> /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 "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 "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 and update finished. Rebooting...\"" >> /var/www/$hubzilladaily echo "#" >> /var/www/$hubzilladaily -echo "reboot" >> /var/www/$hubzilladaily +echo "shutdown -r now" >> /var/www/$hubzilladaily if [ -z "`grep 'hubzilla-daily.sh' /etc/crontab`" ] then @@ -745,38 +621,6 @@ echo "reboot" >> /var/www/$hubzilladaily print_info "configured cron for updates/upgrades" } -function write_uninstall_script { - print_info "writing uninstall script..." - - cat > /var/www/hubzilla-remove.sh <<END -#!/bin/sh -# -# This script removes Hubzilla. -# You might do this for a fresh start using the script. -# The script will remove (almost everything) what was installed by the script, -# all applications including hubzilla and its database. -# -# Backup the certificates of letsencrypt (you never know) -cp -a /var/www/letsencrypt/ ~/backup_le_certificats -# -# Removal -apt-get remove apache2 apache2-utils libapache2-mod-php5 php5 php-pear php5-xcache php5-curl php5-mcrypt php5-gd php5-mysql mysql-server mysql-client phpmyadmin -apt-get purge apache2 apache2-utils libapache2-mod-php5 php5 php-pear php5-xcache php5-curl php5-mcrypt php5-gd php5-mysql mysql-server mysql-client phpmyadmin -apt-get autoremove -apt-get clean -rm /etc/rsnapshot_hubzilla.conf -rm /etc/rsnapshot_hubzilla_external_device.conf -rm -R /etc/apache2/ -rm -R /var/lib/mysql/ -rm -R /var/www -rm -R /etc/selfhost/ -# uncomment the next line if you want to remove the backups -# rm -R /var/cache/rsnapshot -nano /etc/crontab # remove entries there manually -END - chmod -x /var/www/hubzilla-remove.sh -} - ######################################################################## # START OF PROGRAM ######################################################################## @@ -792,11 +636,7 @@ selfhostdir=/etc/selfhost selfhostscript=selfhost-updater.sh hubzilladaily=hubzilla-daily.sh plugins_update=.homeinstall/plugins_update.sh -snapshotconfig=/etc/rsnapshot_hubzilla.conf -snapshotconfig_external_device=/etc/rsnapshot_hubzilla_external_device.conf backup_mount_point=/media/hubzilla_backup -le_dir=/var/www/letsencrypt -sslconf=/etc/apache2/sites-available/default-ssl.conf #set -x # activate debugging from here @@ -820,7 +660,6 @@ configure_cron_selfhost if [ "$le_domain" != "localhost" ] then install_letsencrypt - configure_apache_for_https check_https else print_info "is localhost - skipped installation of letsencrypt and configuration of apache for https" @@ -828,20 +667,12 @@ fi install_hubzilla -if [ "$le_domain" != "localhost" ] -then - rewrite_to_https - install_rsnapshot -else - print_info "is localhost - skipped rewrite to https and installation of rsnapshot" -fi - configure_cron_daily if [ "$le_domain" != "localhost" ] then + install_rsync install_cryptosetup - write_uninstall_script else print_info "is localhost - skipped installation of cryptosetup" fi diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php index 5dd233d28..8985e257a 100644 --- a/Zotlabs/Module/Cdav.php +++ b/Zotlabs/Module/Cdav.php @@ -906,6 +906,19 @@ class Cdav extends Controller { $sources = ''; + if(get_pconfig(local_channel(), 'cdav_calendar', 'channel_calendar')) { + $sources .= '{ + id: \'channel_calendar\', + url: \'/channel_calendar/json/\', + color: \'#3a87ad\' + }, '; + } + + $channel_calendars[] = [ + 'displayname' => $channel['channel_name'], + 'id' => 'channel_calendar' + ]; + foreach($calendars as $calendar) { $editable = (($calendar['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript $color = (($calendar['{http://apple.com/ns/ical/}calendar-color']) ? $calendar['{http://apple.com/ns/ical/}calendar-color'] : '#3a87ad'); @@ -939,6 +952,19 @@ class Cdav extends Controller { $description = ['description', t('Description')]; $location = ['location', t('Location')]; + $catsenabled = feature_enabled(local_channel(), 'categories'); + + require_once('include/acl_selectors.php'); + + $accesslist = new \Zotlabs\Access\AccessList($channel); + $perm_defaults = $accesslist->get(); + + //$acl = (($orig_event['event_xchan']) ? '' : populate_acl(((x($orig_event)) ? $orig_event : $perm_defaults), false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'))); + $acl = populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream')); + + //$permissions = ((x($orig_event)) ? $orig_event : $perm_defaults); + $permissions = $perm_defaults; + $o .= replace_macros(get_markup_template('cdav_calendar.tpl'), [ '$sources' => $sources, '$color' => $color, @@ -955,6 +981,7 @@ class Cdav extends Controller { '$list_week' => t('List week'), '$list_day' => t('List day'), '$title' => $title, + '$channel_calendars' => $channel_calendars, '$writable_calendars' => $writable_calendars, '$dtstart' => $dtstart, '$dtend' => $dtend, @@ -962,11 +989,23 @@ class Cdav extends Controller { '$location' => $location, '$more' => t('More'), '$less' => t('Less'), + '$update' => t('Update'), '$calendar_select_label' => t('Select calendar'), + '$calendar_optiopns_label' => [t('Channel Calendars'), t('CalDAV Calendars')], '$delete' => t('Delete'), '$delete_all' => t('Delete all'), '$cancel' => t('Cancel'), - '$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.') + '$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.'), + + '$channel_hash' => $channel['channel_hash'], + '$acl' => $acl, + '$lockstate' => (($accesslist->is_private()) ? 'lock' : 'unlock'), + '$allow_cid' => acl2json($permissions['allow_cid']), + '$allow_gid' => acl2json($permissions['allow_gid']), + '$deny_cid' => acl2json($permissions['deny_cid']), + '$deny_gid' => acl2json($permissions['deny_gid']), + '$catsenabled' => $catsenabled, + '$categories_label' => t('Categories') ]); return $o; @@ -1053,7 +1092,7 @@ class Cdav extends Controller { } //enable/disable calendars - if(argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch' && intval(argv(3)) && (argv(4) == 1 || argv(4) == 0)) { + if(argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch' && argv(3) && (argv(4) == 1 || argv(4) == 0)) { $id = argv(3); if(! cdav_perms($id,$calendars)) diff --git a/Zotlabs/Module/Channel_calendar.php b/Zotlabs/Module/Channel_calendar.php new file mode 100644 index 000000000..d47d5ad49 --- /dev/null +++ b/Zotlabs/Module/Channel_calendar.php @@ -0,0 +1,620 @@ +<?php +namespace Zotlabs\Module; + +require_once('include/conversation.php'); +require_once('include/bbcode.php'); +require_once('include/datetime.php'); +require_once('include/event.php'); +require_once('include/items.php'); +require_once('include/html2plain.php'); + +class Channel_calendar extends \Zotlabs\Web\Controller { + + function post() { + + logger('post: ' . print_r($_REQUEST,true), LOGGER_DATA); + + if(! local_channel()) + return; + + if(($_FILES) && array_key_exists('userfile',$_FILES) && intval($_FILES['userfile']['size'])) { + $src = $_FILES['userfile']['tmp_name']; + if($src) { + $result = parse_ical_file($src,local_channel()); + if($result) + info( t('Calendar entries imported.') . EOL); + else + notice( t('No calendar entries found.') . EOL); + @unlink($src); + } + goaway(z_root() . '/channel_calendar'); + } + + + $event_id = ((x($_POST,'event_id')) ? intval($_POST['event_id']) : 0); + $event_hash = ((x($_POST,'event_hash')) ? $_POST['event_hash'] : ''); + + $xchan = ((x($_POST,'xchan')) ? dbesc($_POST['xchan']) : ''); + $uid = local_channel(); + + $start_text = escape_tags($_REQUEST['dtstart']); + $finish_text = escape_tags($_REQUEST['dtend']); + + $adjust = intval($_POST['adjust']); + $nofinish = intval($_POST['nofinish']); + + $timezone = ((x($_POST,'timezone_select')) ? notags(trim($_POST['timezone_select'])) : ''); + + $tz = (($timezone) ? $timezone : date_default_timezone_get()); + + $categories = escape_tags(trim($_POST['categories'])); + + // only allow editing your own events. + + if(($xchan) && ($xchan !== get_observer_hash())) + return; + + if($start_text) { + $start = $start_text; + } + else { + $start = sprintf('%d-%d-%d %d:%d:0',$startyear,$startmonth,$startday,$starthour,$startminute); + } + + if($finish_text) { + $finish = $finish_text; + } + else { + $finish = sprintf('%d-%d-%d %d:%d:0',$finishyear,$finishmonth,$finishday,$finishhour,$finishminute); + } + + if($nofinish) { + $finish = NULL_DATE; + } + + + + if($adjust) { + $start = datetime_convert($tz,'UTC',$start); + if(! $nofinish) + $finish = datetime_convert($tz,'UTC',$finish); + } + else { + $start = datetime_convert('UTC','UTC',$start); + if(! $nofinish) + $finish = datetime_convert('UTC','UTC',$finish); + } + + + + // Don't allow the event to finish before it begins. + // It won't hurt anything, but somebody will file a bug report + // and we'll waste a bunch of time responding to it. Time that + // could've been spent doing something else. + + + $summary = escape_tags(trim($_POST['summary'])); + $desc = escape_tags(trim($_POST['desc'])); + $location = escape_tags(trim($_POST['location'])); + $type = escape_tags(trim($_POST['type'])); + + require_once('include/text.php'); + linkify_tags($desc, local_channel()); + linkify_tags($location, local_channel()); + + //$action = ($event_hash == '') ? 'new' : "event/" . $event_hash; + + //fixme: this url gives a wsod if there is a linebreak detected in one of the variables ($desc or $location) + //$onerror_url = z_root() . "/events/" . $action . "?summary=$summary&description=$desc&location=$location&start=$start_text&finish=$finish_text&adjust=$adjust&nofinish=$nofinish&type=$type"; + //$onerror_url = z_root() . "/events"; + + if(strcmp($finish,$start) < 0 && !$nofinish) { + notice( t('Event can not end before it has started.') . EOL); + if(intval($_REQUEST['preview'])) { + echo( t('Unable to generate preview.')); + killme(); + } + //goaway($onerror_url); + } + + if((! $summary) || (! $start)) { + notice( t('Event title and start time are required.') . EOL); + if(intval($_REQUEST['preview'])) { + echo( t('Unable to generate preview.')); + killme(); + } + //goaway($onerror_url); + } + + // $share = ((intval($_POST['distr'])) ? intval($_POST['distr']) : 0); + + $share = 1; + + $channel = \App::get_channel(); + + $acl = new \Zotlabs\Access\AccessList(false); + + if($event_id) { + $x = q("select * from event where id = %d and uid = %d limit 1", + intval($event_id), + intval(local_channel()) + ); + if(! $x) { + notice( t('Event not found.') . EOL); + if(intval($_REQUEST['preview'])) { + echo( t('Unable to generate preview.')); + killme(); + } + return; + } + + $acl->set($x[0]); + + $created = $x[0]['created']; + $edited = datetime_convert(); + + if($x[0]['allow_cid'] === '<' . $channel['channel_hash'] . '>' + && $x[0]['allow_gid'] === '' && $x[0]['deny_cid'] === '' && $x[0]['deny_gid'] === '') { + $share = false; + } + else { + $share = true; + } + } + else { + $created = $edited = datetime_convert(); + if($share) { + $acl->set_from_array($_POST); + } + else { + $acl->set(array('allow_cid' => '<' . $channel['channel_hash'] . '>', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '')); + } + } + + $post_tags = array(); + $channel = \App::get_channel(); + $ac = $acl->get(); + + if(strlen($categories)) { + $cats = explode(',',$categories); + foreach($cats as $cat) { + $post_tags[] = array( + 'uid' => $profile_uid, + 'ttype' => TERM_CATEGORY, + 'otype' => TERM_OBJ_POST, + 'term' => trim($cat), + 'url' => $channel['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)) + ); + } + } + + $datarray = array(); + $datarray['dtstart'] = $start; + $datarray['dtend'] = $finish; + $datarray['summary'] = $summary; + $datarray['description'] = $desc; + $datarray['location'] = $location; + $datarray['etype'] = $type; + $datarray['adjust'] = $adjust; + $datarray['nofinish'] = $nofinish; + $datarray['uid'] = local_channel(); + $datarray['account'] = get_account_id(); + $datarray['event_xchan'] = $channel['channel_hash']; + $datarray['allow_cid'] = $ac['allow_cid']; + $datarray['allow_gid'] = $ac['allow_gid']; + $datarray['deny_cid'] = $ac['deny_cid']; + $datarray['deny_gid'] = $ac['deny_gid']; + $datarray['private'] = (($acl->is_private()) ? 1 : 0); + $datarray['id'] = $event_id; + $datarray['created'] = $created; + $datarray['edited'] = $edited; + + if(intval($_REQUEST['preview'])) { + $html = format_event_html($datarray); + echo $html; + killme(); + } + + $event = event_store_event($datarray); + + if($post_tags) + $datarray['term'] = $post_tags; + + $item_id = event_store_item($datarray,$event); + + if($item_id) { + $r = q("select * from item where id = %d", + intval($item_id) + ); + if($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + $z = q("select * from event where event_hash = '%s' and uid = %d limit 1", + dbesc($r[0]['resource_id']), + intval($channel['channel_id']) + ); + if($z) { + build_sync_packet($channel['channel_id'],array('event_item' => array(encode_item($sync_item[0],true)),'event' => $z)); + } + } + } + + if($share) + \Zotlabs\Daemon\Master::Summon(array('Notifier','event',$item_id)); + + killme(); + + } + + + + function get() { + + if(argc() > 2 && argv(1) == 'ical') { + $event_id = argv(2); + + require_once('include/security.php'); + $sql_extra = permissions_sql(local_channel()); + + $r = q("select * from event where event_hash = '%s' $sql_extra limit 1", + dbesc($event_id) + ); + if($r) { + header('Content-type: text/calendar'); + header('content-disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"' ); + echo ical_wrapper($r); + killme(); + } + else { + notice( t('Event not found.') . EOL ); + return; + } + } + + if(! local_channel()) { + notice( t('Permission denied.') . EOL); + return; + } + + if((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) { + $r = q("update event set dismissed = 1 where id = %d and uid = %d", + intval(argv(2)), + intval(local_channel()) + ); + } + + if((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) { + $r = q("update event set dismissed = 0 where id = %d and uid = %d", + intval(argv(2)), + intval(local_channel()) + ); + } + + $channel = \App::get_channel(); + + $mode = 'view'; + $y = 0; + $m = 0; + $ignored = ((x($_REQUEST,'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : ''); + + if(argc() > 1) { + if(argc() > 2 && argv(1) === 'add') { + $mode = 'add'; + $item_id = intval(argv(2)); + } + if(argc() > 2 && argv(1) === 'drop') { + $mode = 'drop'; + $event_id = argv(2); + } + if(argc() > 2 && intval(argv(1)) && intval(argv(2))) { + $mode = 'view'; + $y = intval(argv(1)); + $m = intval(argv(2)); + } + if(argc() <= 2) { + $mode = 'view'; + $event_id = argv(1); + } + } + + if($mode === 'add') { + event_addtocal($item_id,local_channel()); + killme(); + } + + if($mode == 'view') { + + /* edit/create form */ + if($event_id) { + $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($event_id), + intval(local_channel()) + ); + if(count($r)) + $orig_event = $r[0]; + } + + $channel = \App::get_channel(); + + // Passed parameters overrides anything found in the DB + if(!x($orig_event)) + $orig_event = array(); + + $n_checked = ((x($orig_event) && $orig_event['nofinish']) ? ' checked="checked" ' : ''); + $a_checked = ((x($orig_event) && $orig_event['adjust']) ? ' checked="checked" ' : ''); + $t_orig = ((x($orig_event)) ? $orig_event['summary'] : ''); + $d_orig = ((x($orig_event)) ? $orig_event['description'] : ''); + $l_orig = ((x($orig_event)) ? $orig_event['location'] : ''); + $eid = ((x($orig_event)) ? $orig_event['id'] : 0); + $event_xchan = ((x($orig_event)) ? $orig_event['event_xchan'] : $channel['channel_hash']); + $mid = ((x($orig_event)) ? $orig_event['mid'] : ''); + + $sdt = ((x($orig_event)) ? $orig_event['dtstart'] : 'now'); + + $fdt = ((x($orig_event)) ? $orig_event['dtend'] : '+1 hour'); + + $tz = date_default_timezone_get(); + if(x($orig_event)) + $tz = (($orig_event['adjust']) ? date_default_timezone_get() : 'UTC'); + + $syear = datetime_convert('UTC', $tz, $sdt, 'Y'); + $smonth = datetime_convert('UTC', $tz, $sdt, 'm'); + $sday = datetime_convert('UTC', $tz, $sdt, 'd'); + $shour = datetime_convert('UTC', $tz, $sdt, 'H'); + $sminute = datetime_convert('UTC', $tz, $sdt, 'i'); + + $stext = datetime_convert('UTC',$tz,$sdt); + $stext = substr($stext,0,14) . "00:00"; + + $fyear = datetime_convert('UTC', $tz, $fdt, 'Y'); + $fmonth = datetime_convert('UTC', $tz, $fdt, 'm'); + $fday = datetime_convert('UTC', $tz, $fdt, 'd'); + $fhour = datetime_convert('UTC', $tz, $fdt, 'H'); + $fminute = datetime_convert('UTC', $tz, $fdt, 'i'); + + $ftext = datetime_convert('UTC',$tz,$fdt); + $ftext = substr($ftext,0,14) . "00:00"; + + $type = ((x($orig_event)) ? $orig_event['etype'] : 'event'); + + $f = get_config('system','event_input_format'); + if(! $f) + $f = 'ymd'; + + $thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); + $thismonth = datetime_convert('UTC',date_default_timezone_get(),'now','m'); + if(! $y) + $y = intval($thisyear); + if(! $m) + $m = intval($thismonth); + + $export = false; + if(argc() === 4 && argv(3) === 'export') + $export = true; + + // Put some limits on dates. The PHP date functions don't seem to do so well before 1900. + // An upper limit was chosen to keep search engines from exploring links millions of years in the future. + + if($y < 1901) + $y = 1900; + if($y > 2099) + $y = 2100; + + $nextyear = $y; + $nextmonth = $m + 1; + if($nextmonth > 12) { + $nextmonth = 1; + $nextyear ++; + } + + $prevyear = $y; + if($m > 1) + $prevmonth = $m - 1; + else { + $prevmonth = 12; + $prevyear --; + } + + $dim = get_dim($y,$m); + $start = sprintf('%d-%d-%d %d:%d:%d',$y,$m,1,0,0,0); + $finish = sprintf('%d-%d-%d %d:%d:%d',$y,$m,$dim,23,59,59); + + + if (argv(1) === 'json'){ + if (x($_GET,'start')) $start = $_GET['start']; + if (x($_GET,'end')) $finish = $_GET['end']; + } + + $start = datetime_convert('UTC','UTC',$start); + $finish = datetime_convert('UTC','UTC',$finish); + + $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start); + $adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish); + + if (x($_GET,'id')){ + $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan + from event left join item on resource_id = event_hash where resource_type = 'event' and event.uid = %d and event.id = %d limit 1", + intval(local_channel()), + intval($_GET['id']) + ); + } elseif($export) { + $r = q("SELECT * from event where uid = %d + AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' ) + OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) ", + intval(local_channel()), + dbesc($start), + dbesc($finish), + dbesc($adjust_start), + dbesc($adjust_finish) + ); + } + else { + // fixed an issue with "nofinish" events not showing up in the calendar. + // There's still an issue if the finish date crosses the end of month. + // Noting this for now - it will need to be fixed here and in Friendica. + // Ultimately the finish date shouldn't be involved in the query. + + $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id + from event left join item on event_hash = resource_id + where resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored + AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' ) + OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) ", + intval(local_channel()), + dbesc($start), + dbesc($finish), + dbesc($adjust_start), + dbesc($adjust_finish) + ); + + + } + + $links = array(); + + if($r && ! $export) { + xchan_query($r); + $r = fetch_post_tags($r,true); + + $r = sort_by_date($r); + } + + if($r) { + foreach($r as $rr) { + $j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j')); + if(! x($links,$j)) + $links[$j] = z_root() . '/' . \App::$cmd . '#link-' . $j; + } + } + + $events=array(); + + $last_date = ''; + $fmt = t('l, F j'); + + if($r) { + + foreach($r as $rr) { + $j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j')); + $d = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], $fmt) : datetime_convert('UTC','UTC',$rr['dtstart'],$fmt)); + $d = day_translate($d); + + $start = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'c') : datetime_convert('UTC','UTC',$rr['dtstart'],'c')); + if ($rr['nofinish']){ + $end = null; + } else { + $end = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtend'], 'c') : datetime_convert('UTC','UTC',$rr['dtend'],'c')); + + // give a fake end to birthdays so they get crammed into a + // single day on the calendar + + if($rr['etype'] === 'birthday') + $end = null; + } + + $catsenabled = feature_enabled(local_channel(),'categories'); + $categories = ''; + if($catsenabled){ + if($rr['term']) { + $cats = get_terms_oftype($rr['term'], TERM_CATEGORY); + foreach ($cats as $cat) { + if(strlen($categories)) + $categories .= ', '; + $categories .= $cat['term']; + } + } + } + + $is_first = ($d !== $last_date); + + $last_date = $d; + + $edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root().'/events/'.$rr['event_hash'].'?expandform=1',t('Edit event'),'','') : false); + + $drop = array(z_root().'/events/drop/'.$rr['event_hash'],t('Delete event'),'',''); + + $title = strip_tags(html_entity_decode(zidify_links(bbcode($rr['summary'])),ENT_QUOTES,'UTF-8')); + if(! $title) { + list($title, $_trash) = explode("<br",bbcode($rr['desc']),2); + $title = strip_tags(html_entity_decode($title,ENT_QUOTES,'UTF-8')); + } + $html = format_event_html($rr); + $rr['desc'] = zidify_links(smilies(bbcode($rr['desc']))); + $rr['description'] = htmlentities(html2plain(bbcode($rr['description'])),ENT_COMPAT,'UTF-8',false); + $rr['location'] = zidify_links(smilies(bbcode($rr['location']))); + $events[] = array( + 'calendar_id' => 'channel_calendar', + 'rw' => true, + + 'id'=>$rr['id'], + 'uri' => $rr['event_hash'], + 'start'=> $start, + 'end' => $end, + 'drop' => $drop, + 'allDay' => false, + 'title' => $title, + + 'j' => $j, + 'd' => $d, + 'is_editable' => $edit ? true : false, + + 'is_first'=>$is_first, + 'item'=>$rr, + 'html'=>$html, + 'plink' => array($rr['plink'],t('Link to Source'),'',''), + + 'description' => $rr['description'], + 'location' => $rr['location'], + + 'allow_cid' => expand_acl($rr['allow_cid']), + 'allow_gid' => expand_acl($rr['allow_gid']), + 'deny_cid' => expand_acl($rr['deny_cid']), + 'deny_gid' => expand_acl($rr['deny_gid']), + + 'categories' => $categories + ); + } + } + + if($export) { + header('Content-type: text/calendar'); + header('content-disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"' ); + echo ical_wrapper($r); + killme(); + } + + if (\App::$argv[1] === 'json'){ + json_return_and_die($events); + } + } + + + if($mode === 'drop' && $event_id) { + $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($event_id), + intval(local_channel()) + ); + + $sync_event = $r[0]; + + if($r) { + $r = q("delete from event where event_hash = '%s' and uid = %d", + dbesc($event_id), + intval(local_channel()) + ); + if($r) { + $r = q("update item set resource_type = '', resource_id = '' where resource_type = 'event' and resource_id = '%s' and uid = %d", + dbesc($event_id), + intval(local_channel()) + ); + $sync_event['event_deleted'] = 1; + build_sync_packet(0,array('event' => array($sync_event))); + killme(); + } + notice( t('Failed to remove event' ) . EOL); + killme(); + } + } + + } + +} diff --git a/Zotlabs/Module/Editpost.php b/Zotlabs/Module/Editpost.php index 1c9068e07..85882bf0a 100644 --- a/Zotlabs/Module/Editpost.php +++ b/Zotlabs/Module/Editpost.php @@ -45,7 +45,8 @@ class Editpost extends \Zotlabs\Web\Controller { } if($itm[0]['resource_type'] === 'event' && $itm[0]['resource_id']) { - goaway(z_root() . '/events/' . $itm[0]['resource_id'] . '?expandform=1'); + goaway(z_root() . '/cdav/calendar'); + //goaway(z_root() . '/events/' . $itm[0]['resource_id'] . '?expandform=1'); } $owner_uid = $itm[0]['uid']; diff --git a/Zotlabs/Module/Photo.php b/Zotlabs/Module/Photo.php index 37ae03460..30670c329 100644 --- a/Zotlabs/Module/Photo.php +++ b/Zotlabs/Module/Photo.php @@ -40,7 +40,7 @@ class Photo extends \Zotlabs\Web\Controller { call_hooks('cache_mode_hook', $cache_mode); $observer_xchan = get_observer_hash(); - $ismodified = $_SERVER['HTTP_IF_MODIFIED_SINCE']; + $cachecontrol = ''; if(isset($type)) { @@ -68,8 +68,6 @@ class Photo extends \Zotlabs\Web\Controller { } } - $modified = filemtime($default); - $default = z_root() . '/' . $default; $uid = $person; $data = ''; @@ -97,13 +95,17 @@ class Photo extends \Zotlabs\Web\Controller { $default = $d['default']; $data = $d['data']; $mimetype = $d['mimetype']; + $modified = 0; } if(! $data) { - $x = z_fetch_url($default,true,0,[ 'novalidate' => true ]); + $x = z_fetch_url(z_root() . '/' . $default, true, 0, [ 'novalidate' => true ]); $data = ($x['success'] ? $x['body'] : EMPTY_STR); $mimetype = 'image/png'; + $modified = filemtime($default); } + + $cachecontrol = ', must-revalidate'; } else { @@ -160,18 +162,20 @@ class Photo extends \Zotlabs\Web\Controller { $allowed = (-1); if($u === PHOTO_CACHE) { // Validate cache - $cache = array( - 'resid' => $photo, - 'status' => false - ); - if($cache_mode['on']) + if($cache_mode['on']) { + $cache = array( + 'resid' => $photo, + 'status' => false + ); call_hooks('cache_url_hook', $cache); - if(! $cache['status']) { - $url = htmlspecialchars_decode($r[0]['display_path']); - if(strpos(z_root(),'https:') !== false && strpos($url,'https:') === false) - $url = z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($url); - header("Location: " . $url); - killme(); + if(! $cache['status']) { + $url = htmlspecialchars_decode($r[0]['display_path']); + // SSLify if needed + if(strpos(z_root(),'https:') !== false && strpos($url,'https:') === false) + $url = z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($url); + header("Location: " . $url); + killme(); + } } } } @@ -216,38 +220,23 @@ class Photo extends \Zotlabs\Web\Controller { http_status_exit(404,'not found'); } + if(! $data) + killme(); + + $etag = md5($data . $modified); + + if($modified == 0) + $modified = time(); + header_remove('Pragma'); - if($ismodified === gmdate("D, d M Y H:i:s", $modified) . " GMT") { + if($_SERVER['HTTP_IF_NONE_MATCH'] === $etag || $_SERVER['HTTP_IF_MODIFIED_SINCE'] === gmdate("D, d M Y H:i:s", $modified) . " GMT") { header_remove('Expires'); header_remove('Cache-Control'); header_remove('Set-Cookie'); http_status_exit(304,'not modified'); - } - - if(! isset($data)) { - if(isset($resolution)) { - switch($resolution) { - case 4: - $default = get_default_profile_photo(); - break; - case 5: - $default = get_default_profile_photo(80); - break; - case 6: - $default = get_default_profile_photo(48); - break; - default: - killme(); - // NOTREACHED - break; - } - $x = z_fetch_url(z_root() . '/' . $default,true,0,[ 'novalidate' => true ]); - $data = ($x['success'] ? $x['body'] : EMPTY_STR); - $mimetype = 'image/png'; - } } - + if(isset($res) && intval($res) && $res < 500) { $ph = photo_factory($data, $mimetype); if($ph->is_valid()) { @@ -284,12 +273,13 @@ class Photo extends \Zotlabs\Web\Controller { $maxage = $expires - time(); header("Expires: " . gmdate("D, d M Y H:i:s", $expires) . " GMT"); - header("Cache-Control: max-age=" . $maxage); + header("Cache-Control: max-age=" . $maxage . $cachecontrol); } header("Content-type: " . $mimetype); header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modified) . " GMT"); + header("ETag: " . $etag); header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data))); // If it's a file resource, stream it. diff --git a/Zotlabs/Module/Ping.php b/Zotlabs/Module/Ping.php index a367b1062..f0c3a8821 100644 --- a/Zotlabs/Module/Ping.php +++ b/Zotlabs/Module/Ping.php @@ -447,7 +447,7 @@ class Ping extends \Zotlabs\Web\Controller { $when = day_translate(datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart'], $bd_format)) . (($today) ? ' ' . t('[today]') : ''); $result[] = array( - 'notify_link' => z_root() . '/events', /// @FIXME this takes you to an edit page and it may not be yours, we really want to just view the single event --> '/events/event/' . $rr['event_hash'], + 'notify_link' => z_root() . '/cdav/calendar', /// @FIXME this takes you to an edit page and it may not be yours, we really want to just view the single event --> '/events/event/' . $rr['event_hash'], 'name' => $rr['xchan_name'], 'addr' => $rr['xchan_addr'], 'url' => $rr['xchan_url'], diff --git a/Zotlabs/Module/Profile_photo.php b/Zotlabs/Module/Profile_photo.php index 9f1928e52..a812ca210 100644 --- a/Zotlabs/Module/Profile_photo.php +++ b/Zotlabs/Module/Profile_photo.php @@ -52,14 +52,39 @@ class Profile_photo extends \Zotlabs\Web\Controller { return; } + $channel = \App::get_channel(); + check_form_security_token_redirectOnErr('/profile_photo', 'profile_photo'); + + // Remove cover photo + if(isset($_POST['remove'])) { + + $r = q("SELECT resource_id FROM photo WHERE photo_usage = %d AND uid = %d LIMIT 1", + intval(PHOTO_PROFILE), + intval(local_channel()) + ); + + if($r) { + q("update photo set photo_usage = %d where photo_usage = %d and uid = %d", + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + intval(local_channel()) + ); + + $sync = attach_export_data($channel,$r[0]['resource_id']); + if($sync) + build_sync_packet($channel['channel_id'],array('file' => array($sync))); + } + + $_SESSION['reload_avatar'] = true; + + goaway(z_root() . '/profiles'); + } if((array_key_exists('cropfinal',$_POST)) && (intval($_POST['cropfinal']) == 1)) { // logger('crop: ' . print_r($_POST,true)); - - // phase 2 - we have finished cropping if(argc() != 2) { @@ -161,8 +186,6 @@ class Profile_photo extends \Zotlabs\Web\Controller { return; } - $channel = \App::get_channel(); - // If setting for the default profile, unset the profile photo flag from any other photos I own if($is_default_profile) { @@ -390,8 +413,10 @@ class Profile_photo extends \Zotlabs\Web\Controller { if($sync) build_sync_packet($channel['channel_id'],array('file' => array($sync))); + $_SESSION['reload_avatar'] = true; \Zotlabs\Daemon\Master::Summon(array('Directory',local_channel())); + goaway(z_root() . '/profiles'); } @@ -471,6 +496,7 @@ class Profile_photo extends \Zotlabs\Web\Controller { '$lbl_profiles' => t('Select a profile:'), '$title' => (($importing) ? t('Use Photo for Profile') : t('Change Profile Photo')), '$submit' => (($importing) ? t('Use') : t('Upload')), + '$remove' => t('Remove'), '$profiles' => $profiles, '$single' => ((count($profiles) == 1) ? true : false), '$profile0' => $profiles[0], diff --git a/Zotlabs/Widget/Cdav.php b/Zotlabs/Widget/Cdav.php index 589f915c5..c88530c0b 100644 --- a/Zotlabs/Widget/Cdav.php +++ b/Zotlabs/Widget/Cdav.php @@ -113,10 +113,22 @@ class Cdav { } } + $channel_calendars[] = [ + 'ownernick' => $channel['channel_address'], + 'displayname' => $channel['channel_name'], + 'calendarid' => 'channel_calendar', + 'json_source' => '/channel_calendar/json', + 'color' => '#3a87ad', + 'editable' => true, + 'switch' => get_pconfig(local_channel(), 'cdav_calendar', 'channel_calendar') + ]; + $o .= replace_macros(get_markup_template('cdav_widget_calendar.tpl'), [ - '$my_calendars_label' => t('My Calendars'), + '$channel_calendars_label' => t('Channel Calendar'), + '$channel_calendars' => $channel_calendars, + '$my_calendars_label' => t('CalDAV Calendars'), '$my_calendars' => $my_calendars, - '$shared_calendars_label' => t('Shared Calendars'), + '$shared_calendars_label' => t('Shared CalDAV Calendars'), '$shared_calendars' => $shared_calendars, '$sharee_options' => $sharee_options, '$access_options' => $access_options, @@ -127,7 +139,7 @@ class Cdav { '$create_label' => t('Create new calendar'), '$create' => t('Create'), '$create_placeholder' => t('Calendar Name'), - '$tools_label' => t('Calendar Tools'), + '$tools_label' => t('CalDAV Calendar Tools'), '$import_label' => t('Import calendar'), '$import_placeholder' => t('Select a calendar to import to'), '$upload' => t('Upload'), diff --git a/doc/admin/administrator_guide.md b/doc/admin/administrator_guide.md index 2b1c84180..c884bb0bd 100644 --- a/doc/admin/administrator_guide.md +++ b/doc/admin/administrator_guide.md @@ -1,4 +1,3 @@ - ### Overview $Projectname is more than a simple web application. It is a @@ -17,48 +16,47 @@ such as XAMPP and WAMP are not officially supported at this time however we welcome patches if you manage to get it working. ### Where to find more help -If you encounter problems or have issues not addressed in this documentation, +If you encounter problems or have issues not addressed in this documentation, please let us know via the [Github issue tracker](https://framagit.org/hubzilla/core/issues). Please be as clear as you can about your operating environment and provide as much detail as possible about any error messages you may see, so that we can prevent it from happening in the future. Due to the large variety of operating systems and PHP platforms in existence we may have only limited ability to debug your PHP installation or -acquire any missing modules * but we will do our best to solve any general code +acquire any missing modules, but we will do our best to solve any general code issues. -### Before you begin +### Before you begin #### Choose a domain name or subdomain name for your server -$Projectname can only be installed into the root of a domain or sub-domain, and can +$Projectname can only be installed into the root of a domain or sub-domain, and can not be installed using alternate TCP ports. #### Decide if you will use SSL and obtain an SSL certificate before software installation -You SHOULD use SSL. If you use SSL, you MUST use a "browser-valid" certificate. +You SHOULD use SSL. If you use SSL, you MUST use a "browser-valid" certificate. *You MUST NOT use self-signed certificates!* -Please test your certificate prior to installation. A web tool for testing your -certificate is available at "http://www.digicert.com/help/". When visiting your -site for the first time, please use the SSL ("https://") URL if SSL is available. -This will avoid problems later. The installation routine will not allow you to +Please test your certificate prior to installation. A web tool for testing your +certificate is available at "http://www.digicert.com/help/". When visiting your +site for the first time, please use the SSL ("https://") URL if SSL is available. +This will avoid problems later. The installation routine will not allow you to use a non browser-valid certificate. - -This restriction is incorporated because public posts from you may contain +This restriction is incorporated because public posts from you may contain references to images on your own hub. Other members viewing their stream on other hubs will get warnings if your certificate is not trusted by their web browser. This will confuse many people because this is a decentralised network -and they will get the warning about your hub while viewing their own hub and may -think their own hub has an issue. These warnings are very technical and scary to +and they will get the warning about your hub while viewing their own hub and may +think their own hub has an issue. These warnings are very technical and scary to some folks, many of whom will not know how to proceed except to follow the browser advice. This is disruptive to the community. That said, we recognise the issues surrounding the current certificate infrastructure and agree there are many -problems, but that doesn't change the requirement. +problems, but that doesn't change the requirement. Free "browser-valid" certificates are available from providers such as StartSSL -and LetsEncrypt. +and LetsEncrypt. If you do NOT use SSL, there may be a delay of up to a minute for the initial install script - while we check the SSL port to see if anything responds there. @@ -66,11 +64,11 @@ When communicating with new sites, $Projectname always attempts connection on th SSL port first, before falling back to a less secure connection. If you do not use SSL, your webserver MUST NOT listen on port 443 at all. -If you use LetsEncrypt to provide certificates and create a file under -.well-known/acme-challenge so that LetsEncrypt can verify your domain ownership, -please remove or rename the .well-known directory as soon as the certificate is +If you use LetsEncrypt to provide certificates and create a file under +.well-known/acme-challenge so that LetsEncrypt can verify your domain ownership, +please remove or rename the .well-known directory as soon as the certificate is generated. $Projectname will provide its own handler for ".well-known" services when -it is installed, and an existing directory in this location may prevent some of +it is installed, and an existing directory in this location may prevent some of these services from working correctly. This should not be a problem with Apache, but may be an issue with nginx or other web server platforms. @@ -82,20 +80,20 @@ There are several ways to deploy a new hub. * Automated deployment using an OpenShift virtual private server (VPS) ### Requirements -* Apache with mod-rewrite enabled and "AllowOverride All" so you can use a +* Apache with mod-rewrite enabled and "AllowOverride All" so you can use a local .htaccess file. Some folks have successfully used nginx and lighttpd. Example config scripts are available for these platforms in doc/install. - Apache and nginx have the most support. + Apache and nginx have the most support. -* PHP 5.5 or later. - * Note that on some shared hosting environments, the _command line_ version of -PHP might differ from the _webserver_ version +* PHP 7.1 or later. + * Note that on some shared hosting environments, the _command line_ + version of PHP might differ from the _webserver_ version -* PHP *command line* access with register_argc_argv set to true in the - php.ini file * and with no hosting provider restrictions on the use of +* PHP *command line* access with register_argc_argv set to true in the + php.ini file * and with no hosting provider restrictions on the use of exec() and proc_open(). -* curl, gd (with at least jpeg and png support), mysqli, mbstring, mcrypt, zip, +* curl, gd (with at least jpeg and png support), mysqli, mbstring, mcrypt, zip, and openssl extensions. The imagick extension is not required but desirable. * xml extension is required if you want webdav to work. @@ -106,7 +104,7 @@ PHP might differ from the _webserver_ version * ability to schedule jobs with cron. -* Installation into a top-level domain or sub-domain (without a +* Installation into a top-level domain or sub-domain (without a directory/path component in the URL) is REQUIRED. ### Manual Installation @@ -115,9 +113,9 @@ PHP might differ from the _webserver_ version If you copy the directory tree to your webserver, make sure that you include the hidden files like .htaccess. -If you are able to do so, we recommend using git to clone the source -repository rather than to use a packaged tar or zip file. This makes the -software much easier to update. The Linux command to clone the repository +If you are able to do so, we recommend using git to clone the source +repository rather than to use a packaged tar or zip file. This makes the +software much easier to update. The Linux command to clone the repository into a directory "mywebsite" would be: git clone https://framagit.org/hubzilla/core.git mywebsite @@ -126,7 +124,7 @@ and then you can pick up the latest changes at any time with: git pull -make sure folders ``store/[data]/smarty3`` and ``store`` exist and are +make sure folders ``store/[data]/smarty3`` and ``store`` exist and are writable by the webserver: mkdir -p "store/[data]/smarty3" @@ -150,7 +148,7 @@ web-based administrative tools to function: #### Official addons ##### Installation -Navigate to your website. Then you should clone the addon repository (separately). We'll give this repository a nickname of 'hzaddons'. You can pull in other hubzilla addon repositories by giving them different nicknames:: +Navigate to your website. Then you should clone the addon repository (separately). We'll give this repository a nickname of 'hzaddons'. You can pull in other hubzilla addon repositories by giving them different nicknames: cd mywebsite util/add_addon_repo https://framagit.org/hubzilla/addons.git hzaddons @@ -161,9 +159,9 @@ For keeping the addon tree updated, you should be on your top level website dire cd mywebsite util/update_addon_repo hzaddons -Create searchable representations of the online documentation. You may do this -any time that the documentation is updated : - +Create searchable representations of the online documentation. You may do this +any time that the documentation is updated : + cd mywebsite util/importdoc @@ -203,45 +201,28 @@ We recommend the following addons be installed on all public sites: Several web communities have begun to converge using common protocols. The protocols involved are somewhat limited in their abilities. The GNU-Social protocol for instance offers no privacy modes, and the Diaspora protocol is somewhat restrictive in what kinds of communications are allowed. All comments must be signed in a very unique manner by the original author. The ActivityPub protocol is also being considered and may be supported at a future date. No other existing protocol supports nomadic location as used by this project. This presents some support challenges as some features work with some networks and don't work with others. Nevertheless the federation protocols allow connections to be made to a much larger community of people worldwide. They are provided as addons. -> diaspora - The Diaspora Protocol used by Diaspora and Friendica. You should enable 'Diaspora Statistics' (statistics) first to enable all the available features. +* diaspora - The Diaspora Protocol used by Diaspora and Friendica. You should enable 'Diaspora Statistics' (statistics) first to enable all the available features. -> gnusoc - The GNU-Social Protocol, used by GNU-Social, Mastodon and several other communities. This addon requires you first install the 'pubsubhubbub' service (also an addon). +* gnusoc - The GNU-Social Protocol, used by GNU-Social, Mastodon and several other communities. This addon requires you first install the 'pubsubhubbub' service (also an addon). Each member of your site must choose whether or not to allow these protocols individually as they may conflict with several desirable core features and abilities of this software (such as channel migration and cloning). They do this from their 'Settings -> Feature/Addon Settings' page. The administrator may also set the following: util/config system.diaspora_allowed 1 util/config system.gnusoc_allowed 1 -and enable these protocols automatically for all newly created channels. - - - - - - -### Techlevels - -We've implemented several different mechanisms in order to reduce the apparent complexity and learning curve presented to new members. At the same time, we do not wish to limit any functionality for people who are able to grasp some slightly advanced technical technical features. The first mechanism was to move several features to an optional 'Features' page where they could be enabled at will; with the default interface kept somewhat lean. - -The problem we had now is that the number of features began to grow dramatically, and the Feature page is daunting in possibilities. There are also features present which probably should not be available to all members, but may be extremely useful to those with technical backgrounds. - -The techlevels seeeks to remedy this by grouping features within different levels of technical ability; starting at 0 (uncomfortable with technology), and up to 5 (Unix wizard or equivalent). - -When a new member registers, their account is provided a techlevel setting of 0. On the account settings page they may change this to any available level. A higher level opens more advanced features and possible interactions. - -The account administrator may also lock a particular level, lock a maximum level, or change/re-arrange the features available to any level. Those with the minimum level are typically not very exploratory and are unlikely to discover the advanced modes. This is by design. Those that look around and desire more interactions will find them. In the absence of administrator defaults they may choose any level. As they look at the features available to the level in question, it is generally expected that they will discover some features are beyond their comprehension and it is hoped they will back off to a level where the interface and features are comfortable to their skill level. +and enable these protocols automatically for all newly created channels. ### Service Classes Service classes allow you to set limits on system resources by limiting what individual -accounts can do, including file storage and top-level post limits. Define custom service -classes according to your needs in the `.htconfig.php` file. For example, create +accounts can do, including file storage and top-level post limits. Define custom service +classes according to your needs in the `.htconfig.php` file. For example, create a _standard_ and _premium_ class using the following lines: // Service classes - + App::$config['system']['default_service_class']='standard'; // this is the default service class that is attached to every new account - + // configuration for standard service class App::$config['service_class']['standard'] = array('photo_upload_limit'=>2097152, // total photo storage limit per channel (here 2MB) @@ -251,7 +232,7 @@ a _standard_ and _premium_ class using the following lines: 'total_channels' =>100, // number of channels the user can add, other users can still add this channel, even if the limit is reached 'attach_upload_limit' =>2097152, // total attachment storage limit per channel (here 2MB) 'chatters_inroom' =>20); - + // configuration for premium service class App::$config['service_class']['premium'] = array('photo_upload_limit'=>20000000000, // total photo storage limit per channel (here 20GB) @@ -262,7 +243,7 @@ a _standard_ and _premium_ class using the following lines: 'attach_upload_limit' =>20000000000, // total attachment storage limit per channel (here 20GB) 'chatters_inroom' =>100); -To apply a service class to an existing account, use the command line utility from the +To apply a service class to an existing account, use the command line utility from the web root: `util/service_class` @@ -298,11 +279,11 @@ set the account that owns channel 'blogchan' to service class 'firstclass' (with * access_tokens - maximum number of Guest Access Tokens per channel ### Theme management -#### Repo management example +#### Repo management example 1. Navigate to your hub web root ``` - root@hub:/root# cd /var/www + root@hub:/root# cd /var/www ``` 2. Add the theme repo and give it a name @@ -319,70 +300,136 @@ set the account that owns channel 'blogchan' to service class 'firstclass' (with #### Keywords -There is a "tag cloud" of keywords that can appear on the channel directory page. -If you wish to hide these keywords, which are drawn from the directory server, you +There is a "tag cloud" of keywords that can appear on the channel directory page. +If you wish to hide these keywords, which are drawn from the directory server, you can use the *config* tool: util/config system disable_directory_keywords 1 - -If your hub is in the standalone mode because you do not wish to connect to the -global grid, you may instead ensure the the _directory_server_ system option is + +If your hub is in the standalone mode because you do not wish to connect to the +global grid, you may instead ensure the the _directory_server_ system option is empty: util/config system directory_server "" - ### Administration #### Site Administration -Administration of the website is commonly done through the admin webpage located at /admin on your website. In order to access this page you must have administration rights to the server. Administration rights are granted to the first account to register on your site, **provided** the email address of that account exactly matches the email address you provided as the administrator's email address during setup. +Administration of the website is commonly done through the admin webpage located at /admin on your website. In order to access this page you must have administration rights to the server. Administration rights are granted to the first account to register on your site, **provided** the email address of that account exactly matches the email address you provided as the administrator's email address during setup. -There are several ways that this can fail and leave the system without an administrator account, for instance if the first account that was created provided a different email address than the administrator email address that was supplied during setup. +There are several ways that this can fail and leave the system without an administrator account, for instance if the first account that was created provided a different email address than the administrator email address that was supplied during setup. For security reasons there is no web page or interface on the system which will give you administrator access. If you need to correct a situation where a system has no administrator account it **must** be done by editing the account table in the database. There is no other way. To do this, you will need to locate the entry in the account table which belongs to the desired administrator, and set 'account_roles' for that entry to 4096. You will then be able to access the admin page from your system's profile menu or directly via /admin . -A hub can have multiple admins and there is no limit to how administrators you can have. Repeat the above process for every account you wish to provide with administration rights. - +A hub can have multiple admins and there is no limit to how administrators you can have. Repeat the above process for every account you wish to provide with administration rights. ### Troubleshooting #### Log files -The system logfile is an extremely useful resource for tracking down things that go wrong. This can be enabled in the admin/log configuration page. A loglevel setting of LOGGER_DEBUG is preferred for stable production sites. Most things that go wrong with communications or storage are listed here. A setting of LOGGER_DATA provides [b]much[/b] more detail, but may fill your disk. In either case we recommend the use of logrotate on your operating system to cycle logs and discard older entries. - -At the bottom of your .htconfig.php file are several lines (commented out) which enable PHP error logging. This reports issues with code syntax and executing the code and is the first place you should look for issues which result in a "white screen" or blank page. This is typically the result of code/syntax problems. -Database errors are reported to the system logfile, but we've found it useful to have a file in your top-level directory called dbfail.out which [b]only[/b] collects database related issues. If the file exists and is writable, database errors will be logged to it as well as to the system logfile. - -In the case of "500" errors, the issues may often be logged in your webserver logs, often /var/log/apache2/error.log or something similar. Consult your operating system documentation. +The system logfile is an extremely useful resource for tracking down +things that go wrong. This can be enabled in the admin/log +configuration page. A loglevel setting of `LOGGER_DEBUG` is preferred +for stable production sites. Most things that go wrong with +communications or storage are listed here. A setting of LOGGER_DATA +provides *much* more detail, but may fill your disk. In either +case we recommend the use of logrotate on your operating system to +cycle logs and discard older entries. + +At the bottom of your .htconfig.php file are several lines (commented +out) which enable PHP error logging. This reports issues with code +syntax and executing the code and is the first place you should look +for issues which result in a "white screen" or blank page. This is +typically the result of code/syntax problems. Database errors are +reported to the system logfile, but we've found it useful to have a +file in your top-level directory called dbfail.out which *only* +collects database related issues. If the file exists and is writable, +database errors will be logged to it as well as to the system logfile. + +In the case of "500" errors, the issues may often be logged in your +webserver logs, often /var/log/apache2/error.log or something +similar. Consult your operating system documentation. There are three different log facilities. -**The first is the database failure log**. This is only used if you create a file called specifically 'dbfail.out' in the root folder of your website and make it write-able by the web server. If we have any database failed queries, they are all reported here. They generally indicate typos in our queries, but also occur if the database server disconnects or tables get corrupted. On rare occasions we'll see race conditions in here where two processes tried to create an xchan or cache entry with the same ID. Any other errors (especially persistent errors) should be investigated. - -**The second is the PHP error log**. This is created by the language processor and only reports issues in the language environment. Again these can be syntax errors or programming errors, but these generally are fatal and result in a "white screen of death"; e.g. PHP terminates. You should probably look at this file if something goes wrong that doesn't result in a white screen of death, but it isn't uncommon for this file to be empty for days on end. - -There are some lines at the bottom of the supplied .htconfig.php file; which if uncommented will enable a PHP error log (*extremely* useful for finding the source of white screen failures). This isn't done by default due to potential issues with logfile ownership and write permissions and the fact that there is no logfile rotation by default. - - -**The third is the "application log"**. This is used by $Projectname to report what is going on in the program and usually reports any difficulties or unexpected data we received. It also occasionally reports "heartbeat" status messages to indicate that we reached a certain point in a script. **This** is the most important log file to us, as we create it ourself for the sole purpose of reporting the status of background tasks and anything that seems weird or out of place. It may not be fatal, but maybe just unexpected. If you're performing a task and there's a problem, let us know what is in this file when the problem occurred. (Please don't send me 100M dumps you'll only piss me off). Just a few relevant lines so I can rule out a few hundred thousand lines of code and concentrate on where the problem starts showing up. +**The first is the database failure log**. This is only used if you + create a file called specifically `dbfail.out` in the root folder of + your website and make it write-able by the web server. If we have + any database failed queries, they are all reported here. They + generally indicate typos in our queries, but also occur if the + database server disconnects or tables get corrupted. On rare + occasions we'll see race conditions in here where two processes + tried to create an xchan or cache entry with the same ID. Any other + errors (especially persistent errors) should be investigated. + +**The second is the PHP error log**. This is created by the language + processor and only reports issues in the language environment. Again + these can be syntax errors or programming errors, but these + generally are fatal and result in a "white screen of death"; + e.g. PHP terminates. You should probably look at this file if + something goes wrong that doesn't result in a white screen of death, + but it isn't uncommon for this file to be empty for days on end. + + There are some lines at the bottom of the supplied `.htconfig.php` + file; which if uncommented will enable a PHP error log (*extremely* + useful for finding the source of white screen failures). This isn't + done by default due to potential issues with logfile ownership and + write permissions and the fact that there is no logfile rotation by + default. + +**The third is the "application log"**. This is used by $Projectname + to report what is going on in the program and usually reports any + difficulties or unexpected data we received. It also occasionally + reports "heartbeat" status messages to indicate that we reached a + certain point in a script. **This** is the most important log file + to us, as we create it ourself for the sole purpose of reporting the + status of background tasks and anything that seems weird or out of + place. It may not be fatal, but maybe just unexpected. If you're + performing a task and there's a problem, let us know what is in this + file when the problem occurred. (Please don't send me 100M dumps + you'll only piss me off). Just a few relevant lines so I can rule + out a few hundred thousand lines of code and concentrate on where + the problem starts showing up. + +These are your site logs, not mine. We report serious issues at any +log level. I highly recommend `DEBUG` log level for most sites - which +provides a bit of additional info and doesn't create huge +logfiles. When there's a problem which defies all attempts to track, +you might wish to use `DATA` log level for a short period of time to +capture all the detail of what structures we were dealing with at the +time. This log level will use a lot of space so is recommended only +for brief periods or for developer test sites. + +I recommend configuring logrotate for both the php log and the +application log. I usually have a look at dbfail.out every week or +two, fix any issues reported and then starting over with a fresh +file. Likewise with the PHP logfile. I refer to it once in a while to +see if there's something that needs fixing. + +If something goes wrong, and it's not a fatal error, I look at the +application logfile. Often I will -These are your site logs, not mine. We report serious issues at any log level. I highly recommend 'DEBUG' log level for most sites - which provides a bit of additional info and doesn't create huge logfiles. When there's a problem which defies all attempts to track, you might wish to use DATA log level for a short period of time to capture all the detail of what structures we were dealing with at the time. This log level will use a lot of space so is recommended only for brief periods or for developer test sites. - -I recommend configuring logrotate for both the php log and the application log. I usually have a look at dbfail.out every week or two, fix any issues reported and then starting over with a fresh file. Likewise with the PHP logfile. I refer to it once in a while to see if there's something that needs fixing. - -If something goes wrong, and it's not a fatal error, I look at the application logfile. Often I will ``` -tail -f logfile.out +tail -f logfile.out ``` -While repeating an operation that has problems. Often I'll insert extra logging statements in the code if there isn't any hint what's going wrong. Even something as simple as "got here" or printing out the value of a variable that might be suspect. You can do this too - in fact I encourage you to do so. Once you've found what you need to find, you can +While repeating an operation that has problems. Often I'll insert +extra logging statements in the code if there isn't any hint what's +going wrong. Even something as simple as "got here" or printing out +the value of a variable that might be suspect. You can do this too - +in fact I encourage you to do so. Once you've found what you need to +find, you can ``` git checkout file.php ``` -To immediately clear out all the extra logging stuff you added. Use the information from this log and any detail you can provide from your investigation of the problem to file your bug report - unless your analysis points to the source of the problem. In that case, just fix it. +To immediately clear out all the extra logging stuff you added. Use +the information from this log and any detail you can provide from your +investigation of the problem to file your bug report - unless your +analysis points to the source of the problem. In that case, just fix +it. ##### Rotating log files @@ -390,13 +437,25 @@ To immediately clear out all the extra logging stuff you added. Use the informa 1. Create a directory in your web root called `log` with webserver write permissions 1. Go to the **logrot** admin settings and enter this folder name as well as the max size and number of retained log files. - #### Reporting issues -When reporting issues, please try to provide as much detail as may be necessary for developers to reproduce the issue and provide the complete text of all error messages. - -We encourage you to try to the best of your abilities to use these logs combined with the source code in your possession to troubleshoot issues and find their cause. The community is often able to help, but only you have access to your site logfiles and it is considered a security risk to share them. - -If a code issue has been uncovered, please report it on the project bugtracker (https://framagit.org/hubzilla/core/issues). Again provide as much detail as possible to avoid us going back and forth asking questions about your configuration or how to duplicate the problem, so that we can get right to the problem and figure out what to do about it. You are also welcome to offer your own solutions and submit patches. In fact we encourage this as we are all volunteers and have little spare time available. The more people that help, the easier the workload for everybody. It's OK if your solution isn't perfect. Every little bit helps and perhaps we can improve on it. - - +When reporting issues, please try to provide as much detail as may be +necessary for developers to reproduce the issue and provide the +complete text of all error messages. + +We encourage you to try to the best of your abilities to use these +logs combined with the source code in your possession to troubleshoot +issues and find their cause. The community is often able to help, but +only you have access to your site logfiles and it is considered a +security risk to share them. + +If a code issue has been uncovered, please report it on the project +bugtracker (https://framagit.org/hubzilla/core/issues). Again provide +as much detail as possible to avoid us going back and forth asking +questions about your configuration or how to duplicate the problem, so +that we can get right to the problem and figure out what to do about +it. You are also welcome to offer your own solutions and submit +patches. In fact we encourage this as we are all volunteers and have +little spare time available. The more people that help, the easier the +workload for everybody. It's OK if your solution isn't perfect. Every +little bit helps and perhaps we can improve on it. diff --git a/include/dba/dba_pdo.php b/include/dba/dba_pdo.php index a70e4a1d7..0279342ec 100755 --- a/include/dba/dba_pdo.php +++ b/include/dba/dba_pdo.php @@ -161,23 +161,17 @@ class dba_pdo extends dba_driver { } function unescapebin($str) { - if($this->driver_dbtype === 'pgsql' && (! is_null($str))) { - $x = ''; - while(! feof($str)) { - $x .= fread($str,8192); + if($this->driver_dbtype === 'pgsql') { + if(gettype($str) === 'resource') { + $str = stream_get_contents($str); } - if(substr($x,0,2) === '\\x') { - $x = hex2bin(substr($x,2)); + if(substr($str,0,2) === '\\x') { + $str = hex2bin(substr($str,2)); } - return $x; - - } - else { - return $str; } + return $str; } - function getdriver() { return 'pdo'; } diff --git a/include/event.php b/include/event.php index 77118c329..cfca00a3c 100644 --- a/include/event.php +++ b/include/event.php @@ -1284,6 +1284,10 @@ function cdav_principal($uri) { } function cdav_perms($needle, $haystack, $check_rw = false) { + + if($needle == 'channel_calendar') + return true; + foreach ($haystack as $item) { if($check_rw) { if(is_array($item['id'])) { diff --git a/view/css/cdav_calendar.css b/view/css/cdav_calendar.css index 2deff683c..da594b420 100644 --- a/view/css/cdav_calendar.css +++ b/view/css/cdav_calendar.css @@ -29,3 +29,9 @@ main.fullscreen .fc td:last-child { .fc-list-view { border-width: 0px; } + +.bootstrap-tagsinput { + width: 100%; + padding: 6px 12px; +} + diff --git a/view/theme/redbasic/css/style.css b/view/theme/redbasic/css/style.css index 18c3db665..3bee84378 100644 --- a/view/theme/redbasic/css/style.css +++ b/view/theme/redbasic/css/style.css @@ -942,17 +942,18 @@ a .generic-icons { color: $font_colour; } -.generic-icons:hover, -a .generic-icons:hover { - color: $font_colour; -} - .generic-icons-right { font-size: 1rem; margin-left: 0.5rem; color: $font_colour; } +.generic-icons:hover, +a .generic-icons:hover, +.generic-icons-right:hover, +a .generic-icons-right:hover { + color: $font_colour; +} .generic-icons-nav { font-size: 1rem; diff --git a/view/tpl/cdav_calendar.tpl b/view/tpl/cdav_calendar.tpl index a577e3120..5c2f0080b 100644 --- a/view/tpl/cdav_calendar.tpl +++ b/view/tpl/cdav_calendar.tpl @@ -4,6 +4,14 @@ var new_event = {}; var new_event_id = Math.random().toString(36).substring(7); var views = {'dayGridMonth' : '{{$month}}', 'timeGridWeek' : '{{$week}}', 'timeGridDay' : '{{$day}}', 'listMonth' : '{{$list_month}}', 'listWeek' : '{{$list_week}}', 'listDay' : '{{$list_day}}'}; +var event_id; +var event_uri; + +var contact_allow = []; +var group_allow = []; +var contact_deny = []; +var group_deny = []; + $(document).ready(function() { var calendarEl = document.getElementById('calendar'); calendar = new FullCalendar.Calendar(calendarEl, { @@ -45,7 +53,7 @@ $(document).ready(function() { dtend.setHours(dtend.getHours() + 1); } - $('#event_uri').val(''); + event_uri = ''; $('#id_title').val('New event'); $('#calendar_select').val($("#calendar_select option:first").val()).attr('disabled', false); $('#id_dtstart').val(info.date.toUTCString()); @@ -61,15 +69,32 @@ $(document).ready(function() { eventClick: function(info) { + //reset categories + $('#id_categories').tagsinput('removeAll'); + var event = info.event._def; var dtstart = new Date(info.event._instance.range.start); var dtend = new Date(info.event._instance.range.end); - + console.log(event.extendedProps.categories); + if(event.extendedProps.plink) { + if(! $('#l2s').length) + $('#id_title_wrapper').prepend('<span id="l2s" class="float-right"></span>'); + + $('#l2s').html('<a href="' + event.extendedProps.plink[0] + '" target="_blank"><i class="fa fa-external-link"></i> ' + event.extendedProps.plink[1] + '</a>'); + } + else { + $('#l2s').remove(); + } + if(event.publicId == new_event_id) { + $('#calendar_select').trigger('change'); + $('#event_submit').show(); + event_id = 0; $(window).scrollTop(0); $('.section-content-tools-wrapper, #event_form_wrapper').show(); $('#recurrence_warning').hide(); $('#id_title').focus().val(''); + return false; } @@ -79,18 +104,29 @@ $(document).ready(function() { new_event = {}; } + var calendar_id = ((event.extendedProps.calendar_id.constructor === Array) ? event.extendedProps.calendar_id[0] + ':' + event.extendedProps.calendar_id[1] : event.extendedProps.calendar_id); + if(!event.extendedProps.recurrent) { $(window).scrollTop(0); $('.section-content-tools-wrapper, #event_form_wrapper').show(); $('#recurrence_warning').hide(); - $('#event_uri').val(event.extendedProps.uri); + event_uri = event.extendedProps.uri; $('#id_title').val(event.title); - $('#calendar_select').val(event.extendedProps.calendar_id[0] + ':' + event.extendedProps.calendar_id[1]).attr('disabled', true); + $('#calendar_select').val(calendar_id).attr('disabled', true).trigger('change'); + $('#id_categories').tagsinput('add', event.extendedProps.categories); $('#id_dtstart').val(dtstart.toUTCString()); $('#id_dtend').val(dtend.toUTCString()); $('#id_description').val(event.extendedProps.description); $('#id_location').val(event.extendedProps.location); - $('#event_submit').val('update_event').html('Update'); + $('#event_submit').val('update_event').html('{{$update}}'); + $('#dbtn-acl').addClass('d-none'); + event_id = event.extendedProps.item ? event.extendedProps.item.id : 0; + + contact_allow = event.extendedProps.contact_allow || []; + group_allow = event.extendedProps.group_allow || []; + contact_deny = event.extendedProps.contact_deny || []; + group_deny = event.extendedProps.group_deny || []; + if(event.extendedProps.rw) { $('#event_delete').show(); $('#event_submit').show(); @@ -100,6 +136,11 @@ $(document).ready(function() { $('#id_dtend').attr('disabled', false); $('#id_description').attr('disabled', false); $('#id_location').attr('disabled', false); + + if(calendar_id == 'channel_calendar' && !event.extendedProps.is_editable) { + console.log(calendar_id) + $('#event_submit').hide(); + } } else { $('#event_submit').hide(); @@ -114,8 +155,8 @@ $(document).ready(function() { else if(event.extendedProps.recurrent && event.extendedProps.rw) { $('.section-content-tools-wrapper, #recurrence_warning').show(); $('#event_form_wrapper').hide(); - $('#event_uri').val(event.extendedProps.uri); - $('#calendar_select').val(event.extendedProps.calendar_id[0] + ':' + event.extendedProps.calendar_id[1]).attr('disabled', true); + event_uri = event.extendedProps.uri; + $('#calendar_select').val(calendar_id).attr('disabled', true).trigger('change'); } }, @@ -193,6 +234,13 @@ $(document).ready(function() { calendar.next(); $('#title').text(calendar.view.title); }); + + $('#calendar_select').on('change', function() { + if(this.value === 'channel_calendar') + $('#dbtn-acl, #id_categories_wrapper').removeClass('d-none'); + else + $('#dbtn-acl, #id_categories_wrapper').addClass('d-none'); + }); $('.color-edit').colorpicker({ input: '.color-edit-input' }); @@ -213,9 +261,16 @@ function changeView(action, viewName) { } function add_remove_json_source(source, color, editable, status) { - var parts = source.split('/'); - var id = parts[4]; - + var id, parts = []; + + if(source == '/channel_calendar/json') + id = 'channel_calendar' + + if(! id) { + parts = source.split('/'); + id = parts[4]; + } + var eventSource = calendar.getEventSourceById(id); var selector = '#calendar-btn-' + id; @@ -247,37 +302,102 @@ function updateSize() { } function on_submit() { - $.post( 'cdav/calendar', { - 'submit': $('#event_submit').val(), - 'target': $('#calendar_select').val(), - 'uri': $('#event_uri').val(), - 'title': $('#id_title').val(), - 'dtstart': $('#id_dtstart').val(), - 'dtend': $('#id_dtend').val(), - 'description': $('#id_description').val(), - 'location': $('#id_location').val() - }) - .done(function() { - var parts = $('#calendar_select').val().split(':'); - var eventSource = calendar.getEventSourceById(parts[0]); - eventSource.refetch(); - reset_form(); + if($('#calendar_select').val() == 'channel_calendar') { + if(new_event_id) { + $("input[name='contact_allow[]']").each(function() { + contact_allow.push($(this).val()); + }); + $("input[name='group_allow[]']").each(function() { + group_allow.push($(this).val()); + }); + $("input[name='contact_deny[]']").each(function() { + contact_deny.push($(this).val()); + }); + $("input[name='group_deny[]']").each(function() { + group_deny.push($(this).val()); + }); + } - }); + $.post( 'channel_calendar', { + 'event_id': event_id, + 'event_hash': event_uri, + 'xchan': '{{$channel_hash}}', + //'mid': mid, + 'type': 'event', + 'preview': 0, + 'summary': $('#id_title').val(), + 'dtstart': $('#id_dtstart').val(), + 'dtend': $('#id_dtend').val(), + 'adjust': 0, + 'categories': $('#id_categories').val(), + 'desc': $('#id_description').val(), + 'location': $('#id_location').val(), + 'submit': $('#event_submit').val(), + 'contact_allow[]': contact_allow, + 'group_allow[]': group_allow, + 'contact_deny[]': contact_deny, + 'group_deny[]': group_deny +/* + 'submit': $('#event_submit').val(), + 'target': $('#calendar_select').val(), + 'uri': $('#event_uri').val(), + 'title': $('#id_title').val(), + 'dtstart': $('#id_dtstart').val(), + 'dtend': $('#id_dtend').val(), + 'description': $('#id_description').val(), + 'location': $('#id_location').val() +*/ + }) + .done(function() { + var eventSource = calendar.getEventSourceById('channel_calendar'); + eventSource.refetch(); + reset_form(); + + }); + + } + else { + $.post( 'cdav/calendar', { + 'submit': $('#event_submit').val(), + 'target': $('#calendar_select').val(), + 'uri': event_uri, + 'title': $('#id_title').val(), + 'dtstart': $('#id_dtstart').val(), + 'dtend': $('#id_dtend').val(), + 'description': $('#id_description').val(), + 'location': $('#id_location').val() + }) + .done(function() { + var parts = $('#calendar_select').val().split(':'); + var eventSource = calendar.getEventSourceById(parts[0]); + eventSource.refetch(); + reset_form(); + + }); + } } function on_delete() { - $.post( 'cdav/calendar', { - 'delete': 'delete', - 'target': $('#calendar_select').val(), - 'uri': $('#event_uri').val(), - }) - .done(function() { - var parts = $('#calendar_select').val().split(':'); - var eventSource = calendar.getEventSourceById(parts[0]); - eventSource.refetch(); - reset_form(); - }); + if($('#calendar_select').val() == 'channel_calendar') { + $.get('channel_calendar/drop/' + event_uri, function() { + var eventSource = calendar.getEventSourceById('channel_calendar'); + eventSource.refetch(); + reset_form(); + }); + } + else { + $.post( 'cdav/calendar', { + 'delete': 'delete', + 'target': $('#calendar_select').val(), + 'uri': event_uri + }) + .done(function() { + var parts = $('#calendar_select').val().split(':'); + var eventSource = calendar.getEventSourceById(parts[0]); + eventSource.refetch(); + reset_form(); + }); + } } function reset_form() { @@ -285,8 +405,9 @@ function reset_form() { $('#event_submit').val(''); $('#calendar_select').val(''); - $('#event_uri').val(''); + event_uri = ''; $('#id_title').val(''); + $('#id_categories').tagsinput('removeAll'); $('#id_dtstart').val(''); $('#id_dtend').val(''); @@ -311,6 +432,14 @@ function on_more() { } } +function exportDate() { + alert('not implemented'); + console.log('not implemented'); + //var moment = $('#events-calendar').fullCalendar('getDate'); + //var sT = 'events/' + moment.year() + '/' + (moment.month() + 1) + '/export'; + //window.location.href=sT; +} + </script> <div class="generic-content-wrapper"> @@ -350,16 +479,28 @@ function on_more() { </div> </div> <div id="event_form_wrapper" style="display: none"> - <form id="event_form" method="post" action=""> - <input id="event_uri" type="hidden" name="uri" value=""> + <form id="event_form" method="post" action="" class="acl-form" data-form_id="event_form" data-allow_cid='{{$allow_cid}}' data-allow_gid='{{$allow_gid}}' data-deny_cid='{{$deny_cid}}' data-deny_gid='{{$deny_gid}}'> {{include file="field_input.tpl" field=$title}} <label for="calendar_select">{{$calendar_select_label}}</label> <select id="calendar_select" name="target" class="form-control form-group"> + <optgroup label="{{$calendar_optiopns_label.0}}"> + {{foreach $channel_calendars as $channel_calendar}} + <option value="channel_calendar">{{$channel_calendar.displayname}}</option> + {{/foreach}} + </optgroup> + <optgroup label="{{$calendar_optiopns_label.1}}"> {{foreach $writable_calendars as $writable_calendar}} <option value="{{$writable_calendar.id.0}}:{{$writable_calendar.id.1}}">{{$writable_calendar.displayname}}{{if $writable_calendar.sharer}} ({{$writable_calendar.sharer}}){{/if}}</option> {{/foreach}} + </optgroup> </select> <div id="more_block" style="display: none;"> + {{if $catsenabled}} + <div id="id_categories_wrapper" class="form-group"> + <label id="label_categories" for="id_categories">{{$categories_label}}</label> + <input name="categories" id="id_categories" class="form-control" type="text" value="{{$categories}}" data-role="cat-tagsinput" /> + </div> + {{/if}} {{include file="field_input.tpl" field=$dtstart}} {{include file="field_input.tpl" field=$dtend}} {{include file="field_textarea.tpl" field=$description}} @@ -368,6 +509,7 @@ function on_more() { <div class="form-group"> <div class="pull-right"> <button id="event_more" type="button" class="btn btn-outline-secondary btn-sm"><i class="fa fa-caret-down"></i> {{$more}}</button> + <button id="dbtn-acl" class="btn btn-outline-secondary btn-sm d-none" type="button" data-toggle="modal" data-target="#aclModal"><i id="jot-perms-icon" class="fa fa-{{$lockstate}}"></i></button> <button id="event_submit" type="button" value="" class="btn btn-primary btn-sm"></button> </div> @@ -378,6 +520,7 @@ function on_more() { <div class="clear"></div> </div> </form> + {{$acl}} </div> </div> <div class="section-content-wrapper-np"> diff --git a/view/tpl/cdav_widget_calendar.tpl b/view/tpl/cdav_widget_calendar.tpl index 8d6414ec6..a538cd26d 100644 --- a/view/tpl/cdav_widget_calendar.tpl +++ b/view/tpl/cdav_widget_calendar.tpl @@ -1,3 +1,17 @@ +<div class="widget"> + <h3>{{$channel_calendars_label}}</h3> + {{foreach $channel_calendars as $channel_calendar}} + <div id="calendar-{{$channel_calendar.calendarid}}"> + <div class="ml-3{{if !$channel_calendar@last}} form-group{{/if}}"> + <i id="calendar-btn-{{$channel_calendar.calendarid}}" class="fa {{if $channel_calendar.switch}}fa-calendar-check-o{{else}}fa-calendar-o{{/if}} generic-icons fakelink" onclick="add_remove_json_source('{{$channel_calendar.json_source}}', '{{$channel_calendar.color}}', {{$channel_calendar.editable}})" style="color: {{$channel_calendar.color}};"></i>{{$channel_calendar.displayname}} + <div class="float-right"> + <a href="#" onclick="exportDate(); return false;"><i id="download-icon" class="fa fa-cloud-download fakelink generic-icons-right"></i></a> + </div> + </div> + </div> + {{/foreach}} +</div> + {{if $my_calendars}} <div class="widget"> <h3>{{$my_calendars_label}}</h3> diff --git a/view/tpl/connection_template.tpl b/view/tpl/connection_template.tpl index 4543a69d0..a40406d5d 100755 --- a/view/tpl/connection_template.tpl +++ b/view/tpl/connection_template.tpl @@ -43,7 +43,7 @@ {{/if}} {{if $contact.network}} <div class="contact-info-element"> - <span class="contact-info-label">{{$contact.network_label}}:</span> {{$contact.network}} - <a href="{{$contact.recentlink}}">{{$contact.recent_label}}</a> + <span class="contact-info-label">{{$contact.network_label}}:</span> {{$contact.network}} - <a href="{{$contact.recentlink}}" rel="nofollow noopener">{{$contact.recent_label}}</a> </div> {{/if}} </div> diff --git a/view/tpl/jot-header.tpl b/view/tpl/jot-header.tpl index afaaa62d9..7b1f4ee05 100755 --- a/view/tpl/jot-header.tpl +++ b/view/tpl/jot-header.tpl @@ -354,7 +354,7 @@ var activeCommentText = ''; } function itemAddToCal(id) { - $.get('{{$baseurl}}/events/add/' + id); + $.get('{{$baseurl}}/channel_calendar/add/' + id); if(timer) clearTimeout(timer); timer = setTimeout(updateInit,1000); } diff --git a/view/tpl/profile_photo.tpl b/view/tpl/profile_photo.tpl index b2190dda3..61d8f4254 100755 --- a/view/tpl/profile_photo.tpl +++ b/view/tpl/profile_photo.tpl @@ -125,14 +125,15 @@ <div id="profile-photo-submit-wrapper"> <button type="submit" class="btn btn-outline-primary" name="submit" id="profile-photo-submit">{{$submit}}</button> + <button type="submit" class="btn btn-outline-danger" name="remove" id="profile-photo-remove">{{$remove}}</button> </div> </div> </form> <br /> <div id="profile-photo-link-select-wrapper"> - <button id="embed-photo-wrapper" class="btn btn-default btn-primary" title="{{$embedPhotos}}" onclick="initializeEmbedPhotoDialog();return false;"> - <i id="embed-photo" class="fa fa-file-image-o"></i> {{$select}} + <button id="embed-photo-wrapper" class="btn btn-default btn-primary" title="{{$embedPhotos}}" onclick="initializeEmbedPhotoDialog();return false;"> + <i id="embed-photo" class="fa fa-file-image-o"></i> {{$select}} </button> </div> </div> |