Merge branch 'origin' into 'develop'
Update 4.2 See merge request harukin/core!55
This commit is contained in:
commit
76e879e5de
@ -1,6 +1,6 @@
|
||||
# Select image from https://hub.docker.com/_/php/
|
||||
#image: php:7.2
|
||||
# Use a prepared Hubzilla image to optimise pipeline run
|
||||
# Use a prepared Hubzilla image to optimise pipeline duration
|
||||
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.2
|
||||
|
||||
|
||||
@ -32,55 +32,28 @@ variables:
|
||||
|
||||
|
||||
before_script:
|
||||
# pecl and composer do not work with PHP production restrictions (from Hubzilla Docker image)
|
||||
- if [ -f /usr/local/etc/php/conf.d/z_prod.ini ]; then mv /usr/local/etc/php/conf.d/z_prod.ini /usr/local/etc/php/conf.d/z_prod.ini.off; fi
|
||||
# Install & enable Xdebug for code coverage reports
|
||||
- pecl install xdebug
|
||||
- docker-php-ext-enable xdebug
|
||||
# Install composer
|
||||
- curl -sS https://getcomposer.org/installer | php
|
||||
# Install dev libraries from composer
|
||||
- php composer.phar install --no-progress
|
||||
- php ./composer.phar install --no-progress
|
||||
|
||||
|
||||
# test PHP7 with MySQL 5.7
|
||||
php7.2_mysql 1/2:
|
||||
# hidden job definition with template for MySQL/MariaDB
|
||||
.job_template_mysql: &job_definition_mysql
|
||||
stage: test
|
||||
services:
|
||||
- mysql:5.7
|
||||
script:
|
||||
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
|
||||
|
||||
|
||||
# test PHP7 with MySQL latest (8)
|
||||
php7.2_mysql 2/2:
|
||||
stage: test
|
||||
services:
|
||||
- name: mysql:latest
|
||||
command: ["--default-authentication-plugin=mysql_native_password"]
|
||||
script:
|
||||
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
|
||||
|
||||
|
||||
# test PHP7 with MariaDB latest (10.3)
|
||||
php7.2_mariadb:
|
||||
stage: test
|
||||
services:
|
||||
- name: mariadb:latest
|
||||
alias: mysql
|
||||
script:
|
||||
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
|
||||
|
||||
|
||||
# test PHP7 with PostgreSQL latest
|
||||
php7.2_postgres:
|
||||
# hidden job definition with template for PostgreSQL
|
||||
.job_template_postgres: &job_definition_postgres
|
||||
stage: test
|
||||
services:
|
||||
- postgres:latest
|
||||
@ -95,7 +68,10 @@ php7.2_postgres:
|
||||
#- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt;"
|
||||
# Run the actual tests
|
||||
- vendor/bin/phpunit --configuration tests/phpunit-pgsql.xml --testdox
|
||||
artifacts:
|
||||
|
||||
# hidden job definition with artifacts config template
|
||||
.artifacts_template:
|
||||
artifacts: &artifacts_template
|
||||
expire_in: 1 week
|
||||
# Gitlab should show the results, but has problems parsing PHPUnit's junit file.
|
||||
reports:
|
||||
@ -106,7 +82,52 @@ php7.2_postgres:
|
||||
- tests/results/
|
||||
|
||||
|
||||
# Generate Doxygen API Documentation and deploy it at GitLab pages
|
||||
# PHP7.2 with MySQL 5.7
|
||||
php7.2_mysql5.7:
|
||||
<<: *job_definition_mysql
|
||||
services:
|
||||
- mysql:5.7
|
||||
|
||||
|
||||
# PHP7.2 with MySQL 8 (latest)
|
||||
php7.2_mysql8:
|
||||
<<: *job_definition_mysql
|
||||
services:
|
||||
- name: mysql:8
|
||||
command: ["--default-authentication-plugin=mysql_native_password"]
|
||||
|
||||
|
||||
# PHP7.2 with MariaDB 10.2
|
||||
php7.2_mariadb10.2:
|
||||
<<: *job_definition_mysql
|
||||
services:
|
||||
- name: mariadb:10.2
|
||||
alias: mysql
|
||||
|
||||
|
||||
# PHP7.3 with MariaDB 10.3 (latest)
|
||||
php7.3_mariadb10.3:
|
||||
<<: *job_definition_mysql
|
||||
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
|
||||
services:
|
||||
- name: mariadb:10.3
|
||||
alias: mysql
|
||||
|
||||
|
||||
# PHP7.2 with PostgreSQL latest (11)
|
||||
php7.2_postgres11:
|
||||
<<: *job_definition_postgres
|
||||
artifacts: *artifacts_template
|
||||
|
||||
|
||||
# PHP7.3 with PostgreSQL latest (11)
|
||||
php7.3_postgres11:
|
||||
<<: *job_definition_postgres
|
||||
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
|
||||
artifacts: *artifacts_template
|
||||
|
||||
|
||||
# Generate Doxygen API Documentation and deploy it as GitLab pages
|
||||
pages:
|
||||
stage: deploy
|
||||
cache: {}
|
||||
|
@ -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,58 +92,28 @@ 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
|
||||
## Install Debian 9
|
||||
|
||||
Download the stable Debian at https://www.debian.org/
|
||||
(Debian 8 is no longer supported.)
|
||||
Provided you use a Raspberry Pi 3...
|
||||
|
||||
Create bootable USB drive with Debian on it.You could use
|
||||
Download the OS Raspbian from https://www.raspberrypi.org/downloads/raspbian/
|
||||
|
||||
- unetbootin, https://en.wikipedia.org/wiki/UNetbootin
|
||||
- or simply the linux command "dd"
|
||||
Follow the installation instruction there.
|
||||
|
||||
Example for command dd...
|
||||
## Configure your Router
|
||||
|
||||
su -
|
||||
dd if=2018-10-09-raspbian-stretch.img of=/dev/mmcblk0
|
||||
Your web has to be visible in the internet.
|
||||
|
||||
Do not forget to unmount the SD card before and check if unmounted like in this example...
|
||||
|
||||
su -
|
||||
umount /dev/mmcblk0*
|
||||
df -h
|
||||
|
||||
|
||||
Switch off your mini pc, plug in your USB drive and start the mini pc from the
|
||||
stick. Install Debian. Follow the instructions of the installation.
|
||||
|
||||
### Configure your Router
|
||||
|
||||
Open the ports 80 and 443 on your router for your Debian
|
||||
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
|
||||
|
||||
Follow the instructions in .homeinstall/hubzilla-config.txt.
|
||||
|
||||
In short...
|
||||
|
||||
Your Hubzilla must be reachable by a domain that you can type in your browser
|
||||
|
||||
cooldomain.org
|
||||
@ -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.
|
||||
## Note on Rasperry
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
|
@ -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"
|
||||
|
275
.homeinstall/hubzilla-setup.sh
Executable file → Normal file
275
.homeinstall/hubzilla-setup.sh
Executable file → Normal file
@ -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
|
||||
@ -44,26 +45,11 @@
|
||||
# - 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
|
||||
# - /var/www/ > hubzilla/zap from github
|
||||
# - /etc/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
|
||||
#
|
||||
# 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
|
||||
# -------
|
||||
@ -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 ]
|
||||
# check if user gave mail address
|
||||
if [ -z "$le_email" ]
|
||||
then
|
||||
print_info "$apache_le_conf exist already"
|
||||
die "Failed to install let's encrypt: 'le_domain' is empty in $configfile"
|
||||
fi
|
||||
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 "$backports_list 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
|
||||
echo "deb https://deb.debian.org/debian stretch-backports main" > $backports_list
|
||||
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 ]
|
||||
then
|
||||
print_warn "Failed to configure apache for httpS: Missing certificate file $SSLCertificateFile"
|
||||
return 0
|
||||
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
|
||||
then
|
||||
print_info "seems that apache was already configered to use httpS with $sslconf"
|
||||
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
|
||||
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,7 +572,9 @@ 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 " 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
|
||||
@ -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
|
||||
|
58
CHANGELOG
58
CHANGELOG
@ -1,3 +1,61 @@
|
||||
Hubzilla 4.2 (2019-06-04)
|
||||
- Introduce Calendar app which deprecates Events and CalDAV apps and streamlines the featuresets
|
||||
- Update mod cal to reflect changes in the calendar app
|
||||
- Improve timezone detection for CalDAV calendars
|
||||
- Add mention support to event description in channel calendar
|
||||
- Update jgrowl library
|
||||
- Do not try to oembed URLs without embed tags
|
||||
- Optimise pdf oembed processing
|
||||
- Add form security token to mod register
|
||||
- Replace URLs for mod gallery, mod photos and mod photo on cloned channel post sync
|
||||
- Update justified gallery library
|
||||
- Update bootstrap libraries
|
||||
- Use "cache" flag for bbcode() on content destined for zot6
|
||||
- Improve DB indexing
|
||||
- Drop deprecated columns from channel the table
|
||||
- Replace own image URL in clonned channel posts
|
||||
- Improve DB update handling
|
||||
- Improve item deletion when a contact was removed
|
||||
- Zot6 compatibility for emoji reactions
|
||||
- Add threaded comments support (disabled by default)
|
||||
- Improve xmlify()/unxmlify() performance
|
||||
- Update blueimp/jquery-file-uplad library
|
||||
- Update sabre/vobject library
|
||||
- Various doco updates
|
||||
- Implement remove profile photo button (reset to default photo)
|
||||
- Implement remove cover photo button
|
||||
- Update the homeinstall script
|
||||
- Add command line tool for photo thumbnails storage conversion
|
||||
- Implement option to store photo thumbnails in filesystem instead of DB
|
||||
|
||||
Bugfixes
|
||||
- Fix category widget when using articles
|
||||
- Fix live update not triggering in mod search
|
||||
- Fix encoded URLs in code blocks
|
||||
- Fix wiki headers not escaped
|
||||
- Fix possible xchan protocol confusion in new_contact()
|
||||
- Fix xchan_url not displayed if xchan_addr not available
|
||||
- Fix suggestion ordering in mod directory
|
||||
- Fix event attachment delivery to zot6
|
||||
|
||||
Addons
|
||||
- pubcrawl: improve friendica compatibility by adding the nonstandard diaspora:guid field
|
||||
- pubcrawl: initial suport for events
|
||||
- pubcrawl: improve permalink detection
|
||||
- flashcards: fix moving learn buttons if viewport sizes changes
|
||||
- flashcards: Move card details to the bottom of a card
|
||||
- upgrade_info: provide links to changelog
|
||||
- photocache: do not save filename for cached photos
|
||||
- pubcrawl: save local comment activitypub payload in iconfig to be used for relay
|
||||
- flashcards: UI improvements in box settings
|
||||
- pubcrawl: implement profile update messages
|
||||
- pubcrawl: use URI instead of object for actor
|
||||
- flashcards: fix jumping sync button
|
||||
- pubcrawl: add threaded comments support
|
||||
- pubcrawl: ignore target encoding errors
|
||||
- pubcrawl: format photo items for activitypub
|
||||
|
||||
|
||||
Hubzilla 4.0.3 (2019-04-26)
|
||||
- Add attachments to zot6 event objects
|
||||
- Add zot6 to federated transports
|
||||
|
@ -5,6 +5,8 @@ namespace Zotlabs\Lib;
|
||||
use Zotlabs\Daemon\Master;
|
||||
use Zotlabs\Zot6\HTTPSig;
|
||||
|
||||
require_once('include/event.php');
|
||||
|
||||
class Activity {
|
||||
|
||||
static function encode_object($x) {
|
||||
@ -14,8 +16,10 @@ class Activity {
|
||||
$x = json_decode($x,true);
|
||||
}
|
||||
|
||||
if(is_array($x) && array_key_exists('asld',$x)) {
|
||||
$x = $x['asld'];
|
||||
if(is_array($x)) {
|
||||
|
||||
if(array_key_exists('asld',$x)) {
|
||||
return $x['asld'];
|
||||
}
|
||||
|
||||
if($x['type'] === ACTIVITY_OBJ_PERSON) {
|
||||
@ -36,6 +40,7 @@ class Activity {
|
||||
if($x['type'] === ACTIVITY_OBJ_PHOTO) {
|
||||
return self::fetch_image($x);
|
||||
}
|
||||
}
|
||||
|
||||
return $x;
|
||||
|
||||
@ -151,7 +156,7 @@ class Activity {
|
||||
'type' => 'Image',
|
||||
'id' => $x['id'],
|
||||
'name' => $x['title'],
|
||||
'content' => bbcode($x['body']),
|
||||
'content' => bbcode($x['body'], [ 'cache' => true ]),
|
||||
'source' => [ 'mediaType' => 'text/bbcode', 'content' => $x['body'] ],
|
||||
'published' => datetime_convert('UTC','UTC',$x['created'],ATOM_TIME),
|
||||
'updated' => datetime_convert('UTC','UTC', $x['edited'],ATOM_TIME),
|
||||
@ -181,14 +186,24 @@ class Activity {
|
||||
$y = [
|
||||
'type' => 'Event',
|
||||
'id' => z_root() . '/event/' . $ev['event_hash'],
|
||||
'summary' => bbcode($ev['summary']),
|
||||
'summary' => bbcode($ev['summary'], [ 'cache' => true ]),
|
||||
// RFC3339 Section 4.3
|
||||
'startTime' => (($ev['adjust']) ? datetime_convert('UTC','UTC',$ev['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtstart'],'Y-m-d\\TH:i:s-00:00')),
|
||||
'content' => bbcode($ev['description']),
|
||||
'location' => [ 'type' => 'Place', 'content' => bbcode($ev['location']) ],
|
||||
'content' => bbcode($ev['description'], [ 'cache' => true ]),
|
||||
'location' => [ 'type' => 'Place', 'content' => bbcode($ev['location'], [ 'cache' => true ]) ],
|
||||
'source' => [ 'content' => format_event_bbcode($ev), 'mediaType' => 'text/bbcode' ],
|
||||
'actor' => $actor,
|
||||
];
|
||||
if(! $ev['nofinish']) {
|
||||
$y['endTime'] = (($ev['adjust']) ? datetime_convert('UTC','UTC',$ev['dtend'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtend'],'Y-m-d\\TH:i:s-00:00'));
|
||||
}
|
||||
|
||||
// copy attachments from the passed object - these are already formatted for ActivityStreams
|
||||
|
||||
if($x['attachment']) {
|
||||
$y['attachment'] = $x['attachment'];
|
||||
}
|
||||
|
||||
if($actor) {
|
||||
return $y;
|
||||
}
|
||||
@ -279,8 +294,12 @@ class Activity {
|
||||
$ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME);
|
||||
if($i['created'] !== $i['edited'])
|
||||
$ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME);
|
||||
if ($i['expires'] <= NULL_DATE) {
|
||||
$ret['expires'] = datetime_convert('UTC','UTC',$i['expires'],ATOM_TIME);
|
||||
}
|
||||
|
||||
if($i['app']) {
|
||||
$ret['instrument'] = [ 'type' => 'Service', 'name' => $i['app'] ];
|
||||
$ret['generator'] = [ 'type' => 'Application', 'name' => $i['app'] ];
|
||||
}
|
||||
if($i['location'] || $i['coord']) {
|
||||
$ret['location'] = [ 'type' => 'Place' ];
|
||||
@ -297,15 +316,15 @@ class Activity {
|
||||
$ret['attributedTo'] = $i['author']['xchan_url'];
|
||||
|
||||
if($i['id'] != $i['parent']) {
|
||||
$ret['inReplyTo'] = ((strpos($i['parent_mid'],'http') === 0) ? $i['parent_mid'] : z_root() . '/item/' . urlencode($i['parent_mid']));
|
||||
$ret['inReplyTo'] = ((strpos($i['thr_parent'],'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent']));
|
||||
}
|
||||
|
||||
if($i['mimetype'] === 'text/bbcode') {
|
||||
if($i['title'])
|
||||
$ret['name'] = bbcode($i['title']);
|
||||
$ret['name'] = bbcode($i['title'], [ 'cache' => true ]);
|
||||
if($i['summary'])
|
||||
$ret['summary'] = bbcode($i['summary']);
|
||||
$ret['content'] = bbcode($i['body']);
|
||||
$ret['summary'] = bbcode($i['summary'], [ 'cache' => true ]);
|
||||
$ret['content'] = bbcode($i['body'], [ 'cache' => true ]);
|
||||
$ret['source'] = [ 'content' => $i['body'], 'mediaType' => 'text/bbcode' ];
|
||||
}
|
||||
|
||||
@ -398,7 +417,7 @@ class Activity {
|
||||
$ret = [];
|
||||
|
||||
if($item['attach']) {
|
||||
$atts = json_decode($item['attach'],true);
|
||||
$atts = ((is_array($item['attach'])) ? $item['attach'] : json_decode($item['attach'],true));
|
||||
if($atts) {
|
||||
foreach($atts as $att) {
|
||||
if(strpos($att['type'],'image')) {
|
||||
@ -457,20 +476,26 @@ class Activity {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if($i['verb'] === ACTIVITY_FRIEND) {
|
||||
// Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note
|
||||
$ret['obj_type'] = ACTIVITY_OBJ_NOTE;
|
||||
$ret['obj'] = [];
|
||||
}
|
||||
|
||||
$ret['type'] = self::activity_mapper($i['verb']);
|
||||
|
||||
|
||||
$ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid']));
|
||||
|
||||
if($i['title'])
|
||||
$ret['name'] = html2plain(bbcode($i['title']));
|
||||
$ret['name'] = html2plain(bbcode($i['title'], [ 'cache' => true ]));
|
||||
|
||||
if($i['summary'])
|
||||
$ret['summary'] = bbcode($i['summary']);
|
||||
$ret['summary'] = bbcode($i['summary'], [ 'cache' => true ]);
|
||||
|
||||
if($ret['type'] === 'Announce') {
|
||||
$tmp = preg_replace('/\[share(.*?)\[\/share\]/ism',EMPTY_STR, $i['body']);
|
||||
$ret['content'] = bbcode($tmp);
|
||||
$ret['content'] = bbcode($tmp, [ 'cache' => true ]);
|
||||
$ret['source'] = [
|
||||
'content' => $i['body'],
|
||||
'mediaType' => 'text/bbcode'
|
||||
@ -481,7 +506,7 @@ class Activity {
|
||||
if($i['created'] !== $i['edited'])
|
||||
$ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME);
|
||||
if($i['app']) {
|
||||
$ret['instrument'] = [ 'type' => 'Service', 'name' => $i['app'] ];
|
||||
$ret['generator'] = [ 'type' => 'Application', 'name' => $i['app'] ];
|
||||
}
|
||||
if($i['location'] || $i['coord']) {
|
||||
$ret['location'] = [ 'type' => 'Place' ];
|
||||
@ -496,7 +521,7 @@ class Activity {
|
||||
}
|
||||
|
||||
if($i['id'] != $i['parent']) {
|
||||
$ret['inReplyTo'] = ((strpos($i['parent_mid'],'http') === 0) ? $i['parent_mid'] : z_root() . '/item/' . urlencode($i['parent_mid']));
|
||||
$ret['inReplyTo'] = ((strpos($i['thr_parent'],'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent']));
|
||||
$reply = true;
|
||||
|
||||
if($i['item_private']) {
|
||||
@ -553,6 +578,7 @@ class Activity {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
if($i['target']) {
|
||||
if(! is_array($i['target'])) {
|
||||
$i['target'] = json_decode($i['target'],true);
|
||||
@ -697,7 +723,7 @@ class Activity {
|
||||
// Reactions will just map to normal activities
|
||||
|
||||
if(strpos($verb,ACTIVITY_REACT) !== false)
|
||||
return 'Create';
|
||||
return 'emojiReaction';
|
||||
if(strpos($verb,ACTIVITY_MOOD) !== false)
|
||||
return 'Create';
|
||||
|
||||
@ -1287,6 +1313,12 @@ class Activity {
|
||||
elseif($act->obj['updated']) {
|
||||
$s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']);
|
||||
}
|
||||
if ($act->data['expires']) {
|
||||
$s['expires'] = datetime_convert('UTC','UTC',$act->data['expires']);
|
||||
}
|
||||
elseif ($act->obj['expires']) {
|
||||
$s['expires'] = datetime_convert('UTC','UTC',$act->obj['expires']);
|
||||
}
|
||||
|
||||
if(! $s['created'])
|
||||
$s['created'] = datetime_convert();
|
||||
@ -1305,13 +1337,13 @@ class Activity {
|
||||
$s['verb'] = ACTIVITY_POST;
|
||||
$s['obj_type'] = ACTIVITY_OBJ_NOTE;
|
||||
|
||||
$instrument = $act->get_property_obj('instrument');
|
||||
if(! $instrument)
|
||||
$instrument = $act->get_property_obj('instrument',$act->obj);
|
||||
$generator = $act->get_property_obj('generator');
|
||||
if(! $generator)
|
||||
$generator = $act->get_property_obj('generator',$act->obj);
|
||||
|
||||
if($instrument && array_key_exists('type',$instrument)
|
||||
&& $instrument['type'] === 'Service' && array_key_exists('name',$instrument)) {
|
||||
$s['app'] = escape_tags($instrument['name']);
|
||||
if($generator && array_key_exists('type',$generator)
|
||||
&& in_array($generator['type'], [ 'Application','Service' ] ) && array_key_exists('name',$generator)) {
|
||||
$s['app'] = escape_tags($generator['name']);
|
||||
}
|
||||
|
||||
if($channel['channel_system']) {
|
||||
@ -1471,9 +1503,15 @@ class Activity {
|
||||
elseif($act->obj['updated']) {
|
||||
$s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']);
|
||||
}
|
||||
if ($act->data['expires']) {
|
||||
$s['expires'] = datetime_convert('UTC','UTC',$act->data['expires']);
|
||||
}
|
||||
elseif ($act->obj['expires']) {
|
||||
$s['expires'] = datetime_convert('UTC','UTC',$act->obj['expires']);
|
||||
}
|
||||
|
||||
|
||||
if(in_array($act->type, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept' ])) {
|
||||
if(in_array($act->type, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'emojiReaction' ])) {
|
||||
|
||||
$response_activity = true;
|
||||
|
||||
@ -1514,6 +1552,9 @@ class Activity {
|
||||
if($act->type === 'Announce') {
|
||||
$content['content'] = sprintf( t('🔁 Repeated %1$s\'s %2$s'), $mention, $act->obj['type']);
|
||||
}
|
||||
if ($act->type === 'emojiReaction') {
|
||||
$content['content'] = (($act->tgt && $act->tgt['type'] === 'Image') ? '[img=32x32]' . $act->tgt['url'] . '[/img]' : '&#x' . $act->tgt['name'] . ';');
|
||||
}
|
||||
}
|
||||
|
||||
if(! $s['created'])
|
||||
@ -1565,14 +1606,14 @@ class Activity {
|
||||
$s['obj'] = $act->obj;
|
||||
}
|
||||
|
||||
$instrument = $act->get_property_obj('instrument');
|
||||
if((! $instrument) && (! $response_activity)) {
|
||||
$instrument = $act->get_property_obj('instrument',$act->obj);
|
||||
$generator = $act->get_property_obj('generator');
|
||||
if((! $generator) && (! $response_activity)) {
|
||||
$generator = $act->get_property_obj('generator',$act->obj);
|
||||
}
|
||||
|
||||
if($instrument && array_key_exists('type',$instrument)
|
||||
&& $instrument['type'] === 'Service' && array_key_exists('name',$instrument)) {
|
||||
$s['app'] = escape_tags($instrument['name']);
|
||||
if($generator && array_key_exists('type',$generator)
|
||||
&& in_array($generator['type'], [ 'Application', 'Service' ] ) && array_key_exists('name',$generator)) {
|
||||
$s['app'] = escape_tags($generator['name']);
|
||||
}
|
||||
|
||||
|
||||
|
@ -70,7 +70,7 @@ class Apps {
|
||||
'Channel Home',
|
||||
'View Profile',
|
||||
'Photos',
|
||||
'Events',
|
||||
'Calendar',
|
||||
'Directory',
|
||||
'Search',
|
||||
'Help',
|
||||
@ -342,7 +342,7 @@ class Apps {
|
||||
'Channel Home' => t('Channel Home'),
|
||||
'View Profile' => t('View Profile'),
|
||||
'Photos' => t('Photos'),
|
||||
'Events' => t('Events'),
|
||||
'Calendar' => t('Calendar'),
|
||||
'Directory' => t('Directory'),
|
||||
'Help' => t('Help'),
|
||||
'Mail' => t('Mail'),
|
||||
@ -363,7 +363,6 @@ class Apps {
|
||||
'Privacy Groups' => t('Privacy Groups'),
|
||||
'Notifications' => t('Notifications'),
|
||||
'Order Apps' => t('Order Apps'),
|
||||
'CalDAV' => t('CalDAV'),
|
||||
'CardDAV' => t('CardDAV'),
|
||||
'Channel Sources' => t('Channel Sources'),
|
||||
'Guest Access' => t('Guest Access'),
|
||||
|
@ -58,10 +58,15 @@ class DB_Upgrade {
|
||||
|
||||
|
||||
$c = new $cls();
|
||||
|
||||
$retval = $c->run();
|
||||
|
||||
if($retval != UPDATE_SUCCESS) {
|
||||
|
||||
|
||||
$source = t('Source code of failed update: ') . "\n\n" . @file_get_contents('Zotlabs/Update/' . $s . '.php');
|
||||
|
||||
|
||||
// Prevent sending hundreds of thousands of emails by creating
|
||||
// a lockfile.
|
||||
|
||||
@ -86,7 +91,9 @@ class DB_Upgrade {
|
||||
'$sitename' => \App::$config['system']['sitename'],
|
||||
'$siteurl' => z_root(),
|
||||
'$update' => $x,
|
||||
'$error' => sprintf( t('Update %s failed. See error logs.'), $x)
|
||||
'$error' => sprintf( t('Update %s failed. See error logs.'), $x),
|
||||
'$baseurl' => z_root(),
|
||||
'$source' => $source
|
||||
]
|
||||
)
|
||||
]
|
||||
|
@ -2766,7 +2766,7 @@ class Libzot {
|
||||
|
||||
$profile['description'] = $p[0]['pdesc'];
|
||||
$profile['birthday'] = $p[0]['dob'];
|
||||
if(($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'],$e['channel_timezone'])) !== ''))
|
||||
if(($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'],'UTC')) !== ''))
|
||||
$profile['next_birthday'] = $bd;
|
||||
|
||||
if($age = age($p[0]['dob'],$e['channel_timezone'],''))
|
||||
|
@ -38,6 +38,7 @@ class ThreadItem {
|
||||
|
||||
$this->data = $data;
|
||||
$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
|
||||
$this->threaded = get_config('system','thread_allow');
|
||||
|
||||
$observer = \App::get_observer();
|
||||
|
||||
@ -305,6 +306,7 @@ class ThreadItem {
|
||||
if($this->is_commentable() && $observer) {
|
||||
$like = array( t("I like this \x28toggle\x29"), t("like"));
|
||||
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
|
||||
$reply_to = array( t("Reply on this comment"), t("reply"), t("Reply to"));
|
||||
}
|
||||
|
||||
if ($shareable) {
|
||||
@ -331,7 +333,6 @@ class ThreadItem {
|
||||
if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
|
||||
$is_new = true;
|
||||
|
||||
|
||||
localize_item($item);
|
||||
|
||||
$body = prepare_body($item,true);
|
||||
@ -347,10 +348,6 @@ class ThreadItem {
|
||||
$comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children );
|
||||
$list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : '');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$children = $this->get_children();
|
||||
|
||||
$has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false);
|
||||
@ -373,13 +370,15 @@ class ThreadItem {
|
||||
'text' => strip_tags($body['html']),
|
||||
'id' => $this->get_id(),
|
||||
'mid' => $item['mid'],
|
||||
'parent' => $item['parent'],
|
||||
'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
|
||||
'isevent' => $isevent,
|
||||
'attend' => $attend,
|
||||
'consensus' => $consensus,
|
||||
'conlabels' => $conlabels,
|
||||
'canvote' => $canvote,
|
||||
'linktitle' => sprintf( t('View %s\'s profile - %s'), $profile_name, $item['author']['xchan_addr']),
|
||||
'olinktitle' => sprintf( t('View %s\'s profile - %s'), $this->get_owner_name(), $item['owner']['xchan_addr']),
|
||||
'linktitle' => sprintf( t('View %s\'s profile - %s'), $profile_name, (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url'])),
|
||||
'olinktitle' => sprintf( t('View %s\'s profile - %s'), $this->get_owner_name(), (($item['owner']['xchan_addr']) ? $item['owner']['xchan_addr'] : $item['owner']['xchan_url'])),
|
||||
'llink' => $item['llink'],
|
||||
'viewthread' => $viewthread,
|
||||
'to' => t('to'),
|
||||
@ -428,6 +427,8 @@ class ThreadItem {
|
||||
'emojis' => (($this->is_toplevel() && $this->is_commentable() && $observer && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
|
||||
'like' => $like,
|
||||
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
|
||||
'reply_to' => (((! $this->is_toplevel()) && feature_enabled($conv->get_profile_owner(),'reply_to')) ? $reply_to : ''),
|
||||
'top_hint' => t("Go to previous comment"),
|
||||
'share' => $share,
|
||||
'embed' => $embed,
|
||||
'rawmid' => $item['mid'],
|
||||
@ -442,7 +443,6 @@ class ThreadItem {
|
||||
'multidrop' => ((feature_enabled($conv->get_profile_owner(),'multi_delete')) ? $multidrop : ''),
|
||||
'dropdown_extras' => $dropdown_extras,
|
||||
// end toolbar buttons
|
||||
|
||||
'unseen_comments' => $unseen_comments,
|
||||
'comment_count' => $total_children,
|
||||
'comment_count_txt' => $comment_count_txt,
|
||||
@ -469,7 +469,8 @@ class ThreadItem {
|
||||
'wait' => t('Please wait'),
|
||||
'submid' => str_replace(['+','='], ['',''], base64_encode($item['mid'])),
|
||||
'thread_level' => $thread_level,
|
||||
'settings' => $settings
|
||||
'settings' => $settings,
|
||||
'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? $item['thr_parent'] : '')
|
||||
);
|
||||
|
||||
$arr = array('item' => $item, 'output' => $tmp_item);
|
||||
@ -814,7 +815,7 @@ class ThreadItem {
|
||||
'$anonname' => [ 'anonname', t('Your full name (required)') ],
|
||||
'$anonmail' => [ 'anonmail', t('Your email address (required)') ],
|
||||
'$anonurl' => [ 'anonurl', t('Your website URL (optional)') ],
|
||||
'$auto_save_draft' => $feature_auto_save_draft,
|
||||
'$auto_save_draft' => $feature_auto_save_draft
|
||||
));
|
||||
|
||||
return $comment_box;
|
||||
@ -869,4 +870,3 @@ class ThreadItem {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,46 @@ class Dbsync {
|
||||
$o = '';
|
||||
|
||||
if(argc() > 3 && intval(argv(3)) && argv(2) === 'mark') {
|
||||
// remove the old style config if it exists
|
||||
del_config('database', 'update_r' . intval(argv(3)));
|
||||
set_config('database', '_' . intval(argv(3)), 'success');
|
||||
if(intval(get_config('system','db_version')) < intval(argv(3)))
|
||||
set_config('system','db_version',intval(argv(3)));
|
||||
info( t('Update has been marked successful') . EOL);
|
||||
goaway(z_root() . '/admin/dbsync');
|
||||
}
|
||||
|
||||
if(argc() > 3 && intval(argv(3)) && argv(2) === 'verify') {
|
||||
|
||||
$s = '_' . intval(argv(3));
|
||||
$cls = '\\Zotlabs\Update\\' . $s ;
|
||||
if(class_exists($cls)) {
|
||||
$c = new $cls();
|
||||
if(method_exists($c,'verify')) {
|
||||
$retval = $c->verify();
|
||||
if($retval === UPDATE_FAILED) {
|
||||
$o .= sprintf( t('Verification of update %s failed. Check system logs.'), $s);
|
||||
}
|
||||
elseif($retval === UPDATE_SUCCESS) {
|
||||
$o .= sprintf( t('Update %s was successfully applied.'), $s);
|
||||
set_config('database',$s, 'success');
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Verifying update %s did not return a status. Unknown if it succeeded.'), $s);
|
||||
}
|
||||
else {
|
||||
$o .= sprintf( t('Update %s does not contain a verification function.'), $s );
|
||||
}
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Update function %s could not be found.'), $s);
|
||||
|
||||
return $o;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// remove the old style config if it exists
|
||||
del_config('database', 'update_r' . intval(argv(3)));
|
||||
set_config('database', '_' . intval(argv(3)), 'success');
|
||||
@ -28,14 +68,14 @@ class Dbsync {
|
||||
$c = new $cls();
|
||||
$retval = $c->run();
|
||||
if($retval === UPDATE_FAILED) {
|
||||
$o .= sprintf( t('Executing %s failed. Check system logs.'), $s);
|
||||
$o .= sprintf( t('Executing update procedure %s failed. Check system logs.'), $s);
|
||||
}
|
||||
elseif($retval === UPDATE_SUCCESS) {
|
||||
$o .= sprintf( t('Update %s was successfully applied.'), $s);
|
||||
set_config('database',$s, 'success');
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Update %s did not return a status. Unknown if it succeeded.'), $s);
|
||||
$o .= sprintf( t('Update %s did not return a status. It cannot be determined if it was successful.'), $s);
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Update function %s could not be found.'), $s);
|
||||
@ -59,6 +99,7 @@ class Dbsync {
|
||||
'$banner' => t('Failed Updates'),
|
||||
'$desc' => '',
|
||||
'$mark' => t('Mark success (if update was manually applied)'),
|
||||
'$verify' => t('Attempt to verify this update if a verification procedure exists'),
|
||||
'$apply' => t('Attempt to execute this update step automatically'),
|
||||
'$failed' => $failed
|
||||
));
|
||||
|
57
Zotlabs/Module/Apschema.php
Normal file
57
Zotlabs/Module/Apschema.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
|
||||
class Apschema extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$base = z_root();
|
||||
|
||||
$arr = [
|
||||
'@context' => [
|
||||
'zot' => z_root() . '/apschema#',
|
||||
'id' => '@id',
|
||||
'type' => '@type',
|
||||
'commentPolicy' => 'as:commentPolicy',
|
||||
'meData' => 'zot:meData',
|
||||
'meDataType' => 'zot:meDataType',
|
||||
'meEncoding' => 'zot:meEncoding',
|
||||
'meAlgorithm' => 'zot:meAlgorithm',
|
||||
'meCreator' => 'zot:meCreator',
|
||||
'meSignatureValue' => 'zot:meSignatureValue',
|
||||
'locationAddress' => 'zot:locationAddress',
|
||||
'locationPrimary' => 'zot:locationPrimary',
|
||||
'locationDeleted' => 'zot:locationDeleted',
|
||||
'nomadicLocation' => 'zot:nomadicLocation',
|
||||
'nomadicHubs' => 'zot:nomadicHubs',
|
||||
'emojiReaction' => 'zot:emojiReaction',
|
||||
'expires' => 'zot:expires',
|
||||
|
||||
'magicEnv' => [
|
||||
'@id' => 'zot:magicEnv',
|
||||
'@type' => '@id'
|
||||
],
|
||||
|
||||
'nomadicLocations' => [
|
||||
'@id' => 'zot:nomadicLocations',
|
||||
'@type' => '@id'
|
||||
],
|
||||
|
||||
'ostatus' => 'http://ostatus.org#',
|
||||
'conversation' => 'ostatus:conversation'
|
||||
|
||||
]
|
||||
];
|
||||
|
||||
header('Content-Type: application/ld+json');
|
||||
echo json_encode($arr,JSON_UNESCAPED_SLASHES);
|
||||
killme();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -84,6 +84,7 @@ class Cal extends \Zotlabs\Web\Controller {
|
||||
'$module_url' => '/cal/' . $channel['channel_address'],
|
||||
'$modparams' => 2,
|
||||
'$lang' => \App::$language,
|
||||
'$timezone' => date_default_timezone_get(),
|
||||
'$first_day' => $first_day
|
||||
));
|
||||
|
||||
@ -215,7 +216,7 @@ class Cal extends \Zotlabs\Web\Controller {
|
||||
$sql_extra .= " and etype != 'birthday' ";
|
||||
|
||||
if (x($_GET,'id')){
|
||||
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
|
||||
$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 resource_id = event_hash where resource_type = 'event' and event.uid = %d and event.id = %d $sql_extra limit 1",
|
||||
intval($channel['channel_id']),
|
||||
intval($_GET['id'])
|
||||
@ -227,12 +228,12 @@ class Cal extends \Zotlabs\Web\Controller {
|
||||
// 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
|
||||
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' )) $sql_extra ",
|
||||
intval($channel['channel_id']),
|
||||
$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.event_hash = item.resource_id
|
||||
where item.resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored
|
||||
AND (( event.adjust = 0 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )
|
||||
OR ( event.adjust = 1 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )) ",
|
||||
intval(local_channel()),
|
||||
dbesc($start),
|
||||
dbesc($finish),
|
||||
dbesc($adjust_start),
|
||||
@ -267,15 +268,22 @@ class Cal extends \Zotlabs\Web\Controller {
|
||||
|
||||
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));
|
||||
$tz = get_iconfig($rr, 'event', 'timezone');
|
||||
|
||||
if(! $tz)
|
||||
$tz = 'UTC';
|
||||
|
||||
$rr['timezone'] = $tz;
|
||||
|
||||
$j = (($rr['adjust']) ? datetime_convert($tz,date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j'));
|
||||
$d = (($rr['adjust']) ? datetime_convert($tz,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'));
|
||||
$start = (($rr['adjust']) ? datetime_convert($tz,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'));
|
||||
$end = (($rr['adjust']) ? datetime_convert($tz,date_default_timezone_get(),$rr['dtend'], 'c') : datetime_convert('UTC','UTC',$rr['dtend'],'c'));
|
||||
}
|
||||
|
||||
|
||||
@ -302,7 +310,7 @@ class Cal extends \Zotlabs\Web\Controller {
|
||||
'start'=> $start,
|
||||
'end' => $end,
|
||||
'drop' => $drop,
|
||||
'allDay' => false,
|
||||
'allDay' => (($rr['adjust']) ? 0 : 1),
|
||||
'title' => $title,
|
||||
|
||||
'j' => $j,
|
||||
|
@ -133,10 +133,6 @@ class Cdav extends Controller {
|
||||
|
||||
logger('loggedin');
|
||||
|
||||
if((argv(1) == 'calendars') && (!Apps::system_app_installed(local_channel(), 'CalDAV'))) {
|
||||
killme();
|
||||
}
|
||||
|
||||
if((argv(1) == 'addressbooks') && (!Apps::system_app_installed(local_channel(), 'CardDAV'))) {
|
||||
killme();
|
||||
}
|
||||
@ -221,10 +217,6 @@ class Cdav extends Controller {
|
||||
if(! local_channel())
|
||||
return;
|
||||
|
||||
if((argv(1) === 'calendar') && (! Apps::system_app_installed(local_channel(), 'CalDAV'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if((argv(1) === 'addressbook') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) {
|
||||
return;
|
||||
}
|
||||
@ -279,11 +271,17 @@ class Cdav extends Controller {
|
||||
if(!cdav_perms($id[0],$calendars,true))
|
||||
return;
|
||||
|
||||
$timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : '');
|
||||
$tz = (($timezone) ? $timezone : date_default_timezone_get());
|
||||
|
||||
$allday = $_REQUEST['allday'];
|
||||
|
||||
$title = $_REQUEST['title'];
|
||||
$start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']);
|
||||
$start = datetime_convert($tz, 'UTC', $_REQUEST['dtstart']);
|
||||
$dtstart = new \DateTime($start);
|
||||
|
||||
if($_REQUEST['dtend']) {
|
||||
$end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']);
|
||||
$end = datetime_convert($tz, 'UTC', $_REQUEST['dtend']);
|
||||
$dtend = new \DateTime($end);
|
||||
}
|
||||
$description = $_REQUEST['description'];
|
||||
@ -309,16 +307,23 @@ class Cdav extends Controller {
|
||||
'DTSTART' => $dtstart
|
||||
]
|
||||
]);
|
||||
|
||||
if($dtend) {
|
||||
$vcalendar->VEVENT->add('DTEND', $dtend);
|
||||
$vcalendar->VEVENT->DTEND['TZID'] = App::$timezone;
|
||||
if($allday)
|
||||
$vcalendar->VEVENT->DTEND['VALUE'] = 'DATE';
|
||||
else
|
||||
$vcalendar->VEVENT->DTEND['TZID'] = $tz;
|
||||
}
|
||||
if($description)
|
||||
$vcalendar->VEVENT->add('DESCRIPTION', $description);
|
||||
if($location)
|
||||
$vcalendar->VEVENT->add('LOCATION', $location);
|
||||
|
||||
$vcalendar->VEVENT->DTSTART['TZID'] = App::$timezone;
|
||||
if($allday)
|
||||
$vcalendar->VEVENT->DTSTART['VALUE'] = 'DATE';
|
||||
else
|
||||
$vcalendar->VEVENT->DTSTART['TZID'] = $tz;
|
||||
|
||||
$calendarData = $vcalendar->serialize();
|
||||
|
||||
@ -356,12 +361,17 @@ class Cdav extends Controller {
|
||||
if(!cdav_perms($id[0],$calendars,true))
|
||||
return;
|
||||
|
||||
$timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : '');
|
||||
$tz = (($timezone) ? $timezone : date_default_timezone_get());
|
||||
|
||||
$allday = $_REQUEST['allday'];
|
||||
|
||||
$uri = $_REQUEST['uri'];
|
||||
$title = $_REQUEST['title'];
|
||||
$start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']);
|
||||
$start = datetime_convert($tz, 'UTC', $_REQUEST['dtstart']);
|
||||
$dtstart = new \DateTime($start);
|
||||
if($_REQUEST['dtend']) {
|
||||
$end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']);
|
||||
$end = datetime_convert($tz, 'UTC', $_REQUEST['dtend']);
|
||||
$dtend = new \DateTime($end);
|
||||
}
|
||||
$description = $_REQUEST['description'];
|
||||
@ -373,12 +383,23 @@ class Cdav extends Controller {
|
||||
|
||||
if($title)
|
||||
$vcalendar->VEVENT->SUMMARY = $title;
|
||||
if($dtstart)
|
||||
if($dtstart) {
|
||||
$vcalendar->VEVENT->DTSTART = $dtstart;
|
||||
if($dtend)
|
||||
if($allday)
|
||||
$vcalendar->VEVENT->DTSTART['VALUE'] = 'DATE';
|
||||
else
|
||||
$vcalendar->VEVENT->DTSTART['TZID'] = $tz;
|
||||
}
|
||||
if($dtend) {
|
||||
$vcalendar->VEVENT->DTEND = $dtend;
|
||||
if($allday)
|
||||
$vcalendar->VEVENT->DTEND['VALUE'] = 'DATE';
|
||||
else
|
||||
$vcalendar->VEVENT->DTEND['TZID'] = $tz;
|
||||
}
|
||||
else
|
||||
unset($vcalendar->VEVENT->DTEND);
|
||||
|
||||
if($description)
|
||||
$vcalendar->VEVENT->DESCRIPTION = $description;
|
||||
if($location)
|
||||
@ -414,11 +435,16 @@ class Cdav extends Controller {
|
||||
if(!cdav_perms($id[0],$calendars,true))
|
||||
return;
|
||||
|
||||
$timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : '');
|
||||
$tz = (($timezone) ? $timezone : date_default_timezone_get());
|
||||
|
||||
$allday = $_REQUEST['allday'];
|
||||
|
||||
$uri = $_REQUEST['uri'];
|
||||
$start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']);
|
||||
$start = datetime_convert($tz, 'UTC', $_REQUEST['dtstart']);
|
||||
$dtstart = new \DateTime($start);
|
||||
if($_REQUEST['dtend']) {
|
||||
$end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']);
|
||||
$end = datetime_convert($tz, 'UTC', $_REQUEST['dtend']);
|
||||
$dtend = new \DateTime($end);
|
||||
}
|
||||
|
||||
@ -428,13 +454,20 @@ class Cdav extends Controller {
|
||||
|
||||
if($dtstart) {
|
||||
$vcalendar->VEVENT->DTSTART = $dtstart;
|
||||
if($allday)
|
||||
$vcalendar->VEVENT->DTSTART['VALUE'] = 'DATE';
|
||||
else
|
||||
$vcalendar->VEVENT->DTSTART['TZID'] = $tz;
|
||||
}
|
||||
if($dtend) {
|
||||
$vcalendar->VEVENT->DTEND = $dtend;
|
||||
if($allday)
|
||||
$vcalendar->VEVENT->DTEND['VALUE'] = 'DATE';
|
||||
else
|
||||
$vcalendar->VEVENT->DTEND['TZID'] = $tz;
|
||||
}
|
||||
else {
|
||||
else
|
||||
unset($vcalendar->VEVENT->DTEND);
|
||||
}
|
||||
|
||||
$calendarData = $vcalendar->serialize();
|
||||
|
||||
@ -762,16 +795,27 @@ class Cdav extends Controller {
|
||||
//Import calendar or addressbook
|
||||
if(($_FILES) && array_key_exists('userfile',$_FILES) && intval($_FILES['userfile']['size']) && $_REQUEST['target']) {
|
||||
|
||||
$src = @file_get_contents($_FILES['userfile']['tmp_name']);
|
||||
$src = $_FILES['userfile']['tmp_name'];
|
||||
|
||||
if($src) {
|
||||
|
||||
if($_REQUEST['c_upload']) {
|
||||
if($_REQUEST['target'] == 'channel_calendar') {
|
||||
$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);
|
||||
return;
|
||||
}
|
||||
|
||||
$id = explode(':', $_REQUEST['target']);
|
||||
$ext = 'ics';
|
||||
$table = 'calendarobjects';
|
||||
$column = 'calendarid';
|
||||
$objects = new \Sabre\VObject\Splitter\ICalendar($src);
|
||||
$objects = new \Sabre\VObject\Splitter\ICalendar(@file_get_contents($src));
|
||||
$profile = \Sabre\VObject\Node::PROFILE_CALDAV;
|
||||
$backend = new \Sabre\CalDAV\Backend\PDO($pdo);
|
||||
}
|
||||
@ -781,7 +825,7 @@ class Cdav extends Controller {
|
||||
$ext = 'vcf';
|
||||
$table = 'cards';
|
||||
$column = 'addressbookid';
|
||||
$objects = new \Sabre\VObject\Splitter\VCard($src);
|
||||
$objects = new \Sabre\VObject\Splitter\VCard(@file_get_contents($src));
|
||||
$profile = \Sabre\VObject\Node::PROFILE_CARDDAV;
|
||||
$backend = new \Sabre\CardDAV\Backend\PDO($pdo);
|
||||
}
|
||||
@ -847,15 +891,6 @@ class Cdav extends Controller {
|
||||
if(!local_channel())
|
||||
return;
|
||||
|
||||
if((argv(1) === 'calendar') && (! Apps::system_app_installed(local_channel(), 'CalDAV'))) {
|
||||
//Do not display any associated widgets at this point
|
||||
App::$pdl = '';
|
||||
|
||||
$o = '<b>' . t('CalDAV App') . ' (' . t('Not Installed') . '):</b><br>';
|
||||
$o .= t('CalDAV capable calendar');
|
||||
return $o;
|
||||
}
|
||||
|
||||
if((argv(1) === 'addressbook') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) {
|
||||
//Do not display any associated widgets at this point
|
||||
App::$pdl = '';
|
||||
@ -884,13 +919,13 @@ class Cdav extends Controller {
|
||||
}
|
||||
|
||||
if(argv(1) === 'calendar') {
|
||||
nav_set_selected('CalDAV');
|
||||
nav_set_selected('Calendar');
|
||||
$caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
|
||||
$calendars = $caldavBackend->getCalendarsForUser($principalUri);
|
||||
}
|
||||
|
||||
//Display calendar(s) here
|
||||
if(argc() == 2 && argv(1) === 'calendar') {
|
||||
if(argc() <= 3 && argv(1) === 'calendar') {
|
||||
|
||||
head_add_css('/library/fullcalendar/packages/core/main.min.css');
|
||||
head_add_css('/library/fullcalendar/packages/daygrid/main.min.css');
|
||||
@ -905,10 +940,72 @@ class Cdav extends Controller {
|
||||
head_add_js('/library/fullcalendar/packages/list/main.min.js');
|
||||
|
||||
$sources = '';
|
||||
$resource_id = '';
|
||||
$resource = null;
|
||||
|
||||
if(argc() == 3)
|
||||
$resource_id = argv(2);
|
||||
|
||||
if($resource_id) {
|
||||
$r = q("SELECT event.*, item.author_xchan, item.owner_xchan, item.plink, item.id as item_id FROM event LEFT JOIN item ON event.event_hash = item.resource_id
|
||||
WHERE event.uid = %d AND event.event_hash = '%s' LIMIT 1",
|
||||
intval(local_channel()),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
if($r) {
|
||||
xchan_query($r);
|
||||
$r = fetch_post_tags($r,true);
|
||||
|
||||
$tz = get_iconfig($r[0], 'event', 'timezone');
|
||||
if(! $tz)
|
||||
$tz = 'UTC';
|
||||
|
||||
$r[0]['timezone'] = $tz;
|
||||
$r[0]['dtstart'] = (($r[0]['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $r[0]['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $r[0]['dtstart'], 'c'));
|
||||
$r[0]['dtend'] = (($r[0]['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $r[0]['dtend'], 'c') : datetime_convert('UTC', 'UTC' ,$r[0]['dtend'], 'c'));
|
||||
|
||||
$r[0]['plink'] = [$r[0]['plink'], t('Link to source')];
|
||||
|
||||
$resource = $r[0];
|
||||
|
||||
$catsenabled = feature_enabled(local_channel(),'categories');
|
||||
$categories = '';
|
||||
if($catsenabled){
|
||||
if($r[0]['term']) {
|
||||
$cats = get_terms_oftype($r[0]['term'], TERM_CATEGORY);
|
||||
foreach ($cats as $cat) {
|
||||
if(strlen($categories))
|
||||
$categories .= ', ';
|
||||
$categories .= $cat['term'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($r[0]['dismissed'] == 0) {
|
||||
q("UPDATE event SET dismissed = 1 WHERE event.uid = %d AND event.event_hash = '%s'",
|
||||
intval(local_channel()),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
$color = (($calendar['{http://apple.com/ns/ical/}calendar-color']) ? $calendar['{http://apple.com/ns/ical/}calendar-color'] : '#6cad39');
|
||||
$sharer = (($calendar['share-access'] == 3) ? $calendar['{urn:ietf:params:xml:ns:caldav}calendar-description'] : '');
|
||||
$switch = get_pconfig(local_channel(), 'cdav_calendar', $calendar['id'][0]);
|
||||
if($switch) {
|
||||
@ -933,17 +1030,31 @@ class Cdav extends Controller {
|
||||
$first_day = feature_enabled(local_channel(), 'cal_first_day');
|
||||
$first_day = (($first_day) ? $first_day : 0);
|
||||
|
||||
$title = ['title', t('Event title')];
|
||||
$title = ['title', t('Event title') ];
|
||||
$dtstart = ['dtstart', t('Start date and time')];
|
||||
$dtend = ['dtend', t('End date and time')];
|
||||
$timezone_select = ['timezone_select' , t('Timezone:'), date_default_timezone_get(), '', get_timezones()];
|
||||
|
||||
$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 = (($resource_id) ? $resource : $perm_defaults);
|
||||
|
||||
$o .= replace_macros(get_markup_template('cdav_calendar.tpl'), [
|
||||
'$sources' => $sources,
|
||||
'$color' => $color,
|
||||
'$lang' => App::$language,
|
||||
'$timezone' => App::$timezone,
|
||||
'$timezone' => date_default_timezone_get(),
|
||||
'$first_day' => $first_day,
|
||||
'$prev' => t('Previous'),
|
||||
'$next' => t('Next'),
|
||||
@ -955,6 +1066,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 +1074,28 @@ 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.')
|
||||
'$create' => t('Create'),
|
||||
'$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'),
|
||||
|
||||
'$resource' => json_encode($resource),
|
||||
'$categories' => $categories,
|
||||
'$timezone_select' => ((feature_enabled(local_channel(),'event_tz_select')) ? $timezone_select : '')
|
||||
]);
|
||||
|
||||
return $o;
|
||||
@ -995,8 +1124,8 @@ class Cdav extends Controller {
|
||||
$filters['comp-filters'][0]['time-range']['end'] = $end;
|
||||
|
||||
$uris = $caldavBackend->calendarQuery($id, $filters);
|
||||
|
||||
if($uris) {
|
||||
|
||||
$objects = $caldavBackend->getMultipleCalendarObjects($id, $uris);
|
||||
foreach($objects as $object) {
|
||||
|
||||
@ -1015,30 +1144,33 @@ class Cdav extends Controller {
|
||||
$dtend = (string)$vevent->DTEND;
|
||||
$description = (string)$vevent->DESCRIPTION;
|
||||
$location = (string)$vevent->LOCATION;
|
||||
$timezone = (string)$vevent->DTSTART['TZID'];
|
||||
$timezone_str = (string)$vevent->DTSTART['TZID'];
|
||||
$rw = ((cdav_perms($id[0],$calendars,true)) ? true : false);
|
||||
$editable = $rw ? true : false;
|
||||
$recurrent = ((isset($vevent->{'RECURRENCE-ID'})) ? true : false);
|
||||
|
||||
if($recurrent) {
|
||||
$editable = false;
|
||||
$timezone = $recurrent_timezone;
|
||||
$timezone_str = $recurrent_timezone;
|
||||
}
|
||||
|
||||
$allDay = false;
|
||||
// Try to get an usable olson format timezone
|
||||
$timezone_obj = \Sabre\VObject\TimeZoneUtil::getTimeZone($timezone_str, $vcalendar);
|
||||
$timezone = $timezone_obj->getName();
|
||||
|
||||
// allDay event rules
|
||||
if(!strpos($dtstart, 'T') && !strpos($dtend, 'T'))
|
||||
$allDay = true;
|
||||
if(strpos($dtstart, 'T000000') && strpos($dtend, 'T000000'))
|
||||
$allDay = true;
|
||||
// If we got nothing fallback to UTC
|
||||
if(! $timezone)
|
||||
$timezone = 'UTC';
|
||||
|
||||
$allDay = (((string)$vevent->DTSTART['VALUE'] == 'DATE') ? true : false);
|
||||
|
||||
$events[] = [
|
||||
'calendar_id' => $id,
|
||||
'uri' => $object['uri'],
|
||||
'title' => $title,
|
||||
'start' => datetime_convert($timezone, $timezone, $dtstart, 'c'),
|
||||
'end' => (($dtend) ? datetime_convert($timezone, $timezone, $dtend, 'c') : ''),
|
||||
'timezone' => $timezone,
|
||||
'start' => datetime_convert($timezone, date_default_timezone_get(), $dtstart, 'c'),
|
||||
'end' => (($dtend) ? datetime_convert($timezone, date_default_timezone_get(), $dtend, 'c') : ''),
|
||||
'description' => $description,
|
||||
'location' => $location,
|
||||
'allDay' => $allDay,
|
||||
@ -1053,7 +1185,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))
|
||||
@ -1312,12 +1444,13 @@ class Cdav extends Controller {
|
||||
$caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
|
||||
$properties = [
|
||||
'{DAV:}displayname' => t('Default Calendar'),
|
||||
'{http://apple.com/ns/ical/}calendar-color' => '#3a87ad',
|
||||
'{http://apple.com/ns/ical/}calendar-color' => '#6cad39',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name']
|
||||
];
|
||||
|
||||
$id = $caldavBackend->createCalendar($uri, 'default', $properties);
|
||||
set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1);
|
||||
set_pconfig(local_channel(), 'cdav_calendar' , 'channel_calendar', 1);
|
||||
|
||||
//create default addressbook
|
||||
$carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo);
|
||||
|
@ -310,10 +310,6 @@ class Channel extends Controller {
|
||||
$sql_extra2 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery2))));
|
||||
}
|
||||
|
||||
if($datequery || $datequery2) {
|
||||
$sql_extra2 .= " and item.item_thread_top != 0 ";
|
||||
}
|
||||
|
||||
if($order === 'post')
|
||||
$ordering = "created";
|
||||
else
|
||||
@ -342,7 +338,7 @@ class Channel extends Controller {
|
||||
AND (abook.abook_blocked = 0 or abook.abook_flags is null)
|
||||
AND item.item_wall = 1 AND item.item_thread_top = 1
|
||||
$sql_extra $sql_extra2
|
||||
ORDER BY $ordering DESC $pager_sql ",
|
||||
ORDER BY $ordering DESC, item_id $pager_sql ",
|
||||
intval(App::$profile['profile_uid'])
|
||||
);
|
||||
}
|
||||
|
441
Zotlabs/Module/Channel_calendar.php
Normal file
441
Zotlabs/Module/Channel_calendar.php
Normal file
@ -0,0 +1,441 @@
|
||||
<?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;
|
||||
|
||||
$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();
|
||||
|
||||
// only allow editing your own events.
|
||||
if(($xchan) && ($xchan !== get_observer_hash()))
|
||||
return;
|
||||
|
||||
$timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : '');
|
||||
$tz = (($timezone) ? $timezone : date_default_timezone_get());
|
||||
|
||||
$categories = escape_tags(trim($_POST['categories']));
|
||||
|
||||
$adjust = intval($_POST['adjust']);
|
||||
|
||||
$start = (($adjust) ? datetime_convert($tz, 'UTC', escape_tags($_REQUEST['dtstart'])) : datetime_convert('UTC', 'UTC', escape_tags($_REQUEST['dtstart'])));
|
||||
$finish = (($adjust) ? datetime_convert($tz, 'UTC', escape_tags($_REQUEST['dtend'])) : datetime_convert('UTC', 'UTC', escape_tags($_REQUEST['dtend'])));
|
||||
|
||||
$summary = escape_tags(trim($_POST['summary']));
|
||||
$desc = escape_tags(trim($_POST['desc']));
|
||||
$location = escape_tags(trim($_POST['location']));
|
||||
$type = escape_tags(trim($_POST['type']));
|
||||
|
||||
// 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.
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
if((! $summary) || (! $start)) {
|
||||
notice( t('Event title and start time are required.') . EOL);
|
||||
if(intval($_REQUEST['preview'])) {
|
||||
echo( t('Unable to generate preview.'));
|
||||
}
|
||||
killme();
|
||||
}
|
||||
|
||||
$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();
|
||||
}
|
||||
else {
|
||||
$created = $edited = datetime_convert();
|
||||
$acl->set_from_array($_POST);
|
||||
}
|
||||
|
||||
$post_tags = array();
|
||||
$channel = \App::get_channel();
|
||||
$ac = $acl->get();
|
||||
|
||||
$str_contact_allow = $ac['allow_cid'];
|
||||
$str_group_allow = $ac['allow_gid'];
|
||||
$str_contact_deny = $ac['deny_cid'];
|
||||
$str_group_deny = $ac['deny_gid'];
|
||||
|
||||
$private = $acl->is_private();
|
||||
|
||||
require_once('include/text.php');
|
||||
$results = linkify_tags($desc, local_channel());
|
||||
|
||||
if($results) {
|
||||
// Set permissions based on tag replacements
|
||||
set_linkified_perms($results, $str_contact_allow, $str_group_allow, local_channel(), false, $private);
|
||||
|
||||
foreach($results as $result) {
|
||||
$success = $result['success'];
|
||||
if($success['replaced']) {
|
||||
$post_tags[] = array(
|
||||
'uid' => local_channel(),
|
||||
'ttype' => $success['termtype'],
|
||||
'otype' => TERM_OBJ_POST,
|
||||
'term' => $success['term'],
|
||||
'url' => $success['url']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen($categories)) {
|
||||
$cats = explode(',',$categories);
|
||||
foreach($cats as $cat) {
|
||||
$post_tags[] = array(
|
||||
'uid' => local_channel(),
|
||||
'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'] = 0;
|
||||
$datarray['uid'] = local_channel();
|
||||
$datarray['account'] = get_account_id();
|
||||
$datarray['event_xchan'] = $channel['channel_hash'];
|
||||
$datarray['allow_cid'] = $str_contact_allow;
|
||||
$datarray['allow_gid'] = $str_group_allow;
|
||||
$datarray['deny_cid'] = $str_contact_deny;
|
||||
$datarray['deny_gid'] = $str_group_deny;
|
||||
$datarray['private'] = intval($private);
|
||||
$datarray['id'] = $event_id;
|
||||
$datarray['created'] = $created;
|
||||
$datarray['edited'] = $edited;
|
||||
$datarray['timezone'] = $tz;
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\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';
|
||||
$export = false;
|
||||
$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 && argv(1) === 'export') {
|
||||
$export = true;
|
||||
}
|
||||
if(argc() > 2 && intval(argv(1)) && intval(argv(2))) {
|
||||
$mode = 'view';
|
||||
}
|
||||
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();
|
||||
|
||||
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, item.id as item_id
|
||||
from event left join item on item.resource_id = event.event_hash
|
||||
where item.resource_type = 'event' and event.uid = %d and event.id = %d limit 1",
|
||||
intval(local_channel()),
|
||||
intval($_GET['id'])
|
||||
);
|
||||
}
|
||||
elseif($export) {
|
||||
$r = q("SELECT event.*, item.id as item_id
|
||||
from event left join item on item.resource_id = event.event_hash
|
||||
where event.uid = %d and event.dtstart > '%s' and event.dtend > event.dtstart",
|
||||
intval(local_channel()),
|
||||
dbesc(NULL_DATE)
|
||||
);
|
||||
}
|
||||
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.event_hash = item.resource_id
|
||||
where item.resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored
|
||||
AND (( event.adjust = 0 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )
|
||||
OR ( event.adjust = 1 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )) ",
|
||||
intval(local_channel()),
|
||||
dbesc($start),
|
||||
dbesc($finish),
|
||||
dbesc($adjust_start),
|
||||
dbesc($adjust_finish)
|
||||
);
|
||||
}
|
||||
|
||||
if($r && ! $export) {
|
||||
xchan_query($r);
|
||||
$r = fetch_post_tags($r,true);
|
||||
$r = sort_by_date($r);
|
||||
}
|
||||
|
||||
$events = [];
|
||||
|
||||
if($r) {
|
||||
|
||||
foreach($r as $rr) {
|
||||
|
||||
$tz = get_iconfig($rr, 'event', 'timezone');
|
||||
|
||||
if(! $tz)
|
||||
$tz = 'UTC';
|
||||
|
||||
$start = (($rr['adjust']) ? datetime_convert($tz, 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($tz, date_default_timezone_get(), $rr['dtend'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtend'], 'c'));
|
||||
}
|
||||
|
||||
$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'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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'),'','');
|
||||
|
||||
$events[] = array(
|
||||
'calendar_id' => 'channel_calendar',
|
||||
'rw' => true,
|
||||
'id'=>$rr['id'],
|
||||
'uri' => $rr['event_hash'],
|
||||
'timezone' => $tz,
|
||||
'start'=> $start,
|
||||
'end' => $end,
|
||||
'drop' => $drop,
|
||||
'allDay' => (($rr['adjust']) ? 0 : 1),
|
||||
'title' => htmlentities($rr['summary'], ENT_COMPAT, 'UTF-8', false),
|
||||
'editable' => $edit ? true : false,
|
||||
'item' => $rr,
|
||||
'plink' => [$rr['plink'], t('Link to source')],
|
||||
'description' => htmlentities($rr['description'], ENT_COMPAT, 'UTF-8', false),
|
||||
'location' => htmlentities($rr['location'], ENT_COMPAT, 'UTF-8', false),
|
||||
'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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -69,6 +69,9 @@ class Cover_photo extends \Zotlabs\Web\Controller {
|
||||
build_sync_packet($channel['channel_id'],array('file' => array($sync)));
|
||||
}
|
||||
|
||||
// Update directory in background
|
||||
\Zotlabs\Daemon\Master::Summon(array('Directory',$channel['channel_id']));
|
||||
|
||||
goaway(z_root() . '/cover_photo');
|
||||
}
|
||||
|
||||
@ -129,7 +132,7 @@ class Cover_photo extends \Zotlabs\Web\Controller {
|
||||
if(file_exists($tmp_name)) {
|
||||
$base_image = $r[0];
|
||||
$gis = getimagesize($tmp_name);
|
||||
logger('gis: ' . print_r($gis,true));
|
||||
logger('gis: ' . print_r($gis,true), LOGGER_DEBUG);
|
||||
$base_image['width'] = $gis[0];
|
||||
$base_image['height'] = $gis[1];
|
||||
$base_image['content'] = @file_get_contents($tmp_name);
|
||||
@ -190,24 +193,17 @@ logger('gis: ' . print_r($gis,true));
|
||||
'filename' => $base_image['filename'],
|
||||
'album' => t('Cover Photos'),
|
||||
'os_path' => $base_image['os_path'],
|
||||
'display_path' => $base_image['display_path']
|
||||
'display_path' => $base_image['display_path'],
|
||||
'photo_usage' => PHOTO_COVER
|
||||
];
|
||||
|
||||
$p['imgscale'] = 7;
|
||||
$p['photo_usage'] = PHOTO_COVER;
|
||||
|
||||
$r1 = $im->save($p);
|
||||
$r1 = $im->storeThumbnail($p, PHOTO_RES_COVER_1200);
|
||||
|
||||
$im->doScaleImage(850,310);
|
||||
$p['imgscale'] = 8;
|
||||
|
||||
$r2 = $im->save($p);
|
||||
|
||||
$r2 = $im->storeThumbnail($p, PHOTO_RES_COVER_850);
|
||||
|
||||
$im->doScaleImage(425,160);
|
||||
$p['imgscale'] = 9;
|
||||
|
||||
$r3 = $im->save($p);
|
||||
$r3 = $im->storeThumbnail($p, PHOTO_RES_COVER_425);
|
||||
|
||||
if($r1 === false || $r2 === false || $r3 === false) {
|
||||
// if one failed, delete them all so we can start over.
|
||||
@ -216,6 +212,17 @@ logger('gis: ' . print_r($gis,true));
|
||||
dbesc($base_image['resource_id']),
|
||||
local_channel()
|
||||
);
|
||||
|
||||
$x = q("SELECT content FROM photo WHERE resource_id = '%s' AND uid = %d AND os_storage = 1 AND imgscale >= 7",
|
||||
dbesc($base_image['resource_id']),
|
||||
local_channel()
|
||||
);
|
||||
if($x) {
|
||||
foreach($x as $xx) {
|
||||
@unlink(dbunescbin($xx['content']));
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -225,6 +232,8 @@ logger('gis: ' . print_r($gis,true));
|
||||
if($sync)
|
||||
build_sync_packet($channel['channel_id'],array('file' => array($sync)));
|
||||
|
||||
// Update directory in background
|
||||
\Zotlabs\Daemon\Master::Summon(array('Directory',$channel['channel_id']));
|
||||
}
|
||||
else
|
||||
notice( t('Unable to process image') . EOL);
|
||||
|
@ -104,7 +104,13 @@ class Directory extends \Zotlabs\Web\Controller {
|
||||
|
||||
if($suggest) {
|
||||
|
||||
$r = suggestion_query(local_channel(),get_observer_hash());
|
||||
// the directory options have no effect in suggestion mode
|
||||
|
||||
$globaldir = 1;
|
||||
$safe_mode = 1;
|
||||
$type = 0;
|
||||
|
||||
$r = suggestion_query(local_channel(),get_observer_hash(),0,60);
|
||||
|
||||
if(! $r) {
|
||||
notice( t('No default suggestions were found.') . EOL);
|
||||
@ -213,11 +219,16 @@ class Directory extends \Zotlabs\Web\Controller {
|
||||
|
||||
if($j['results']) {
|
||||
|
||||
$results = $j['results'];
|
||||
if($suggest) {
|
||||
$results = self::reorder_results($results,$addresses);
|
||||
}
|
||||
|
||||
$entries = array();
|
||||
|
||||
$photo = 'thumb';
|
||||
|
||||
foreach($j['results'] as $rr) {
|
||||
foreach($results as $rr) {
|
||||
|
||||
$profile_link = chanlink_url($rr['url']);
|
||||
|
||||
@ -438,5 +449,22 @@ class Directory extends \Zotlabs\Web\Controller {
|
||||
return $o;
|
||||
}
|
||||
|
||||
static public function reorder_results($results,$suggests) {
|
||||
|
||||
if(! $suggests)
|
||||
return $results;
|
||||
|
||||
$out = [];
|
||||
foreach($suggests as $k => $v) {
|
||||
foreach($results as $rv) {
|
||||
if($k == $rv['address']) {
|
||||
$out[intval($v)] = $rv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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/' . $itm[0]['resource_id']);
|
||||
//goaway(z_root() . '/events/' . $itm[0]['resource_id'] . '?expandform=1');
|
||||
}
|
||||
|
||||
$owner_uid = $itm[0]['uid'];
|
||||
|
@ -41,24 +41,44 @@ class Embedphotos extends \Zotlabs\Web\Controller {
|
||||
json_return_and_die(array('errormsg' => 'Error retrieving link ' . $href, 'status' => false));
|
||||
}
|
||||
$resource_id = array_pop(explode('/', $href));
|
||||
$r = q("SELECT obj from item where resource_type = 'photo' and resource_id = '%s' limit 1",
|
||||
dbesc($resource_id)
|
||||
$x = self::photolink($resource_id);
|
||||
if($x)
|
||||
json_return_and_die(array('status' => true, 'photolink' => $x, 'resource_id' => $resource_id));
|
||||
json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static function photolink($resource) {
|
||||
$channel = \App::get_channel();
|
||||
$output = EMPTY_STR;
|
||||
if($channel) {
|
||||
$resolution = ((feature_enabled($channel['channel_id'],'large_photos')) ? 1 : 2);
|
||||
$r = q("select mimetype, height, width from photo where resource_id = '%s' and $resolution = %d and uid = %d limit 1",
|
||||
dbesc($resource),
|
||||
intval($resolution),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
if (!$r) {
|
||||
json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false));
|
||||
}
|
||||
$obj = json_decode($r[0]['obj'], true);
|
||||
if (x($obj, 'body')) {
|
||||
$photolink = $obj['body'];
|
||||
} elseif (x($obj, 'bbcode')) {
|
||||
$photolink = $obj['bbcode'];
|
||||
} else {
|
||||
json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false));
|
||||
}
|
||||
json_return_and_die(array('status' => true, 'photolink' => $photolink, 'resource_id' => $resource_id));
|
||||
if(! $r)
|
||||
return $output;
|
||||
|
||||
if($r[0]['mimetype'] === 'image/jpeg')
|
||||
$ext = '.jpg';
|
||||
elseif($r[0]['mimetype'] === 'image/png')
|
||||
$ext = '.png';
|
||||
elseif($r[0]['mimetype'] === 'image/gif')
|
||||
$ext = '.gif';
|
||||
else
|
||||
$ext = EMPTY_STR;
|
||||
|
||||
$output = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $resource . ']' .
|
||||
'[zmg=' . $r[0]['width'] . 'x' . $r[0]['height'] . ']' . z_root() . '/photo/' . $resource . '-' . $resolution . $ext . '[/zmg][/zrl]';
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get photos from an album.
|
||||
*
|
||||
|
@ -35,7 +35,6 @@ class Getfile extends \Zotlabs\Web\Controller {
|
||||
$sig = $_POST['signature'];
|
||||
$resource = $_POST['resource'];
|
||||
$revision = intval($_POST['revision']);
|
||||
$resolution = (-1);
|
||||
|
||||
if(! $hash)
|
||||
killme();
|
||||
@ -81,10 +80,15 @@ class Getfile extends \Zotlabs\Web\Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
if(substr($resource,-2,1) == '-') {
|
||||
if(isset($_POST['resolution']))
|
||||
$resolution = intval($_POST['resolution']);
|
||||
elseif(substr($resource,-2,1) == '-') {
|
||||
$resolution = intval(substr($resource,-1,1));
|
||||
$resource = substr($resource,0,-2);
|
||||
}
|
||||
else {
|
||||
$resolution = (-1);
|
||||
}
|
||||
|
||||
$slop = intval(get_pconfig($channel['channel_id'],'system','getfile_time_slop'));
|
||||
if($slop < 1)
|
||||
@ -106,9 +110,10 @@ class Getfile extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
|
||||
if($resolution > 0) {
|
||||
$r = q("select * from photo where resource_id = '%s' and uid = %d limit 1",
|
||||
$r = q("SELECT * FROM photo WHERE resource_id = '%s' AND uid = %d AND imgscale = %d LIMIT 1",
|
||||
dbesc($resource),
|
||||
intval($channel['channel_id'])
|
||||
intval($channel['channel_id']),
|
||||
$resolution
|
||||
);
|
||||
if($r) {
|
||||
header('Content-type: ' . $r[0]['mimetype']);
|
||||
|
@ -491,7 +491,7 @@ class Like extends \Zotlabs\Web\Controller {
|
||||
|
||||
$arr['item_flags'] = $item_flags;
|
||||
$arr['item_wall'] = $item_wall;
|
||||
$arr['parent_mid'] = (($extended_like) ? $mid : $item['mid']);
|
||||
$arr['parent_mid'] = (($extended_like) ? $arr['mid'] : $item['mid']);
|
||||
$arr['owner_xchan'] = (($extended_like) ? $ch[0]['xchan_hash'] : $thread_owner['xchan_hash']);
|
||||
$arr['author_xchan'] = $observer['xchan_hash'];
|
||||
|
||||
@ -546,7 +546,7 @@ class Like extends \Zotlabs\Web\Controller {
|
||||
dbesc($observer['xchan_hash']),
|
||||
dbesc($ch[0]['channel_hash']),
|
||||
intval($post_id),
|
||||
dbesc($mid),
|
||||
dbesc($arr['mid']),
|
||||
dbesc($activity),
|
||||
dbesc(($tgttype)? $tgttype : $objtype),
|
||||
dbesc($obj_id),
|
||||
@ -555,7 +555,7 @@ class Like extends \Zotlabs\Web\Controller {
|
||||
$r = q("select * from likes where liker = '%s' and likee = '%s' and i_mid = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' ",
|
||||
dbesc($observer['xchan_hash']),
|
||||
dbesc($ch[0]['channel_hash']),
|
||||
dbesc($mid),
|
||||
dbesc($arr['mid']),
|
||||
dbesc($activity),
|
||||
dbesc(($tgttype)? $tgttype : $objtype),
|
||||
dbesc($obj_id)
|
||||
|
@ -69,6 +69,14 @@ class Linkinfo extends \Zotlabs\Web\Controller {
|
||||
killme();
|
||||
}
|
||||
if(stripos($type,'video/') !== false) {
|
||||
$thumb = self::get_video_poster($url);
|
||||
if($thumb) {
|
||||
if ($zrl)
|
||||
echo $br . '[zvideo poster=\'' . $thumb . '\']' . $url . '[/zvideo]' . $br;
|
||||
else
|
||||
echo $br . '[video poster=\'' . $thumb . '\']' . $url . '[/video]' . $br;
|
||||
killme();
|
||||
}
|
||||
if($zrl)
|
||||
echo $br . '[zvideo]' . $url . '[/zvideo]' . $br;
|
||||
else
|
||||
@ -217,6 +225,41 @@ class Linkinfo extends \Zotlabs\Web\Controller {
|
||||
return($complete);
|
||||
}
|
||||
|
||||
public static function get_video_poster($url) {
|
||||
|
||||
if(strpos($url,z_root() . '/cloud/') === false) {
|
||||
return EMPTY_STR;
|
||||
}
|
||||
$m = parse_url($url,PHP_URL_PATH);
|
||||
if($m) {
|
||||
// strip leading '/cloud/'
|
||||
$m = substr($m,7);
|
||||
}
|
||||
$nick = substr($m,0,strpos($m,'/'));
|
||||
$p = substr($m,strpos($m,'/')+1);
|
||||
|
||||
// get the channel to check permissions
|
||||
|
||||
$u = channelx_by_nick($nick);
|
||||
|
||||
if($u && $p) {
|
||||
|
||||
$sql_extra = permissions_sql(intval($u['channel_id']));
|
||||
|
||||
$r = q("select hash, content from attach where display_path = '%s' and uid = %d and os_storage = 1 $sql_extra limit 1",
|
||||
dbesc($p),
|
||||
intval($u['channel_id'])
|
||||
);
|
||||
if($r) {
|
||||
$path = dbunescbin($r[0]['content']);
|
||||
if($path && @file_exists($path . '.thumb')) {
|
||||
return z_root() . '/poster/' . $nick . '/' . $r[0]['hash'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return EMPTY_STR;
|
||||
}
|
||||
|
||||
|
||||
public static function parseurl_getsiteinfo($url) {
|
||||
$siteinfo = array();
|
||||
|
@ -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 = '';
|
||||
@ -83,7 +81,7 @@ class Photo extends \Zotlabs\Web\Controller {
|
||||
$modified = strtotime($r[0]['edited'] . "Z");
|
||||
$mimetype = $r[0]['mimetype'];
|
||||
if(intval($r[0]['os_storage']))
|
||||
$data = file_get_contents($data);
|
||||
$data = file_get_contents(dbunescbin($r[0]['content']));
|
||||
else
|
||||
$data = dbunescbin($r[0]['content']);
|
||||
}
|
||||
@ -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,19 @@ class Photo extends \Zotlabs\Web\Controller {
|
||||
$allowed = (-1);
|
||||
if($u === PHOTO_CACHE) {
|
||||
// Validate cache
|
||||
if($cache_mode['on']) {
|
||||
$cache = array(
|
||||
'resid' => $photo,
|
||||
'status' => false
|
||||
);
|
||||
if($cache_mode['on'])
|
||||
call_hooks('cache_url_hook', $cache);
|
||||
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();
|
||||
goaway($url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,38 +219,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 +272,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.
|
||||
|
@ -239,95 +239,53 @@ class Photos extends \Zotlabs\Web\Controller {
|
||||
intval($page_owner_uid)
|
||||
);
|
||||
if(count($r)) {
|
||||
$d = (($r[0]['os_storage']) ? @file_get_contents(dbunescbin($r[0]['content'])) : dbunescbin($r[0]['content']));
|
||||
$ph = photo_factory($d, $r[0]['mimetype']);
|
||||
|
||||
$ph = photo_factory(@file_get_contents(dbunescbin($r[0]['content'])), $r[0]['mimetype']);
|
||||
if($ph->is_valid()) {
|
||||
$rotate_deg = ( (intval($_POST['rotate']) == 1) ? 270 : 90 );
|
||||
$ph->rotate($rotate_deg);
|
||||
|
||||
$width = $ph->getWidth();
|
||||
$height = $ph->getHeight();
|
||||
$edited = datetime_convert();
|
||||
|
||||
if(intval($r[0]['os_storage'])) {
|
||||
@file_put_contents($r[0]['content'],$ph->imageString());
|
||||
$data = $r[0]['content'];
|
||||
$fsize = @filesize($r[0]['content']);
|
||||
q("update attach set filesize = %d where hash = '%s' and uid = %d",
|
||||
intval($fsize),
|
||||
q("update attach set filesize = %d, edited = '%s' where hash = '%s' and uid = %d",
|
||||
strlen($ph->imageString()),
|
||||
dbescdate($edited),
|
||||
dbesc($resource_id),
|
||||
intval($page_owner_uid)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$data = $ph->imageString();
|
||||
$fsize = strlen($data);
|
||||
}
|
||||
|
||||
$x = q("update photo set edited = '%s', content = '%s', filesize = %d, height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 0",
|
||||
dbesc(datetime_convert()),
|
||||
dbescbin($data),
|
||||
intval($fsize),
|
||||
intval($height),
|
||||
intval($width),
|
||||
dbesc($resource_id),
|
||||
intval($page_owner_uid)
|
||||
);
|
||||
$ph->saveImage(dbunescbin($r[0]['content']));
|
||||
|
||||
$arr = [
|
||||
'aid' => get_account_id(),
|
||||
'uid' => intval($page_owner_uid),
|
||||
'resource_id' => dbesc($resource_id),
|
||||
'filename' => $r[0]['filename'],
|
||||
'imgscale' => 0,
|
||||
'album' => $r[0]['album'],
|
||||
'os_path' => $r[0]['os_path'],
|
||||
'os_storage' => 1,
|
||||
'os_syspath' => dbunescbin($r[0]['content']),
|
||||
'display_path' => $r[0]['display_path'],
|
||||
'photo_usage' => PHOTO_NORMAL,
|
||||
'edited' => dbescdate($edited)
|
||||
];
|
||||
|
||||
$ph->save($arr);
|
||||
|
||||
unset($arr['os_syspath']);
|
||||
|
||||
if($width > 1024 || $height > 1024)
|
||||
$ph->scaleImage(1024);
|
||||
|
||||
$width = $ph->getWidth();
|
||||
$height = $ph->getHeight();
|
||||
$data = $ph->imageString();
|
||||
$fsize = strlen($data);
|
||||
|
||||
$x = q("update photo set edited = '%s', content = '%s', filesize = %d, height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 1",
|
||||
dbesc(datetime_convert()),
|
||||
dbescbin($data),
|
||||
intval($fsize),
|
||||
intval($height),
|
||||
intval($width),
|
||||
dbesc($resource_id),
|
||||
intval($page_owner_uid)
|
||||
);
|
||||
|
||||
$ph->storeThumbnail($arr, PHOTO_RES_1024);
|
||||
|
||||
if($width > 640 || $height > 640)
|
||||
$ph->scaleImage(640);
|
||||
|
||||
$width = $ph->getWidth();
|
||||
$height = $ph->getHeight();
|
||||
$data = $ph->imageString();
|
||||
$fsize = strlen($data);
|
||||
|
||||
$x = q("update photo set edited = '%s', content = '%s', filesize = %d, height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 2",
|
||||
dbesc(datetime_convert()),
|
||||
dbescbin($data),
|
||||
intval($fsize),
|
||||
intval($height),
|
||||
intval($width),
|
||||
dbesc($resource_id),
|
||||
intval($page_owner_uid)
|
||||
);
|
||||
|
||||
$ph->storeThumbnail($arr, PHOTO_RES_640);
|
||||
|
||||
if($width > 320 || $height > 320)
|
||||
$ph->scaleImage(320);
|
||||
|
||||
$width = $ph->getWidth();
|
||||
$height = $ph->getHeight();
|
||||
$data = $ph->imageString();
|
||||
$fsize = strlen($data);
|
||||
|
||||
$x = q("update photo set edited = '%s', content = '%s', filesize = %d, height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 3",
|
||||
dbesc(datetime_convert()),
|
||||
dbescbin($data),
|
||||
intval($fsize),
|
||||
intval($height),
|
||||
intval($width),
|
||||
dbesc($resource_id),
|
||||
intval($page_owner_uid)
|
||||
);
|
||||
$ph->storeThumbnail($arr, PHOTO_RES_320);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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/' . $rr['event_hash'],
|
||||
'name' => $rr['xchan_name'],
|
||||
'addr' => $rr['xchan_addr'],
|
||||
'url' => $rr['xchan_url'],
|
||||
|
37
Zotlabs/Module/Poster.php
Normal file
37
Zotlabs/Module/Poster.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
require_once('include/security.php');
|
||||
|
||||
class Poster extends Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$nick = argv(1);
|
||||
$hash = argv(2);
|
||||
|
||||
if(! ($nick && $hash)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$u = channelx_by_nick($nick);
|
||||
|
||||
$sql_extra = permissions_sql(intval($u['channel_id']));
|
||||
|
||||
$r = q("select content from attach where hash = '%s' and uid = %d and os_storage = 1 $sql_extra limit 1",
|
||||
dbesc($hash),
|
||||
intval($u['channel_id'])
|
||||
);
|
||||
if($r) {
|
||||
$path = dbunescbin($r[0]['content']);
|
||||
if($path && @file_exists($path . '.thumb')) {
|
||||
header('Content-Type: image/jpeg');
|
||||
echo file_get_contents($path . '.thumb');
|
||||
killme();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
@ -119,38 +144,47 @@ class Profile_photo extends \Zotlabs\Web\Controller {
|
||||
'filename' => $base_image['filename'],
|
||||
'album' => t('Profile Photos'),
|
||||
'os_path' => $base_image['os_path'],
|
||||
'display_path' => $base_image['display_path']
|
||||
'display_path' => $base_image['display_path'],
|
||||
'photo_usage' => PHOTO_PROFILE,
|
||||
'edited' => dbescdate($base_image['edited'])
|
||||
];
|
||||
|
||||
$p['imgscale'] = PHOTO_RES_PROFILE_300;
|
||||
$p['photo_usage'] = (($is_default_profile) ? PHOTO_PROFILE : PHOTO_NORMAL);
|
||||
|
||||
$r1 = $im->save($p);
|
||||
$r1 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_300);
|
||||
|
||||
$im->scaleImage(80);
|
||||
$p['imgscale'] = PHOTO_RES_PROFILE_80;
|
||||
|
||||
$r2 = $im->save($p);
|
||||
$r2 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_80);
|
||||
|
||||
$im->scaleImage(48);
|
||||
$p['imgscale'] = PHOTO_RES_PROFILE_48;
|
||||
|
||||
$r3 = $im->save($p);
|
||||
$r3 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_48);
|
||||
|
||||
if($r1 === false || $r2 === false || $r3 === false) {
|
||||
// if one failed, delete them all so we can start over.
|
||||
notice( t('Image resize failed.') . EOL );
|
||||
$x = q("delete from photo where resource_id = '%s' and uid = %d and imgscale in ( %d, %d, %d ) ",
|
||||
$x = q("delete from photo where resource_id = '%s' and uid = %d and imgscale in ( %d, %d, %d )",
|
||||
dbesc($base_image['resource_id']),
|
||||
local_channel(),
|
||||
intval(PHOTO_RES_PROFILE_300),
|
||||
intval(PHOTO_RES_PROFILE_80),
|
||||
intval(PHOTO_RES_PROFILE_48)
|
||||
);
|
||||
return;
|
||||
|
||||
$x = q("SELECT content FROM photo WHERE resource_id = '%s' AND uid = %d AND os_storage = 1 AND imgscale IN ( %d, %d, %d )",
|
||||
dbesc($base_image['resource_id']),
|
||||
local_channel(),
|
||||
intval(PHOTO_RES_PROFILE_300),
|
||||
intval(PHOTO_RES_PROFILE_80),
|
||||
intval(PHOTO_RES_PROFILE_48)
|
||||
);
|
||||
if($x) {
|
||||
foreach($x as $xx) {
|
||||
@unlink(dbunescbin($xx['content']));
|
||||
}
|
||||
}
|
||||
|
||||
$channel = \App::get_channel();
|
||||
return;
|
||||
}
|
||||
|
||||
// If setting for the default profile, unset the profile photo flag from any other photos I own
|
||||
|
||||
@ -198,7 +232,7 @@ class Profile_photo extends \Zotlabs\Web\Controller {
|
||||
$r = q("UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s'
|
||||
where xchan_hash = '%s'",
|
||||
dbesc($im->getType()),
|
||||
dbesc(datetime_convert()),
|
||||
dbescdate($base_image['edited']),
|
||||
dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']),
|
||||
dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']),
|
||||
dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']),
|
||||
@ -245,7 +279,7 @@ class Profile_photo extends \Zotlabs\Web\Controller {
|
||||
else {
|
||||
require_once('include/attach.php');
|
||||
|
||||
$res = attach_store(\App::get_channel(), get_observer_hash(), '', array('album' => t('Profile Photos'), 'hash' => $hash));
|
||||
$res = attach_store(\App::get_channel(), get_observer_hash(), '', array('album' => t('Profile Photos'), 'hash' => $hash, 'nosync' => true));
|
||||
|
||||
logger('attach_store: ' . print_r($res,true));
|
||||
}
|
||||
@ -353,20 +387,23 @@ class Profile_photo extends \Zotlabs\Web\Controller {
|
||||
|
||||
if($havescale) {
|
||||
// unset any existing profile photos
|
||||
$r = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND uid = %d",
|
||||
$x = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND uid = %d",
|
||||
intval(PHOTO_NORMAL),
|
||||
intval(PHOTO_PROFILE),
|
||||
intval(local_channel()));
|
||||
intval(local_channel())
|
||||
);
|
||||
|
||||
$r = q("UPDATE photo SET photo_usage = %d WHERE uid = %d AND resource_id = '%s'",
|
||||
$edited = datetime_convert();
|
||||
|
||||
$x = q("UPDATE photo SET photo_usage = %d, edited = '%s' WHERE uid = %d AND resource_id = '%s' AND imgscale > 0",
|
||||
intval(PHOTO_PROFILE),
|
||||
dbescdate($edited),
|
||||
intval(local_channel()),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
|
||||
$r = q("UPDATE xchan set xchan_photo_date = '%s'
|
||||
where xchan_hash = '%s'",
|
||||
dbesc(datetime_convert()),
|
||||
$x = q("UPDATE xchan SET xchan_photo_date = '%s' WHERE xchan_hash = '%s'",
|
||||
dbescdate($edited),
|
||||
dbesc($channel['xchan_hash'])
|
||||
);
|
||||
|
||||
@ -376,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');
|
||||
}
|
||||
|
||||
@ -457,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],
|
||||
|
@ -59,6 +59,14 @@ class React extends \Zotlabs\Web\Controller {
|
||||
$n['body'] = "\n\n[zmg=32x32]" . z_root() . '/images/emoji/' . $emoji . '.png[/zmg]' . "\n\n";
|
||||
$n['author_xchan'] = $channel['channel_hash'];
|
||||
|
||||
$n['tgt_type'] = 'Image';
|
||||
$n['target'] = [
|
||||
'type' => 'Image',
|
||||
'name' => $emoji,
|
||||
'url' => z_root() . '/images/emoji/' . $emoji . '.png'
|
||||
];
|
||||
|
||||
|
||||
$x = item_store($n);
|
||||
|
||||
retain_item($postid);
|
||||
|
@ -1,10 +1,11 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/channel.php');
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
require_once('include/security.php');
|
||||
|
||||
class Register extends \Zotlabs\Web\Controller {
|
||||
class Register extends Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
@ -40,6 +41,8 @@ class Register extends \Zotlabs\Web\Controller {
|
||||
|
||||
function post() {
|
||||
|
||||
check_form_security_token_redirectOnErr('/register', 'register');
|
||||
|
||||
$max_dailies = intval(get_config('system','max_daily_registrations'));
|
||||
if($max_dailies) {
|
||||
$r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s",
|
||||
@ -270,6 +273,7 @@ class Register extends \Zotlabs\Web\Controller {
|
||||
|
||||
$o = replace_macros(get_markup_template('register.tpl'), array(
|
||||
|
||||
'$form_security_token' => get_form_security_token("register"),
|
||||
'$title' => t('Registration'),
|
||||
'$reg_is' => $registration_is,
|
||||
'$registertext' => bbcode(get_config('system','register_text')),
|
||||
|
@ -39,7 +39,7 @@ class Search extends \Zotlabs\Web\Controller {
|
||||
|
||||
$o = '<div id="live-search"></div>' . "\r\n";
|
||||
|
||||
$o = '<div class="generic-content-wrapper-styled">' . "\r\n";
|
||||
$o .= '<div class="generic-content-wrapper-styled">' . "\r\n";
|
||||
|
||||
$o .= '<h3>' . t('Search') . '</h3>';
|
||||
|
||||
|
@ -36,7 +36,7 @@ class Calendar {
|
||||
'$rpath' => $rpath,
|
||||
'$action_url' => 'settings/' . $module,
|
||||
'$form_security_token' => get_form_security_token('settings_' . $module),
|
||||
'$title' => t('CalDAV Settings'),
|
||||
'$title' => t('Calendar Settings'),
|
||||
'$features' => process_module_features_get(local_channel(), $features),
|
||||
'$submit' => t('Submit')
|
||||
));
|
||||
|
@ -96,9 +96,26 @@ class Wall_attach extends \Zotlabs\Web\Controller {
|
||||
$s = "\n\n" . $r['body'] . "\n\n";
|
||||
}
|
||||
else {
|
||||
$s = "\n\n" . '[attachment]' . $r['data']['hash'] . ',' . $r['data']['revision'] . '[/attachment]' . "\n";
|
||||
if(strpos($r['data']['filetype'],'video') === 0) {
|
||||
// give a wee bit of time for the background thumbnail processor to do its thing
|
||||
// or else we'll never see a video poster
|
||||
sleep(3);
|
||||
$url = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path'];
|
||||
$thumb = Linkinfo::get_video_poster($url);
|
||||
if($thumb) {
|
||||
$s = "\n\n" . '[zvideo poster=\'' . $thumb . '\']' . $url . '[/zvideo]' . "\n\n";
|
||||
}
|
||||
else {
|
||||
$s = "\n\n" . '[zvideo]' . $url . '[/zvideo]' . "\n\n";
|
||||
}
|
||||
}
|
||||
if(strpos($r['data']['filetype'],'audio') === 0) {
|
||||
$url = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path'];
|
||||
echo "\n\n" . '[zaudio]' . $url . '[/zaudio]' . "\n\n";
|
||||
}
|
||||
|
||||
$s .= "\n\n" . '[attachment]' . $r['data']['hash'] . ',' . $r['data']['revision'] . '[/attachment]' . "\n";
|
||||
}
|
||||
|
||||
$sync = attach_export_data($channel,$r['data']['hash']);
|
||||
if($sync) {
|
||||
|
@ -293,9 +293,9 @@ class Wiki extends Controller {
|
||||
}
|
||||
|
||||
//$wikiheaderName = urldecode($wikiUrlName);
|
||||
$wikiheaderName = NativeWiki::name_decode($wikiUrlName);
|
||||
$wikiheaderName = escape_tags(NativeWiki::name_decode($wikiUrlName));
|
||||
//$wikiheaderPage = urldecode($pageUrlName);
|
||||
$wikiheaderPage = NativeWiki::name_decode($pageUrlName);
|
||||
$wikiheaderPage = escape_tags(NativeWiki::name_decode($pageUrlName));
|
||||
|
||||
$renamePage = (($wikiheaderPage === 'Home') ? '' : t('Rename page'));
|
||||
$sharePage = t('Share');
|
||||
@ -373,13 +373,13 @@ class Wiki extends Controller {
|
||||
|
||||
$placeholder = t('Short description of your changes (optional)');
|
||||
|
||||
$zrl = urlencode( z_root() . '/wiki/' . argv(1) . '/' . NativeWiki::name_encode($wikiUrlName) . '/' . NativeWiki::name_encode($pageUrlName) );
|
||||
$zrl = z_root() . '/wiki/' . argv(1) . '/' . NativeWiki::name_encode($wikiUrlName) . '/' . NativeWiki::name_encode($pageUrlName);
|
||||
$o .= replace_macros(get_markup_template('wiki.tpl'),array(
|
||||
'$wikiheaderName' => $wikiheaderName,
|
||||
'$wikiheaderPage' => $wikiheaderPage,
|
||||
'$renamePage' => $renamePage,
|
||||
'$sharePage' => $sharePage,
|
||||
'$shareLink' => '#^[zrl=' . $zrl . ']' . '[ ' . $owner['channel_name'] . ' ] ' . $wikiheaderName . ' - ' . $wikiheaderPage . '[/zrl]',
|
||||
'$shareLink' => urlencode('#^[zrl=' . $zrl . ']' . '[ ' . $owner['channel_name'] . ' ] ' . $wikiheaderName . ' - ' . $wikiheaderPage . '[/zrl]'),
|
||||
'$showPageControls' => $showPageControls,
|
||||
'$editOrSourceLabel' => (($showPageControls) ? t('Edit') : t('Source')),
|
||||
'$tools_label' => 'Page Tools',
|
||||
|
@ -448,6 +448,7 @@ abstract class PhotoDriver {
|
||||
$p['width'] = (($arr['width']) ? $arr['width'] : $this->getWidth());
|
||||
$p['height'] = (($arr['height']) ? $arr['height'] : $this->getHeight());
|
||||
$p['expires'] = (($arr['expires']) ? $arr['expires'] : gmdate('Y-m-d H:i:s', time() + get_config('system', 'photo_cache_time', 86400)));
|
||||
$p['profile'] = ((array_key_exists('profile', $arr)) ? intval($arr['profile']) : 0);
|
||||
|
||||
if(! intval($p['imgscale']))
|
||||
logger('save: ' . print_r($arr, true), LOGGER_DATA);
|
||||
@ -481,18 +482,49 @@ abstract class PhotoDriver {
|
||||
allow_gid = '%s',
|
||||
deny_cid = '%s',
|
||||
deny_gid = '%s',
|
||||
expires = '%s'
|
||||
expires = '%s',
|
||||
profile = %d
|
||||
where id = %d",
|
||||
intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($x[0]['id']));
|
||||
intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($p['profile']), intval($x[0]['id']));
|
||||
} else {
|
||||
$p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']);
|
||||
$r = q("INSERT INTO photo
|
||||
( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires )
|
||||
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']));
|
||||
( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires, profile )
|
||||
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($p['profile']));
|
||||
}
|
||||
logger('Photo save imgscale ' . $p['imgscale'] . ' returned ' . intval($r));
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stores thumbnail to database or filesystem.
|
||||
*
|
||||
* @param array $arr
|
||||
* @param scale int
|
||||
* @return boolean|array
|
||||
*/
|
||||
public function storeThumbnail($arr, $scale = 0) {
|
||||
|
||||
$arr['imgscale'] = $scale;
|
||||
|
||||
if(boolval(get_config('system','filesystem_storage_thumbnails', 0)) && $scale > 0) {
|
||||
$channel = channelx_by_n($arr['uid']);
|
||||
$arr['os_storage'] = 1;
|
||||
$arr['os_syspath'] = 'store/' . $channel['channel_address'] . '/' . $arr['os_path'] . '-' . $scale;
|
||||
if(! $this->saveImage($arr['os_syspath']))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
$arr['os_storage'] = 0;
|
||||
|
||||
if(! $this->save($arr)) {
|
||||
if(array_key_exists('os_syspath', $arr))
|
||||
@unlink($arr['os_syspath']);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
namespace Zotlabs\Render;
|
||||
|
||||
class SmartyInterface extends \Smarty {
|
||||
use Smarty;
|
||||
use App;
|
||||
|
||||
class SmartyInterface extends Smarty {
|
||||
|
||||
public $filename;
|
||||
|
||||
@ -16,26 +19,27 @@ class SmartyInterface extends \Smarty {
|
||||
// The order is thus very important here
|
||||
|
||||
$template_dirs = array('theme' => "view/theme/$thname/tpl/");
|
||||
if( x(\App::$theme_info,"extends") )
|
||||
if ( x(App::$theme_info,"extends") ) {
|
||||
$template_dirs = $template_dirs + array('extends' => "view/theme/" . \App::$theme_info["extends"] . "/tpl/");
|
||||
}
|
||||
$template_dirs = $template_dirs + array('base' => 'view/tpl/');
|
||||
$this->setTemplateDir($template_dirs);
|
||||
|
||||
$basecompiledir = \App::$config['system']['smarty3_folder'];
|
||||
$basecompiledir = App::$config['system']['smarty3_folder'];
|
||||
|
||||
$this->setCompileDir($basecompiledir.'/compiled/');
|
||||
$this->setConfigDir($basecompiledir.'/config/');
|
||||
$this->setCacheDir($basecompiledir.'/cache/');
|
||||
|
||||
$this->left_delimiter = \App::get_template_ldelim('smarty3');
|
||||
$this->right_delimiter = \App::get_template_rdelim('smarty3');
|
||||
$this->left_delimiter = App::get_template_ldelim('smarty3');
|
||||
$this->right_delimiter = App::get_template_rdelim('smarty3');
|
||||
|
||||
// Don't report errors so verbosely
|
||||
$this->error_reporting = E_ALL & (~E_NOTICE);
|
||||
}
|
||||
|
||||
function parsed($template = '') {
|
||||
if($template) {
|
||||
if ($template) {
|
||||
return $this->fetch('string:' . $template);
|
||||
}
|
||||
return $this->fetch('file:' . $this->filename);
|
||||
|
@ -2,28 +2,33 @@
|
||||
|
||||
namespace Zotlabs\Render;
|
||||
|
||||
use App;
|
||||
|
||||
|
||||
class SmartyTemplate implements TemplateEngine {
|
||||
|
||||
static $name ="smarty3";
|
||||
|
||||
public function __construct(){
|
||||
public function __construct() {
|
||||
|
||||
// Cannot use get_config() here because it is called during installation when there is no DB.
|
||||
// FIXME: this may leak private information such as system pathnames.
|
||||
|
||||
$basecompiledir = ((array_key_exists('smarty3_folder',\App::$config['system']))
|
||||
? \App::$config['system']['smarty3_folder'] : '');
|
||||
if (!$basecompiledir) $basecompiledir = str_replace('Zotlabs','',dirname(__dir__)) . "/" . TEMPLATE_BUILD_PATH;
|
||||
if (!is_dir($basecompiledir)) {
|
||||
$basecompiledir = ((array_key_exists('smarty3_folder', App::$config['system']))
|
||||
? App::$config['system']['smarty3_folder'] : '');
|
||||
if (! $basecompiledir) {
|
||||
$basecompiledir = str_replace('Zotlabs','',dirname(__dir__)) . "/" . TEMPLATE_BUILD_PATH;
|
||||
}
|
||||
if (! is_dir($basecompiledir)) {
|
||||
@os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true);
|
||||
if (!is_dir($basecompiledir)) {
|
||||
if (! is_dir($basecompiledir)) {
|
||||
echo "<b>ERROR:</b> folder <tt>$basecompiledir</tt> does not exist."; killme();
|
||||
}
|
||||
}
|
||||
if(!is_writable($basecompiledir)){
|
||||
if (! is_writable($basecompiledir)) {
|
||||
echo "<b>ERROR:</b> folder <tt>$basecompiledir</tt> must be writable by webserver."; killme();
|
||||
}
|
||||
\App::$config['system']['smarty3_folder'] = $basecompiledir;
|
||||
App::$config['system']['smarty3_folder'] = $basecompiledir;
|
||||
}
|
||||
|
||||
// TemplateEngine interface
|
||||
@ -31,18 +36,18 @@ class SmartyTemplate implements TemplateEngine {
|
||||
public function replace_macros($s, $r) {
|
||||
$template = '';
|
||||
|
||||
// these are available for use in all templates
|
||||
// macro or macros available for use in all templates
|
||||
|
||||
$r['$z_baseurl'] = z_root();
|
||||
$r['$z_server_role'] = \Zotlabs\Lib\System::get_server_role();
|
||||
$r['$z_techlevel'] = get_account_techlevel();
|
||||
|
||||
if(gettype($s) === 'string') {
|
||||
if (gettype($s) === 'string') {
|
||||
$template = $s;
|
||||
$s = new SmartyInterface();
|
||||
}
|
||||
foreach($r as $key=>$value) {
|
||||
if($key[0] === '$') {
|
||||
foreach ($r as $key=>$value) {
|
||||
if ($key[0] === '$') {
|
||||
$key = substr($key, 1);
|
||||
}
|
||||
$s->assign($key, $value);
|
||||
@ -50,32 +55,32 @@ class SmartyTemplate implements TemplateEngine {
|
||||
return $s->parsed($template);
|
||||
}
|
||||
|
||||
public function get_markup_template($file, $root=''){
|
||||
public function get_markup_template($file, $root = '') {
|
||||
$template_file = theme_include($file, $root);
|
||||
if($template_file) {
|
||||
if ($template_file) {
|
||||
$template = new SmartyInterface();
|
||||
$template->filename = $template_file;
|
||||
|
||||
return $template;
|
||||
}
|
||||
return "";
|
||||
return EMPTY_STR;
|
||||
}
|
||||
|
||||
public function get_intltext_template($file, $root='') {
|
||||
public function get_intltext_template($file, $root = '') {
|
||||
|
||||
$lang = \App::$language;
|
||||
$lang = App::$language;
|
||||
if ($root != '' && substr($root,-1) != '/' ) {
|
||||
$root .= '/';
|
||||
}
|
||||
foreach (Array(
|
||||
$root."view/$lang/$file",
|
||||
$root."view/en/$file",
|
||||
''
|
||||
) as $template_file) {
|
||||
if (is_file($template_file)) { break; }
|
||||
foreach ( [ $root . "view/$lang/$file", $root . "view/en/$file", '' ] as $template_file) {
|
||||
if (is_file($template_file)) {
|
||||
break;
|
||||
}
|
||||
if ($template_file=='') {$template_file = theme_include($file,$root);}
|
||||
if($template_file) {
|
||||
}
|
||||
if ($template_file == '') {
|
||||
$template_file = theme_include($file,$root);
|
||||
}
|
||||
if ($template_file) {
|
||||
$template = new SmartyInterface();
|
||||
$template->filename = $template_file;
|
||||
return $template;
|
||||
|
47
Zotlabs/Update/_1232.php
Normal file
47
Zotlabs/Update/_1232.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Update;
|
||||
|
||||
class _1232 {
|
||||
|
||||
function run() {
|
||||
|
||||
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
|
||||
return UPDATE_SUCCESS;
|
||||
}
|
||||
else {
|
||||
q("START TRANSACTION");
|
||||
|
||||
$r = q("ALTER TABLE channel
|
||||
DROP channel_r_stream,
|
||||
DROP channel_r_profile,
|
||||
DROP channel_r_photos,
|
||||
DROP channel_r_abook,
|
||||
DROP channel_w_stream,
|
||||
DROP channel_w_wall,
|
||||
DROP channel_w_tagwall,
|
||||
DROP channel_w_comment,
|
||||
DROP channel_w_mail,
|
||||
DROP channel_w_photos,
|
||||
DROP channel_w_chat,
|
||||
DROP channel_a_delegate,
|
||||
DROP channel_r_storage,
|
||||
DROP channel_w_storage,
|
||||
DROP channel_r_pages,
|
||||
DROP channel_w_pages,
|
||||
DROP channel_a_republish,
|
||||
DROP channel_w_like"
|
||||
);
|
||||
}
|
||||
|
||||
if($r) {
|
||||
q("COMMIT");
|
||||
return UPDATE_SUCCESS;
|
||||
}
|
||||
|
||||
q("ROLLBACK");
|
||||
return UPDATE_FAILED;
|
||||
|
||||
}
|
||||
|
||||
}
|
38
Zotlabs/Update/_1233.php
Normal file
38
Zotlabs/Update/_1233.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Update;
|
||||
|
||||
class _1233 {
|
||||
|
||||
function run() {
|
||||
|
||||
q("START TRANSACTION");
|
||||
|
||||
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
|
||||
$r1 = q("DROP INDEX item_uid_mid");
|
||||
|
||||
$r2 = q("create index item_uid_mid on item (uid, mid)");
|
||||
$r3 = q("create index xchan_photo_m on xchan (xchan_photo_m)");
|
||||
|
||||
$r = ($r1 && $r2 && $r3);
|
||||
}
|
||||
else {
|
||||
$r1 = q("ALTER TABLE item DROP INDEX uid_mid");
|
||||
|
||||
$r2 = q("ALTER TABLE item ADD INDEX uid_mid (uid, mid)");
|
||||
$r3 = q("ALTER TABLE xchan ADD INDEX xchan_photo_m (xchan_photo_m)");
|
||||
|
||||
$r = ($r1 && $r2 && $r3);
|
||||
}
|
||||
|
||||
if($r) {
|
||||
q("COMMIT");
|
||||
return UPDATE_SUCCESS;
|
||||
}
|
||||
|
||||
q("ROLLBACK");
|
||||
return UPDATE_FAILED;
|
||||
|
||||
}
|
||||
|
||||
}
|
26
Zotlabs/Update/_1234.php
Normal file
26
Zotlabs/Update/_1234.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Update;
|
||||
|
||||
class _1234 {
|
||||
|
||||
function run() {
|
||||
|
||||
q("START TRANSACTION");
|
||||
|
||||
$r = q("DELETE FROM app WHERE app_name = '%s' OR app_name = '%s'",
|
||||
dbesc('Events'),
|
||||
dbesc('CalDAV')
|
||||
);
|
||||
|
||||
if($r) {
|
||||
q("COMMIT");
|
||||
return UPDATE_SUCCESS;
|
||||
}
|
||||
|
||||
q("ROLLBACK");
|
||||
return UPDATE_FAILED;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -18,10 +18,9 @@ class Categories {
|
||||
|
||||
$articles = ((array_key_exists('articles',$arr) && $arr['articles']) ? true : false);
|
||||
|
||||
if(($articles) && (! feature_enabled(App::$profile['profile_uid'],'articles')))
|
||||
if(($articles) && (! Apps::system_app_installed(App::$profile['profile_uid'],'Articles')))
|
||||
return '';
|
||||
|
||||
|
||||
if((! App::$profile['profile_uid'])
|
||||
|| (! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),(($cards || $articles) ? 'view_pages' : 'view_stream')))) {
|
||||
return '';
|
||||
|
@ -22,7 +22,7 @@ class Cdav {
|
||||
|
||||
$o = '';
|
||||
|
||||
if(argc() == 2 && argv(1) === 'calendar') {
|
||||
if(argc() <= 3 && argv(1) === 'calendar') {
|
||||
|
||||
$caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
|
||||
|
||||
@ -57,7 +57,7 @@ class Cdav {
|
||||
|
||||
$switch = get_pconfig(local_channel(), 'cdav_calendar', $sabrecal['id'][0]);
|
||||
|
||||
$color = (($sabrecal['{http://apple.com/ns/ical/}calendar-color']) ? $sabrecal['{http://apple.com/ns/ical/}calendar-color'] : '#3a87ad');
|
||||
$color = (($sabrecal['{http://apple.com/ns/ical/}calendar-color']) ? $sabrecal['{http://apple.com/ns/ical/}calendar-color'] : '#6cad39');
|
||||
|
||||
$editable = (($sabrecal['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript
|
||||
|
||||
@ -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,
|
||||
@ -124,10 +136,11 @@ class Cdav {
|
||||
'$share' => t('Share'),
|
||||
'$edit_label' => t('Calendar name and color'),
|
||||
'$edit' => t('Edit'),
|
||||
'$create_label' => t('Create new calendar'),
|
||||
'$create_label' => t('Create new CalDAV calendar'),
|
||||
'$create' => t('Create'),
|
||||
'$create_placeholder' => t('Calendar Name'),
|
||||
'$tools_label' => t('Calendar Tools'),
|
||||
'$tools_options_label' => [t('Channel Calendars'), t('CalDAV Calendars')],
|
||||
'$import_label' => t('Import calendar'),
|
||||
'$import_placeholder' => t('Select a calendar to import to'),
|
||||
'$upload' => t('Upload'),
|
||||
|
@ -69,7 +69,7 @@ class Notifications {
|
||||
'label' => t('New Events'),
|
||||
'title' => t('New Events Notifications'),
|
||||
'viewall' => [
|
||||
'url' => 'events',
|
||||
'url' => 'cdav/calendar',
|
||||
'label' => t('View events')
|
||||
],
|
||||
'markall' => [
|
||||
|
@ -1,6 +1,6 @@
|
||||
version: 2
|
||||
version: 1
|
||||
url: $baseurl/cdav/calendar, $baseurl/settings/calendar
|
||||
requires: local_channel
|
||||
name: CalDAV
|
||||
name: Calendar
|
||||
photo: icon:calendar
|
||||
categories: Productivity, Personal
|
||||
categories: Productivity, nav_featured_app
|
@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
url: $baseurl/events, $baseurl/settings/events
|
||||
requires: local_channel
|
||||
name: Events
|
||||
photo: icon:calendar
|
||||
categories: nav_featured_app, Productivity
|
BIN
app/events.png
BIN
app/events.png
Binary file not shown.
Before Width: | Height: | Size: 4.0 KiB |
9
boot.php
9
boot.php
@ -50,10 +50,10 @@ require_once('include/attach.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
define ( 'PLATFORM_NAME', 'hubzilla' );
|
||||
define ( 'STD_VERSION', '4.0.3' );
|
||||
define ( 'STD_VERSION', '4.2' );
|
||||
define ( 'ZOT_REVISION', '6.0a' );
|
||||
|
||||
define ( 'DB_UPDATE_VERSION', 1231 );
|
||||
define ( 'DB_UPDATE_VERSION', 1234 );
|
||||
|
||||
define ( 'PROJECT_BASE', __DIR__ );
|
||||
|
||||
@ -84,7 +84,8 @@ define ( 'DIRECTORY_FALLBACK_MASTER', 'https://zotadel.net');
|
||||
|
||||
$DIRECTORY_FALLBACK_SERVERS = array(
|
||||
'https://zotadel.net',
|
||||
'https://zotsite.net'
|
||||
'https://zotsite.net',
|
||||
'https://hub.netzgemeinde.eu'
|
||||
);
|
||||
|
||||
|
||||
@ -467,7 +468,7 @@ define ( 'NAMESPACE_YMEDIA', 'http://search.yahoo.com/mrss/' );
|
||||
|
||||
define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' );
|
||||
|
||||
define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.3' );
|
||||
define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.5' );
|
||||
/**
|
||||
* activity stream defines
|
||||
*/
|
||||
|
@ -39,7 +39,7 @@
|
||||
"lukasreschke/id3parser": "^0.0.1",
|
||||
"smarty/smarty": "~3.1",
|
||||
"ramsey/uuid": "^3.8",
|
||||
"twbs/bootstrap": "4.1.3",
|
||||
"twbs/bootstrap": "^4.3.1",
|
||||
"blueimp/jquery-file-upload": "^9.25",
|
||||
"desandro/imagesloaded": "^4.1"
|
||||
},
|
||||
|
35
composer.lock
generated
35
composer.lock
generated
@ -4,20 +4,20 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "8da1fe9aabe6c20d116a21f63fff8ac2",
|
||||
"content-hash": "f4dce457cd65f92a26d8197617f2f560",
|
||||
"packages": [
|
||||
{
|
||||
"name": "blueimp/jquery-file-upload",
|
||||
"version": "v9.28.0",
|
||||
"version": "v9.30.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vkhramtsov/jQuery-File-Upload.git",
|
||||
"reference": "ff5accfe2e5c4a522777faa980a90cf86a636d1d"
|
||||
"reference": "1fceec556879403e5c1ae32a7c448aa12b8c3558"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vkhramtsov/jQuery-File-Upload/zipball/ff5accfe2e5c4a522777faa980a90cf86a636d1d",
|
||||
"reference": "ff5accfe2e5c4a522777faa980a90cf86a636d1d",
|
||||
"url": "https://api.github.com/repos/vkhramtsov/jQuery-File-Upload/zipball/1fceec556879403e5c1ae32a7c448aa12b8c3558",
|
||||
"reference": "1fceec556879403e5c1ae32a7c448aa12b8c3558",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -59,7 +59,7 @@
|
||||
"upload",
|
||||
"widget"
|
||||
],
|
||||
"time": "2018-11-13T05:41:39+00:00"
|
||||
"time": "2019-04-22T09:21:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bshaffer/oauth2-server-php",
|
||||
@ -861,16 +861,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sabre/vobject",
|
||||
"version": "4.1.6",
|
||||
"version": "4.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sabre-io/vobject.git",
|
||||
"reference": "122cacbdea2c6133ac04db86ec05854beef75adf"
|
||||
"reference": "bd500019764e434ff65872d426f523e7882a0739"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sabre-io/vobject/zipball/122cacbdea2c6133ac04db86ec05854beef75adf",
|
||||
"reference": "122cacbdea2c6133ac04db86ec05854beef75adf",
|
||||
"url": "https://api.github.com/repos/sabre-io/vobject/zipball/bd500019764e434ff65872d426f523e7882a0739",
|
||||
"reference": "bd500019764e434ff65872d426f523e7882a0739",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -879,8 +879,7 @@
|
||||
"sabre/xml": ">=1.5 <3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "> 4.8.35, <6.0.0",
|
||||
"sabre/cs": "^1.0.0"
|
||||
"phpunit/phpunit": "> 4.8.35, <6.0.0"
|
||||
},
|
||||
"suggest": {
|
||||
"hoa/bench": "If you would like to run the benchmark scripts"
|
||||
@ -954,7 +953,7 @@
|
||||
"xCal",
|
||||
"xCard"
|
||||
],
|
||||
"time": "2018-04-20T07:22:50+00:00"
|
||||
"time": "2019-02-19T13:05:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sabre/xml",
|
||||
@ -1207,16 +1206,16 @@
|
||||
},
|
||||
{
|
||||
"name": "twbs/bootstrap",
|
||||
"version": "v4.1.3",
|
||||
"version": "v4.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twbs/bootstrap.git",
|
||||
"reference": "3b558734382ce58b51e5fc676453bfd53bba9201"
|
||||
"reference": "8fa0d3010112dca5dd6dd501173415856001ba8b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twbs/bootstrap/zipball/3b558734382ce58b51e5fc676453bfd53bba9201",
|
||||
"reference": "3b558734382ce58b51e5fc676453bfd53bba9201",
|
||||
"url": "https://api.github.com/repos/twbs/bootstrap/zipball/8fa0d3010112dca5dd6dd501173415856001ba8b",
|
||||
"reference": "8fa0d3010112dca5dd6dd501173415856001ba8b",
|
||||
"shasum": ""
|
||||
},
|
||||
"replace": {
|
||||
@ -1254,7 +1253,7 @@
|
||||
"sass",
|
||||
"web"
|
||||
],
|
||||
"time": "2018-07-24T15:54:34+00:00"
|
||||
"time": "2019-02-13T16:01:40+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
### Overview
|
||||
|
||||
$Projectname is more than a simple web application. It is a
|
||||
@ -24,7 +23,7 @@ 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
|
||||
@ -45,7 +44,6 @@ 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
|
||||
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
|
||||
@ -87,9 +85,9 @@ There are several ways to deploy a new hub.
|
||||
Example config scripts are available for these platforms in doc/install.
|
||||
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
|
||||
@ -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
|
||||
@ -203,9 +201,9 @@ 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:
|
||||
|
||||
@ -214,23 +212,6 @@ Each member of your site must choose whether or not to allow these protocols ind
|
||||
|
||||
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.
|
||||
|
||||
### Service Classes
|
||||
|
||||
Service classes allow you to set limits on system resources by limiting what individual
|
||||
@ -331,7 +312,6 @@ empty:
|
||||
|
||||
util/config system directory_server ""
|
||||
|
||||
|
||||
### Administration
|
||||
|
||||
#### Site Administration
|
||||
@ -344,45 +324,112 @@ For security reasons there is no web page or interface on the system which will
|
||||
|
||||
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.
|
||||
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 [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.
|
||||
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.
|
||||
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 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.
|
||||
**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.
|
||||
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 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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
If something goes wrong, and it's not a fatal error, I look at the application logfile. Often I will
|
||||
```
|
||||
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.
|
||||
|
@ -51,6 +51,7 @@ Options are:
|
||||
[*= system.email_notify_icon_url ] URL of image (32x32) to display in email notifications (HTML bodies).
|
||||
[*= system.expire_delivery_reports ] Expiration in days for delivery reports - default 10
|
||||
[*= system.expire_limit ] Don't expire any more than this number of posts per channel per expiration run to keep from exhausting memory. Default 5000.
|
||||
[*= system.filesystem_storage_thumbnails ] If '1', use filesystem instead SQL database to store thumbnails. Default is '0'. Introduced in 4.2
|
||||
[*= system.hidden_version_siteinfo ] If true, do not report the software version on siteinfo pages (system.hide_version also hides the version on these pages, this setting *only* hides the version on siteinfo pages).
|
||||
[*= system.hide_help ] Don't display help documentation link in nav bar
|
||||
[*= system.hide_in_statistics ] Tell the red statistics servers to completely hide this hub in hub lists.
|
||||
@ -79,7 +80,7 @@ Options are:
|
||||
[*= system.startpage ] Set the default page to be taken to after a login for all channels at this website. Can be overwritten by user settings.
|
||||
[*= system.sys_expire_days ] How many days to keep discovered public content from other sites
|
||||
[*= system.taganyone ] Allow the @mention tagging of anyone whether you are connected or not.
|
||||
[*= system.tempdir ] Place to store temporary files (currently unused), default is defined in the PHP configuration
|
||||
[*= system.tempdir ] Place to store temporary files (currently unused), default is defined in the PHP configuration.
|
||||
[*= system.tos_url ] Set an alternative link for the ToS location.
|
||||
[*= system.transport_security_header ] if non-zero and SSL is being used, include a strict-transport-security header on webpages
|
||||
[*= system.uploaddir ] Location to upload files (default is system.tempdir, currently used only by js_upload plugin)
|
||||
|
@ -1521,6 +1521,17 @@ function attach_drop_photo($channel_id,$resource) {
|
||||
if($x) {
|
||||
drop_item($x[0]['id'],false,(($x[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1),true);
|
||||
}
|
||||
|
||||
$r = q("SELECT content FROM photo WHERE resource_id = '%s' AND uid = %d AND os_storage = 1",
|
||||
dbesc($resource),
|
||||
intval($channel_id)
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $i) {
|
||||
@unlink(dbunescbin($i['content']));
|
||||
}
|
||||
}
|
||||
|
||||
q("DELETE FROM photo WHERE uid = %d AND resource_id = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($resource)
|
||||
|
@ -87,12 +87,11 @@ function nakedoembed($match) {
|
||||
|
||||
$strip_url = strip_escaped_zids($url);
|
||||
|
||||
$o = oembed_fetch_url($strip_url);
|
||||
// this function no longer performs oembed on naked links
|
||||
// because they author may have created naked links intentionally.
|
||||
// Now it just strips zids on naked links.
|
||||
|
||||
if ($o['type'] == 'error')
|
||||
return str_replace($url,$strip_url,$match[0]);
|
||||
|
||||
return '[embed]' . $strip_url . '[/embed]';
|
||||
}
|
||||
|
||||
function tryzrlaudio($match) {
|
||||
@ -986,7 +985,11 @@ function bbcode($Text, $options = []) {
|
||||
|
||||
// leave open the posibility of [map=something]
|
||||
// this is replaced in prepare_body() which has knowledge of the item location
|
||||
|
||||
if ($cache) {
|
||||
$Text = str_replace([ '[map]','[/map]' ], [ '','' ], $Text);
|
||||
$Text = preg_replace('/\[map=(.*?)\]/ism','$1',$Text);
|
||||
}
|
||||
else {
|
||||
if (strpos($Text,'[/map]') !== false) {
|
||||
$Text = preg_replace_callback("/\[map\](.*?)\[\/map\]/ism", 'bb_map_location', $Text);
|
||||
}
|
||||
@ -996,6 +999,7 @@ function bbcode($Text, $options = []) {
|
||||
if (strpos($Text,'[map]') !== false) {
|
||||
$Text = preg_replace("/\[map\]/", '<div class="map"></div>', $Text);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for bold text
|
||||
if (strpos($Text,'[b]') !== false) {
|
||||
|
@ -373,19 +373,46 @@ function contact_remove($channel_id, $abook_id) {
|
||||
if(intval($abook['abook_self']))
|
||||
return false;
|
||||
|
||||
$r = q("select id from item where (owner_xchan = '%s' or author_xchan = '%s') and uid = %d and item_retained = 0 and item_starred = 0",
|
||||
$r = q("select id, parent from item where (owner_xchan = '%s' or author_xchan = '%s') and uid = %d and item_retained = 0 and item_starred = 0",
|
||||
dbesc($abook['abook_xchan']),
|
||||
dbesc($abook['abook_xchan']),
|
||||
intval($channel_id)
|
||||
);
|
||||
if($r) {
|
||||
$already_saved = [];
|
||||
foreach($r as $rr) {
|
||||
$w = $x = $y = null;
|
||||
|
||||
// optimise so we only process newly seen parent items
|
||||
if (in_array($rr['parent'],$already_saved)) {
|
||||
continue;
|
||||
}
|
||||
// if this isn't the parent, fetch the parent's item_retained and item_starred to see if the conversation
|
||||
// should be retained
|
||||
if($rr['id'] != $rr['parent']) {
|
||||
$w = q("select id, item_retained, item_starred from item where id = %d",
|
||||
intval($rr['parent'])
|
||||
);
|
||||
if($w) {
|
||||
// see if the conversation was filed
|
||||
$x = q("select uid from term where otype = %d and oid = %d and ttype = %d limit 1",
|
||||
intval(TERM_OBJ_POST),
|
||||
intval($w[0]['id']),
|
||||
intval(TERM_FILE)
|
||||
);
|
||||
if (intval($w[0]['item_retained']) || intval($w[0]['item_starred']) || $x) {
|
||||
$already_saved[] = $rr['parent'];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// see if this item was filed
|
||||
$y = q("select uid from term where otype = %d and oid = %d and ttype = %d limit 1",
|
||||
intval(TERM_OBJ_POST),
|
||||
intval($rr['id']),
|
||||
intval(TERM_FILE)
|
||||
);
|
||||
if($x) {
|
||||
if ($y) {
|
||||
continue;
|
||||
}
|
||||
drop_item($rr['id'],false);
|
||||
|
@ -516,13 +516,14 @@ function update_birthdays() {
|
||||
'event_xchan' => $rr['xchan_hash'],
|
||||
'dtstart' => datetime_convert('UTC', 'UTC', $rr['abook_dob']),
|
||||
'dtend' => datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '),
|
||||
'adjust' => intval(feature_enabled($rr['abook_channel'],'smart_birthdays')),
|
||||
'adjust' => 0,
|
||||
'summary' => sprintf( t('%1$s\'s birthday'), $rr['xchan_name']),
|
||||
'description' => sprintf( t('Happy Birthday %1$s'), '[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]'),
|
||||
'etype' => 'birthday',
|
||||
];
|
||||
|
||||
$z = event_store_event($ev);
|
||||
|
||||
if ($z) {
|
||||
$item_id = event_store_item($ev, $z);
|
||||
q("update abook set abook_dob = '%s' where abook_id = %d",
|
||||
|
@ -485,3 +485,28 @@ function db_columns($table) {
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
function db_indexes($table) {
|
||||
|
||||
if($table) {
|
||||
if(ACTIVE_DBTYPE === DBTYPE_POSTGRES) {
|
||||
$r = q("SELECT indexname from pg_indexes where tablename = '%s'",
|
||||
dbesc($table)
|
||||
);
|
||||
if($r) {
|
||||
return ids_to_array($r,'indexname');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$r = q("show index from %s",
|
||||
dbesc($table)
|
||||
);
|
||||
if($r) {
|
||||
return ids_to_array($r,'Key_name');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class dba_pdo extends dba_driver {
|
||||
$this->driver_dbtype = $scheme;
|
||||
|
||||
if(strpbrk($server,':;')) {
|
||||
$dsn = $server;
|
||||
$dsn = $this->driver_dbtype . ':unix_socket=' . trim($server, ':;');
|
||||
}
|
||||
else {
|
||||
$dsn = $this->driver_dbtype . ':host=' . $server . (intval($port) ? ';port=' . $port : '');
|
||||
|
@ -27,6 +27,7 @@ function format_event_html($ev) {
|
||||
if(! ((is_array($ev)) && count($ev)))
|
||||
return '';
|
||||
|
||||
$tz = (($ev['timezone']) ? $ev['timezone'] : 'UTC');
|
||||
|
||||
$bd_format = t('l F d, Y \@ g:i A') ; // Friday January 18, 2011 @ 8:01 AM
|
||||
|
||||
@ -39,7 +40,7 @@ function format_event_html($ev) {
|
||||
$o .= '<div class="event-start"><span class="event-label">' . t('Starts:') . '</span> <span class="dtstart" title="'
|
||||
. datetime_convert('UTC', 'UTC', $ev['dtstart'], (($ev['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' ))
|
||||
. '" >'
|
||||
. (($ev['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(),
|
||||
. (($ev['adjust']) ? day_translate(datetime_convert($tz, date_default_timezone_get(),
|
||||
$ev['dtstart'] , $bd_format ))
|
||||
: day_translate(datetime_convert('UTC', 'UTC',
|
||||
$ev['dtstart'] , $bd_format)))
|
||||
@ -49,7 +50,7 @@ function format_event_html($ev) {
|
||||
$o .= '<div class="event-end" ><span class="event-label">' . t('Finishes:') . '</span> <span class="dtend" title="'
|
||||
. datetime_convert('UTC','UTC',$ev['dtend'], (($ev['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' ))
|
||||
. '" >'
|
||||
. (($ev['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(),
|
||||
. (($ev['adjust']) ? day_translate(datetime_convert($tz, date_default_timezone_get(),
|
||||
$ev['dtend'] , $bd_format ))
|
||||
: day_translate(datetime_convert('UTC', 'UTC',
|
||||
$ev['dtend'] , $bd_format )))
|
||||
@ -75,17 +76,35 @@ function format_event_obj($jobject) {
|
||||
//ensure compatibility with older items - this check can be removed at a later point
|
||||
if(array_key_exists('description', $object)) {
|
||||
|
||||
$bd_format = t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8:01 AM
|
||||
$tz = (($object['timezone']) ? $object['timezone'] : 'UTC');
|
||||
$allday = (($object['adjust']) ? false : true);
|
||||
|
||||
$dtstart = new DateTime($object['dtstart']);
|
||||
$dtend = new DateTime($object['dtend']);
|
||||
$dtdiff = $dtstart->diff($dtend);
|
||||
|
||||
if($allday && ($dtdiff->days < 2))
|
||||
$oneday = true;
|
||||
|
||||
if($allday && !$oneday) {
|
||||
// Subtract one day from the end date so we can use the "first day - last day" format for display.
|
||||
$dtend->modify('-1 day');
|
||||
$object['dtend'] = datetime_convert('UTC', 'UTC', $dtend->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
$bd_format = (($allday) ? t('l F d, Y') : t('l F d, Y \@ g:i A')); // Friday January 18, 2011 @ 8:01 AM or Friday January 18, 2011 for allday events
|
||||
|
||||
$event['header'] = replace_macros(get_markup_template('event_item_header.tpl'),array(
|
||||
'$title' => zidify_links(smilies(bbcode($object['title']))),
|
||||
'$dtstart_label' => t('Starts:'),
|
||||
'$dtstart_title' => datetime_convert('UTC', 'UTC', $object['dtstart'], (($object['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )),
|
||||
'$dtstart_dt' => (($object['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['dtstart'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['dtstart'] , $bd_format))),
|
||||
'$dtstart_label' => t('Start:'),
|
||||
'$dtstart_title' => datetime_convert($tz, date_default_timezone_get(), $object['dtstart'], (($object['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )),
|
||||
'$dtstart_dt' => (($object['adjust']) ? day_translate(datetime_convert($tz, date_default_timezone_get(), $object['dtstart'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['dtstart'] , $bd_format))),
|
||||
'$finish' => (($object['nofinish']) ? false : true),
|
||||
'$dtend_label' => t('Finishes:'),
|
||||
'$dtend_title' => datetime_convert('UTC','UTC',$object['dtend'], (($object['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )),
|
||||
'$dtend_dt' => (($object['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['dtend'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['dtend'] , $bd_format )))
|
||||
'$dtend_label' => t('End:'),
|
||||
'$dtend_title' => datetime_convert($tz, date_default_timezone_get(), $object['dtend'], (($object['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )),
|
||||
'$dtend_dt' => (($object['adjust']) ? day_translate(datetime_convert($tz, date_default_timezone_get(), $object['dtend'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['dtend'] , $bd_format ))),
|
||||
'$allday' => $allday,
|
||||
'$oneday' => $oneday
|
||||
));
|
||||
|
||||
$event['content'] = replace_macros(get_markup_template('event_item_content.tpl'),array(
|
||||
@ -125,6 +144,12 @@ function format_event_ical($ev) {
|
||||
if($ev['etype'] === 'task')
|
||||
return format_todo_ical($ev);
|
||||
|
||||
$tz = get_iconfig($ev['item_id'], 'event', 'timezone');
|
||||
if(! $tz)
|
||||
$tz = 'UTC';
|
||||
|
||||
$tzid = ';TZID=' . $tz;
|
||||
|
||||
$o = '';
|
||||
|
||||
$o .= "\r\nBEGIN:VEVENT";
|
||||
@ -132,10 +157,19 @@ function format_event_ical($ev) {
|
||||
$o .= "\r\nCREATED:" . datetime_convert('UTC','UTC', $ev['created'],'Ymd\\THis\\Z');
|
||||
$o .= "\r\nLAST-MODIFIED:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z');
|
||||
$o .= "\r\nDTSTAMP:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z');
|
||||
|
||||
if($ev['adjust']) {
|
||||
if($ev['dtstart'])
|
||||
$o .= "\r\nDTSTART:" . datetime_convert('UTC','UTC', $ev['dtstart'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : ''));
|
||||
$o .= "\r\nDTSTART$tzid:" . datetime_convert($tz,'UTC', $ev['dtstart'],'Ymd\\THis\\Z');
|
||||
if($ev['dtend'] && ! $ev['nofinish'])
|
||||
$o .= "\r\nDTEND:" . datetime_convert('UTC','UTC', $ev['dtend'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : ''));
|
||||
$o .= "\r\nDTEND$tzid:" . datetime_convert($tz,'UTC', $ev['dtend'],'Ymd\\THis\\Z');
|
||||
}
|
||||
else {
|
||||
if($ev['dtstart'])
|
||||
$o .= "\r\nDTSTART;VALUE=DATE:" . datetime_convert('UTC','UTC', $ev['dtstart'],'Ymd');
|
||||
if($ev['dtend'] && ! $ev['nofinish'])
|
||||
$o .= "\r\nDTEND;VALUE=DATE:" . datetime_convert('UTC','UTC', $ev['dtend'],'Ymd');
|
||||
}
|
||||
if($ev['summary']) {
|
||||
$o .= "\r\nSUMMARY:" . format_ical_text($ev['summary']);
|
||||
$o .= "\r\nX-ZOT-SUMMARY:" . format_ical_sourcetext($ev['summary']);
|
||||
@ -723,17 +757,9 @@ function parse_vobject($ical, $type) {
|
||||
|
||||
|
||||
function parse_ical_file($f,$uid) {
|
||||
require_once('vendor/autoload.php');
|
||||
|
||||
$s = @file_get_contents($f);
|
||||
|
||||
// Change the current timezone to something besides UTC.
|
||||
// Doesn't matter what it is, as long as it isn't UTC.
|
||||
// Save the current timezone so we can reset it when we're done processing.
|
||||
|
||||
$saved_timezone = date_default_timezone_get();
|
||||
date_default_timezone_set('Australia/Sydney');
|
||||
|
||||
$ical = VObject\Reader::read($s);
|
||||
|
||||
if($ical) {
|
||||
@ -749,8 +775,6 @@ function parse_ical_file($f,$uid) {
|
||||
}
|
||||
}
|
||||
|
||||
date_default_timezone_set($saved_timezone);
|
||||
|
||||
if($ical)
|
||||
return true;
|
||||
|
||||
@ -782,12 +806,23 @@ function event_import_ical($ical, $uid) {
|
||||
|
||||
// logger('dtstart: ' . var_export($dtstart,true));
|
||||
|
||||
$ev['dtstart'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC',
|
||||
$ev['timezone'] = 'UTC';
|
||||
|
||||
// Try to get an usable olson format timezone
|
||||
if($ev['adjust']) {
|
||||
//TODO: we should pass the vcalendar to getTimeZone() to be more accurate
|
||||
// we do not have it here since parse_ical_file() is passing the vevent only.
|
||||
$timezone_obj = \Sabre\VObject\TimeZoneUtil::getTimeZone($ical->DTSTART['TZID']);
|
||||
$timezone = $timezone_obj->getName();
|
||||
$ev['timezone'] = $timezone;
|
||||
}
|
||||
|
||||
$ev['dtstart'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),$ev['timezone'],
|
||||
$dtstart->format(\DateTime::W3C));
|
||||
|
||||
if(isset($ical->DTEND)) {
|
||||
$dtend = $ical->DTEND->getDateTime();
|
||||
$ev['dtend'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC',
|
||||
$ev['dtend'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),$ev['timezone'],
|
||||
$dtend->format(\DateTime::W3C));
|
||||
}
|
||||
else {
|
||||
@ -1042,6 +1077,7 @@ function event_store_item($arr, $event) {
|
||||
'type' => ACTIVITY_OBJ_EVENT,
|
||||
'id' => z_root() . '/event/' . $r[0]['resource_id'],
|
||||
'title' => $arr['summary'],
|
||||
'timezone' => $arr['timezone'],
|
||||
'dtstart' => $arr['dtstart'],
|
||||
'dtend' => $arr['dtend'],
|
||||
'nofinish' => $arr['nofinish'],
|
||||
@ -1107,6 +1143,8 @@ function event_store_item($arr, $event) {
|
||||
}
|
||||
|
||||
$item_id = $r[0]['id'];
|
||||
set_iconfig($item_id, 'event', 'timezone', $arr['timezone'], true);
|
||||
|
||||
/**
|
||||
* @hooks event_updated
|
||||
* Called when an event record is modified.
|
||||
@ -1163,7 +1201,7 @@ function event_store_item($arr, $event) {
|
||||
$item_arr['item_thread_top'] = $item_thread_top;
|
||||
|
||||
$attach = array(array(
|
||||
'href' => z_root() . '/events/ical/' . urlencode($event['event_hash']),
|
||||
'href' => z_root() . '/channel_calendar/ical/' . urlencode($event['event_hash']),
|
||||
'length' => 0,
|
||||
'type' => 'text/calendar',
|
||||
'title' => t('event') . '-' . $event['event_hash'],
|
||||
@ -1185,7 +1223,7 @@ function event_store_item($arr, $event) {
|
||||
// otherwise we'll fallback to /display/$message_id
|
||||
|
||||
if($wall)
|
||||
$item_arr['plink'] = z_root() . '/channel/' . $z[0]['channel_address'] . '/?f=&mid=' . urlencode($item_arr['mid']);
|
||||
$item_arr['plink'] = z_root() . '/channel/' . $z[0]['channel_address'] . '/?f=&mid=' . gen_link_id($item_arr['mid']);
|
||||
else
|
||||
$item_arr['plink'] = z_root() . '/display/' . gen_link_id($item_arr['mid']);
|
||||
|
||||
@ -1197,6 +1235,7 @@ function event_store_item($arr, $event) {
|
||||
'type' => ACTIVITY_OBJ_EVENT,
|
||||
'id' => z_root() . '/event/' . $event['event_hash'],
|
||||
'title' => $arr['summary'],
|
||||
'timezone' => $arr['timezone'],
|
||||
'dtstart' => $arr['dtstart'],
|
||||
'dtend' => $arr['dtend'],
|
||||
'nofinish' => $arr['nofinish'],
|
||||
@ -1223,6 +1262,7 @@ function event_store_item($arr, $event) {
|
||||
// activities refer to the item message_id as the parent.
|
||||
|
||||
set_iconfig($item_arr, 'system','event_id',$event['event_hash'],true);
|
||||
set_iconfig($item_arr, 'event','timezone',$arr['timezone'],true);
|
||||
|
||||
$res = item_store($item_arr);
|
||||
|
||||
@ -1284,6 +1324,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'])) {
|
||||
|
@ -79,7 +79,7 @@ function get_features($filtered = true, $level = (-1)) {
|
||||
|
||||
'calendar' => [
|
||||
|
||||
t('CalDAV'),
|
||||
t('Calendar'),
|
||||
|
||||
[
|
||||
'cal_first_day',
|
||||
@ -87,6 +87,14 @@ function get_features($filtered = true, $level = (-1)) {
|
||||
t('Default is Sunday'),
|
||||
false,
|
||||
get_config('feature_lock','cal_first_day')
|
||||
],
|
||||
|
||||
[
|
||||
'event_tz_select',
|
||||
t('Event Timezone Selection'),
|
||||
t('Allow event creation in timezones other than your own.'),
|
||||
false,
|
||||
get_config('feature_lock','event_tz_select'),
|
||||
]
|
||||
|
||||
],
|
||||
@ -167,6 +175,14 @@ function get_features($filtered = true, $level = (-1)) {
|
||||
t('Ability to mark special posts with a star indicator'),
|
||||
false,
|
||||
get_config('feature_lock','star_posts'),
|
||||
],
|
||||
|
||||
[
|
||||
'reply_to',
|
||||
t('Reply on comment'),
|
||||
t('Ability to reply on selected comment'),
|
||||
false,
|
||||
get_config('feature_lock','reply_to'),
|
||||
]
|
||||
|
||||
],
|
||||
@ -274,22 +290,6 @@ function get_features($filtered = true, $level = (-1)) {
|
||||
t('Default is Sunday'),
|
||||
false,
|
||||
get_config('feature_lock','events_cal_first_day')
|
||||
],
|
||||
|
||||
[
|
||||
'smart_birthdays',
|
||||
t('Smart Birthdays'),
|
||||
t('Make birthday events timezone aware in case your friends are scattered across the planet.'),
|
||||
true,
|
||||
get_config('feature_lock','smart_birthdays'),
|
||||
],
|
||||
|
||||
[
|
||||
'event_tz_select',
|
||||
t('Event Timezone Selection'),
|
||||
t('Allow event creation in timezones other than your own.'),
|
||||
false,
|
||||
get_config('feature_lock','event_tz_select'),
|
||||
]
|
||||
|
||||
],
|
||||
|
@ -142,7 +142,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
|
||||
$sql_options = (($protocol) ? " and xchan_network = '" . dbesc($protocol) . "' " : '');
|
||||
|
||||
|
||||
$r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' $sql_options limit 1",
|
||||
$r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' $sql_options ",
|
||||
dbesc($url),
|
||||
dbesc($url)
|
||||
);
|
||||
@ -169,18 +169,19 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
|
||||
}
|
||||
|
||||
if($wf || $d) {
|
||||
$r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1",
|
||||
$r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s'",
|
||||
dbesc(($wf) ? $wf : $url),
|
||||
dbesc($url)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$xchan = zot_record_preferred($r,'xchan_network');
|
||||
|
||||
// if discovery was a success we should have an xchan record in $r
|
||||
|
||||
if($r) {
|
||||
$xchan = $r[0];
|
||||
$xchan_hash = $r[0]['xchan_hash'];
|
||||
if($xchan) {
|
||||
$xchan_hash = $xchan['xchan_hash'];
|
||||
$their_perms = 0;
|
||||
}
|
||||
}
|
||||
@ -191,9 +192,9 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
|
||||
return $result;
|
||||
}
|
||||
|
||||
$allowed = (($is_zot || in_array($r[0]['xchan_network'],['rss','zot6'])) ? 1 : 0);
|
||||
$allowed = (($is_zot || in_array($xchan['xchan_network'],['rss','zot6'])) ? 1 : 0);
|
||||
|
||||
$x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => $allowed, 'singleton' => 0);
|
||||
$x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $xchan, 'allowed' => $allowed, 'singleton' => 0);
|
||||
|
||||
call_hooks('follow_allow',$x);
|
||||
|
||||
|
@ -219,8 +219,7 @@ function html2bbcode($message)
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
|
||||
// I'm removing something really disturbing
|
||||
// Don't know exactly what it is
|
||||
// I'm removing the UTF-8 encoding of a NO-BREAK SPACE codepoint
|
||||
$message = str_replace(chr(194).chr(160), ' ', $message);
|
||||
|
||||
$message = str_replace(" ", " ", $message);
|
||||
|
@ -94,7 +94,8 @@ function import_channel($channel, $account_id, $seize, $newname = '') {
|
||||
'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall',
|
||||
'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish',
|
||||
'channel_a_delegate', 'perm_limits', 'channel_password', 'channel_salt',
|
||||
'channel_moved', 'channel_removed', 'channel_deleted', 'channel_system'
|
||||
'channel_moved', 'channel_removed', 'channel_deleted', 'channel_system',
|
||||
'channel_r_photos', 'channel_w_photos'
|
||||
];
|
||||
|
||||
$clean = array();
|
||||
@ -896,9 +897,9 @@ function import_menus($channel, $menus) {
|
||||
$m['menu_name'] = $menu['pagetitle'];
|
||||
$m['menu_desc'] = $menu['desc'];
|
||||
if($menu['created'])
|
||||
$m['menu_created'] = datetime_convert($menu['created']);
|
||||
$m['menu_created'] = datetime_convert('UTC','UTC',$menu['created']);
|
||||
if($menu['edited'])
|
||||
$m['menu_edited'] = datetime_convert($menu['edited']);
|
||||
$m['menu_edited'] = datetime_convert('UTC','UTC',$menu['edited']);
|
||||
|
||||
$m['menu_flags'] = 0;
|
||||
if($menu['flags']) {
|
||||
@ -954,9 +955,9 @@ function sync_menus($channel, $menus) {
|
||||
$m['menu_name'] = $menu['pagetitle'];
|
||||
$m['menu_desc'] = $menu['desc'];
|
||||
if($menu['created'])
|
||||
$m['menu_created'] = datetime_convert($menu['created']);
|
||||
$m['menu_created'] = datetime_convert('UTC','UTC',$menu['created']);
|
||||
if($menu['edited'])
|
||||
$m['menu_edited'] = datetime_convert($menu['edited']);
|
||||
$m['menu_edited'] = datetime_convert('UTC','UTC',$menu['edited']);
|
||||
|
||||
$m['menu_flags'] = 0;
|
||||
if($menu['flags']) {
|
||||
@ -1345,6 +1346,7 @@ function sync_files($channel, $files) {
|
||||
logger('attachment store failed',LOGGER_NORMAL,LOG_ERR);
|
||||
}
|
||||
if($f['photo']) {
|
||||
|
||||
foreach($f['photo'] as $p) {
|
||||
unset($p['id']);
|
||||
$p['aid'] = $channel['channel_account_id'];
|
||||
@ -1366,6 +1368,7 @@ function sync_files($channel, $files) {
|
||||
dbesc($p['resource_id']),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
$update_xchan = $p['edited'];
|
||||
}
|
||||
|
||||
// same for cover photos
|
||||
@ -1385,19 +1388,20 @@ function sync_files($channel, $files) {
|
||||
else
|
||||
$p['content'] = (($p['content'])? base64_decode($p['content']) : '');
|
||||
|
||||
if(intval($p['imgscale']) && (! $p['content'])) {
|
||||
if(intval($p['imgscale']) && (! empty($p['content']))) {
|
||||
|
||||
$time = datetime_convert();
|
||||
|
||||
$parr = array('hash' => $channel['channel_hash'],
|
||||
$parr = array(
|
||||
'hash' => $channel['channel_hash'],
|
||||
'time' => $time,
|
||||
'resource' => $att['hash'],
|
||||
'resource' => $p['resource_id'],
|
||||
'revision' => 0,
|
||||
'signature' => base64url_encode(rsa_sign($channel['channel_hash'] . '.' . $time, $channel['channel_prvkey'])),
|
||||
'resolution' => $p['imgscale']
|
||||
'resolution' => intval($p['imgscale'])
|
||||
);
|
||||
|
||||
$stored_image = $newfname . '-' . intval($p['imgscale']);
|
||||
$stored_image = $newfname . '-' . $p['imgscale'];
|
||||
|
||||
$fp = fopen($stored_image,'w');
|
||||
if(! $fp) {
|
||||
@ -1406,7 +1410,6 @@ function sync_files($channel, $files) {
|
||||
}
|
||||
$redirects = 0;
|
||||
|
||||
|
||||
$headers = [];
|
||||
$headers['Accept'] = 'application/x-zot+json' ;
|
||||
$headers['Sigtoken'] = random_string();
|
||||
@ -1414,8 +1417,17 @@ function sync_files($channel, $files) {
|
||||
|
||||
$x = z_post_url($fetch_url,$parr,$redirects,[ 'filep' => $fp, 'headers' => $headers]);
|
||||
fclose($fp);
|
||||
|
||||
// Override remote hub thumbnails storage settings
|
||||
if(! boolval(get_config('system','filesystem_storage_thumbnails', 0))) {
|
||||
$p['os_storage'] = 0;
|
||||
$p['content'] = file_get_contents($stored_image);
|
||||
unlink($stored_image);
|
||||
@unlink($stored_image);
|
||||
}
|
||||
else {
|
||||
$p['os_storage'] = 1;
|
||||
$p['content'] = $stored_image;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isset($p['display_path']))
|
||||
@ -1447,6 +1459,16 @@ function sync_files($channel, $files) {
|
||||
create_table_from_array('photo',$p, [ 'content' ] );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set xchan photo date to prevent thumbnails fetch for clones on profile update packet recieve
|
||||
if(isset($update_xchan)) {
|
||||
|
||||
$x = q("UPDATE xchan SET xchan_photo_date = '%s' WHERE xchan_hash = '%s'",
|
||||
dbescdate($update_xchan),
|
||||
dbesc($channel['channel_hash'])
|
||||
);
|
||||
}
|
||||
|
||||
\Zotlabs\Daemon\Master::Summon([ 'Thumbnail' , $att['hash'] ]);
|
||||
@ -1621,12 +1643,12 @@ function import_webpage_element($element, $channel, $type) {
|
||||
$arr['created'] = $iteminfo[0]['created'];
|
||||
}
|
||||
else { // otherwise, generate the creation times and unique id
|
||||
$arr['created'] = datetime_convert('UTC', 'UTC');
|
||||
$arr['created'] = datetime_convert();
|
||||
$arr['uuid'] = item_message_id();
|
||||
$arr['mid'] = $arr['parent_mid'] = z_root() . '/item/' . $arr['uuid'];
|
||||
}
|
||||
// Update the edited time whether or not the element already exists
|
||||
$arr['edited'] = datetime_convert('UTC', 'UTC');
|
||||
$arr['edited'] = datetime_convert();
|
||||
// Import the actual element content
|
||||
$arr['body'] = file_get_contents($element['path']);
|
||||
// The element owner is the channel importing the elements
|
||||
|
@ -4278,7 +4278,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
|
||||
if($arr['mid'])
|
||||
$sql_options .= " and parent_mid = '" . dbesc($arr['mid']) . "' ";
|
||||
|
||||
$sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE item_thread_top = 1 $sql_options $item_normal ) ";
|
||||
$sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE $item_uids and item_thread_top = 1 $sql_options $item_normal ) ";
|
||||
|
||||
if($arr['since_id'])
|
||||
$sql_extra .= " and item.id > " . $since_id . " ";
|
||||
|
@ -33,6 +33,7 @@ function js_strings() {
|
||||
'$name_empty' => t('A channel name is required.'),
|
||||
'$name_ok1' => t('This is a '),
|
||||
'$name_ok2' => t(' channel name'),
|
||||
'$to_reply' => t('Back to reply'),
|
||||
|
||||
// translatable prefix and suffix strings for jquery.timeago -
|
||||
// using the defaults set below if left untranslated, empty strings if
|
||||
|
@ -76,6 +76,8 @@ function markdown_to_bb($s, $use_zrl = false, $options = []) {
|
||||
|
||||
$s = html2bbcode($s);
|
||||
|
||||
$s = bb_code_protect($s);
|
||||
|
||||
// Convert everything that looks like a link to a link
|
||||
if($use_zrl) {
|
||||
if (strpos($s,'[/img]') !== false) {
|
||||
@ -88,6 +90,8 @@ function markdown_to_bb($s, $use_zrl = false, $options = []) {
|
||||
$s = preg_replace("/([^\]\=\{\/]|^)(https?\:\/\/)([a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]+)/ismu", '$1[url=$2$3]$2$3[/url]',$s);
|
||||
}
|
||||
|
||||
$s = bb_code_unprotect($s);
|
||||
|
||||
// remove duplicate adjacent code tags
|
||||
$s = preg_replace("/(\[code\])+(.*?)(\[\/code\])+/ism","[code]$2[/code]", $s);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php /** @file */
|
||||
|
||||
use Zotlabs\Lib as Zlib;
|
||||
use Zotlabs\Lib\Cache;
|
||||
|
||||
|
||||
function oembed_replacecb($matches){
|
||||
@ -133,7 +133,6 @@ function oembed_fetch_url($embedurl){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$txt = null;
|
||||
|
||||
// we should try to cache this and avoid a lookup on each render
|
||||
@ -143,8 +142,20 @@ function oembed_fetch_url($embedurl){
|
||||
|
||||
$furl = ((local_channel() && $zrl) ? zid($embedurl) : $embedurl);
|
||||
|
||||
if($action !== 'block') {
|
||||
$txt = Zlib\Cache::get('[' . App::$videowidth . '] ' . $furl);
|
||||
if($action !== 'block' && (! get_config('system','oembed_cache_disable'))) {
|
||||
$txt = Cache::get('[' . App::$videowidth . '] ' . $furl);
|
||||
}
|
||||
|
||||
if(strpos(strtolower($embedurl),'.pdf') !== false) {
|
||||
$action = 'allow';
|
||||
$j = [
|
||||
'html' => '<object data="' . $embedurl . '" type="application/pdf" style="width: 100%; height: 300px;"></object>',
|
||||
'title' => t('View PDF'),
|
||||
'type' => 'pdf'
|
||||
];
|
||||
|
||||
// set $txt to something so that we don't attempt to fetch what could be a lengthy pdf.
|
||||
$txt = EMPTY_STR;
|
||||
}
|
||||
|
||||
if(is_null($txt)) {
|
||||
@ -215,17 +226,7 @@ function oembed_fetch_url($embedurl){
|
||||
// save in cache
|
||||
|
||||
if(! get_config('system','oembed_cache_disable'))
|
||||
Zlib\Cache::set('[' . App::$videowidth . '] ' . $furl, $txt);
|
||||
|
||||
}
|
||||
|
||||
if(strpos(strtolower($embedurl),'.pdf') !== false) {
|
||||
$action = 'allow';
|
||||
$j = [
|
||||
'html' => '<object data="' . $embedurl . '" type="application/pdf" style="width: 100%; height: 300px;"></object>',
|
||||
'title' => t('View PDF'),
|
||||
'type' => 'pdf'
|
||||
];
|
||||
Cache::set('[' . App::$videowidth . '] ' . $furl, $txt);
|
||||
|
||||
}
|
||||
|
||||
|
@ -277,8 +277,7 @@ function photo_upload($channel, $observer, $args) {
|
||||
if(($width > 1024 || $height > 1024) && (! $errors))
|
||||
$ph->scaleImage(1024);
|
||||
|
||||
$p['imgscale'] = 1;
|
||||
$r1 = $ph->save($p);
|
||||
$r1 = $ph->storeThumbnail($p, PHOTO_RES_1024);
|
||||
$link[1] = array(
|
||||
'rel' => 'alternate',
|
||||
'type' => 'text/html',
|
||||
@ -292,8 +291,7 @@ function photo_upload($channel, $observer, $args) {
|
||||
if(($width > 640 || $height > 640) && (! $errors))
|
||||
$ph->scaleImage(640);
|
||||
|
||||
$p['imgscale'] = 2;
|
||||
$r2 = $ph->save($p);
|
||||
$r2 = $ph->storeThumbnail($p, PHOTO_RES_640);
|
||||
$link[2] = array(
|
||||
'rel' => 'alternate',
|
||||
'type' => 'text/html',
|
||||
@ -307,8 +305,7 @@ function photo_upload($channel, $observer, $args) {
|
||||
if(($width > 320 || $height > 320) && (! $errors))
|
||||
$ph->scaleImage(320);
|
||||
|
||||
$p['imgscale'] = 3;
|
||||
$r3 = $ph->save($p);
|
||||
$r3 = $ph->storeThumbnail($p, PHOTO_RES_320);
|
||||
$link[3] = array(
|
||||
'rel' => 'alternate',
|
||||
'type' => 'text/html',
|
||||
|
@ -409,7 +409,8 @@ function autoname($len) {
|
||||
* @return string Escaped text.
|
||||
*/
|
||||
function xmlify($str) {
|
||||
$buffer = '';
|
||||
|
||||
//$buffer = '';
|
||||
|
||||
if(is_array($str)) {
|
||||
|
||||
@ -418,7 +419,7 @@ function xmlify($str) {
|
||||
|
||||
btlogger('xmlify called with array: ' . print_r($str,true), LOGGER_NORMAL, LOG_WARNING);
|
||||
}
|
||||
|
||||
/*
|
||||
$len = mb_strlen($str);
|
||||
for($x = 0; $x < $len; $x ++) {
|
||||
$char = mb_substr($str,$x,1);
|
||||
@ -452,6 +453,11 @@ function xmlify($str) {
|
||||
$buffer = trim($buffer);
|
||||
|
||||
return($buffer);
|
||||
*/
|
||||
$buffer = htmlspecialchars($str, ENT_QUOTES, "UTF-8");
|
||||
$buffer = trim($buffer);
|
||||
return $buffer;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -464,9 +470,22 @@ function xmlify($str) {
|
||||
* @return string
|
||||
*/
|
||||
function unxmlify($s) {
|
||||
/*
|
||||
$ret = str_replace('&', '&', $s);
|
||||
$ret = str_replace(array('<', '>', '"', '''), array('<', '>', '"', "'"), $ret);
|
||||
|
||||
return $ret;
|
||||
*/
|
||||
|
||||
if(is_array($s)) {
|
||||
|
||||
// allow to fall through so we ge a PHP error, as the log statement will
|
||||
// probably get lost in the noise unless we're specifically looking for it.
|
||||
|
||||
btlogger('unxmlify called with array: ' . print_r($s,true), LOGGER_NORMAL, LOG_WARNING);
|
||||
}
|
||||
|
||||
$ret = htmlspecialchars_decode($s, ENT_QUOTES);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@ -3080,12 +3099,11 @@ function item_url_replace($channel,&$item,$old,$new,$oldnick = '') {
|
||||
json_url_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['target']);
|
||||
}
|
||||
|
||||
$x = preg_replace("/".preg_quote($old,'/')."\/(search|\w+\/".$channel['channel_address'].")/", $new.'/${1}', $item['body']);
|
||||
if($x) {
|
||||
$item['body'] = $x;
|
||||
$item['body'] = preg_replace("/(\[zrl=".preg_quote($old,'/')."\/(photo|photos|gallery)\/".$channel['channel_address'].".+\]\[zmg=\d+x\d+\])".preg_quote($old,'/')."\/(.+\[\/zmg\])/", '${1}'.$new.'/${3}', $item['body']);
|
||||
$item['body'] = preg_replace("/".preg_quote($old,'/')."\/(search|\w+\/".$channel['channel_address'].")/", $new.'/${1}', $item['body']);
|
||||
|
||||
$item['sig'] = base64url_encode(rsa_sign($item['body'],$channel['channel_prvkey']));
|
||||
$item['item_verified'] = 1;
|
||||
}
|
||||
|
||||
$item['plink'] = str_replace($old,$new,$item['plink']);
|
||||
if($oldnick)
|
||||
|
@ -925,11 +925,27 @@ function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
|
||||
$local = q("select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1",
|
||||
dbesc($xchan_hash)
|
||||
);
|
||||
|
||||
if($local) {
|
||||
// @FIXME This should be removed in future when profile photo update by file sync procedure will be applied
|
||||
// on most hubs in the network
|
||||
// <---
|
||||
$ph = z_fetch_url($arr['photo'], true);
|
||||
|
||||
if($ph['success']) {
|
||||
|
||||
// Do not fetch already received thumbnails
|
||||
$x = q("SELECT resource_id FROM photo WHERE uid = %d AND imgscale = %d AND filesize = %d LIMIT 1",
|
||||
intval($local[0]['channel_id']),
|
||||
intval(PHOTO_RES_PROFILE_300),
|
||||
strlen($ph['body'])
|
||||
);
|
||||
|
||||
if($x)
|
||||
$hash = $x[0]['resource_id'];
|
||||
else
|
||||
$hash = import_channel_photo($ph['body'], $arr['photo_mimetype'], $local[0]['channel_account_id'], $local[0]['channel_id']);
|
||||
}
|
||||
|
||||
if($hash) {
|
||||
// unless proven otherwise
|
||||
@ -955,6 +971,7 @@ function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
|
||||
);
|
||||
}
|
||||
}
|
||||
// --->
|
||||
|
||||
// reset the names in case they got messed up when we had a bug in this function
|
||||
$photos = array(
|
||||
@ -965,7 +982,6 @@ function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$photos = import_xchan_photo($arr['photo'], $xchan_hash);
|
||||
}
|
||||
@ -3634,7 +3650,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) {
|
||||
'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall',
|
||||
'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall',
|
||||
'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish',
|
||||
'channel_a_delegate', 'channel_moved'
|
||||
'channel_a_delegate', 'channel_moved', 'channel_r_photos', 'channel_w_photos'
|
||||
];
|
||||
|
||||
$clean = array();
|
||||
@ -4414,7 +4430,7 @@ function zotinfo($arr) {
|
||||
|
||||
$profile['description'] = $p[0]['pdesc'];
|
||||
$profile['birthday'] = $p[0]['dob'];
|
||||
if(($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'],$e['channel_timezone'])) !== ''))
|
||||
if(($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'],'UTC')) !== ''))
|
||||
$profile['next_birthday'] = $bd;
|
||||
|
||||
if($age = age($p[0]['dob'],$e['channel_timezone'],''))
|
||||
|
@ -175,7 +175,7 @@ encounter a database configuration which cannot be expressed on the setup form
|
||||
(for instance using MySQL with an unusual socket location); you can supply
|
||||
the PDO connection string as the database hostname. For instance
|
||||
|
||||
mysql:unix_socket=/my/special/socket_path
|
||||
:/path/to/socket.file
|
||||
|
||||
You should still fill in all other applicable form values as needed.
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,7 @@
|
||||
|
||||
// Then set the following for your MySQL installation
|
||||
|
||||
$db_host = 'your.mysqlhost.com'; // Use 'localhost' if you aren't using a remote server
|
||||
$db_host = 'your.mysqlhost.com'; // Use 'localhost' or ':/path/to/socket.file' if you aren't using a remote server
|
||||
$db_port = 0; // leave 0 for default or set your port
|
||||
$db_user = 'mysqlusername';
|
||||
$db_pass = 'mysqlpassword';
|
||||
|
@ -269,24 +269,6 @@ CREATE TABLE IF NOT EXISTS `channel` (
|
||||
`channel_allow_gid` mediumtext NOT NULL,
|
||||
`channel_deny_cid` mediumtext NOT NULL,
|
||||
`channel_deny_gid` mediumtext NOT NULL,
|
||||
`channel_r_stream` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_r_profile` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_r_photos` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_r_abook` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_stream` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_wall` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_tagwall` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_comment` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_mail` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_photos` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_chat` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_a_delegate` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_r_storage` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_storage` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_r_pages` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_pages` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_a_republish` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_w_like` int(10) unsigned NOT NULL DEFAULT 0 ,
|
||||
`channel_removed` tinyint(1) NOT NULL DEFAULT 0 ,
|
||||
`channel_system` tinyint(1) NOT NULL DEFAULT 0 ,
|
||||
`channel_moved` char(191) NOT NULL DEFAULT '',
|
||||
@ -683,7 +665,7 @@ CREATE TABLE IF NOT EXISTS `item` (
|
||||
KEY `mimetype` (`mimetype`),
|
||||
KEY `mid` (`mid`),
|
||||
KEY `parent_mid` (`parent_mid`),
|
||||
KEY `uid_mid` (`mid`,`uid`),
|
||||
KEY `uid_mid` (`uid`,`mid`),
|
||||
KEY `comment_policy` (`comment_policy`),
|
||||
KEY `layout_mid` (`layout_mid`),
|
||||
KEY `public_policy` (`public_policy`),
|
||||
@ -1342,7 +1324,8 @@ CREATE TABLE IF NOT EXISTS `xchan` (
|
||||
KEY `xchan_selfcensored` (`xchan_selfcensored`),
|
||||
KEY `xchan_system` (`xchan_system`),
|
||||
KEY `xchan_pubforum` (`xchan_pubforum`),
|
||||
KEY `xchan_deleted` (`xchan_deleted`)
|
||||
KEY `xchan_deleted` (`xchan_deleted`),
|
||||
KEY `xchan_photo_m` (`xchan_photo_m`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xchat` (
|
||||
|
@ -650,7 +650,7 @@ create index "item_revision" on item ("revision");
|
||||
create index "item_mimetype" on item ("mimetype");
|
||||
create index "item_mid" on item ("mid");
|
||||
create index "item_parent_mid" on item ("parent_mid");
|
||||
create index "item_uid_mid" on item ("mid","uid");
|
||||
create index "item_uid_mid" on item ("uid","mid");
|
||||
create index "item_public_policy" on item ("public_policy");
|
||||
create index "item_comment_policy" on item ("comment_policy");
|
||||
create index "item_layout_mid" on item ("layout_mid");
|
||||
@ -1312,6 +1312,7 @@ create index "xchan_selfcensored" on xchan ("xchan_selfcensored");
|
||||
create index "xchan_system" on xchan ("xchan_system");
|
||||
create index "xchan_pubforum" on xchan ("xchan_pubforum");
|
||||
create index "xchan_deleted" on xchan ("xchan_deleted");
|
||||
create index "xchan_photo_m" on xchan ("xchan_photo_m");
|
||||
|
||||
CREATE TABLE "xchat" (
|
||||
"xchat_id" serial NOT NULL,
|
||||
|
5
library/jgrowl/.gitignore
vendored
Normal file
5
library/jgrowl/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
jGrowl.iml
|
||||
jGrowl.ipr
|
||||
jGrowl.iws
|
61
library/jgrowl/Gruntfile.js
Normal file
61
library/jgrowl/Gruntfile.js
Normal file
@ -0,0 +1,61 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
uglify: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
sourceMapName: 'jquery.jgrowl.map'
|
||||
},
|
||||
jgrowl: {
|
||||
files: {
|
||||
'jquery.jgrowl.min.js': ['jquery.jgrowl.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
less: {
|
||||
jgrowl: {
|
||||
files: {
|
||||
"jquery.jgrowl.css": "less/jgrowl.less"
|
||||
}
|
||||
}
|
||||
},
|
||||
cssmin: {
|
||||
jgrowl: {
|
||||
expand: true,
|
||||
src: 'jquery.jgrowl.css',
|
||||
ext: '.jgrowl.min.css'
|
||||
}
|
||||
},
|
||||
jshint: {
|
||||
files: ['Gruntfile.js', 'jquery.jgrowl.js'],
|
||||
options: {
|
||||
// options here to override JSHint defaults
|
||||
globals: {
|
||||
jQuery: true,
|
||||
console: true,
|
||||
module: true,
|
||||
document: true
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
scripts: {
|
||||
files: ['jquery.jgrowl.js', 'less/*'],
|
||||
tasks: ['jshint', 'less', 'cssmin', 'uglify'],
|
||||
options: {
|
||||
spawn: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-cssmin');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
|
||||
grunt.registerTask('test', ['jshint']);
|
||||
grunt.registerTask('default', ['jshint', 'uglify', 'less', 'cssmin']);
|
||||
};
|
7
library/jgrowl/LICENSE
Normal file
7
library/jgrowl/LICENSE
Normal file
@ -0,0 +1,7 @@
|
||||
Copyright (c) 2012 Stan Lemon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,3 +0,0 @@
|
||||
https://github.com/stanlemon/jGrowl
|
||||
|
||||
jGrowl is free and open source, it's distributed under the MIT and GPL licenses
|
75
library/jgrowl/README.md
Normal file
75
library/jgrowl/README.md
Normal file
@ -0,0 +1,75 @@
|
||||
# jGrowl
|
||||
jGrowl is a jQuery plugin that raises unobtrusive messages within the browser, similar to the way that OS X's Growl Framework works. The idea is simple, deliver notifications to the end user in a noticeable way that doesn't obstruct the work flow and yet keeps the user informed.
|
||||
|
||||
## Installation
|
||||
jGrowl can be added to your project using package managers like bower and npm or directly into your html using cdnjs, as well as the good old fashioned copy-paste into your project directory.
|
||||
|
||||
Use cdnjs:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.4.1/jquery.jgrowl.min.css" />
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.4.1/jquery.jgrowl.min.js"></script>
|
||||
```
|
||||
|
||||
Install with bower
|
||||
|
||||
```
|
||||
bower install jgrowl
|
||||
```
|
||||
|
||||
Install with npm
|
||||
|
||||
```
|
||||
npm install jgrowl
|
||||
```
|
||||
|
||||
```js
|
||||
// Sample 1
|
||||
$.jGrowl("Hello world!");
|
||||
// Sample 2
|
||||
$.jGrowl("Stick this!", { sticky: true });
|
||||
// Sample 3
|
||||
$.jGrowl("A message with a header", { header: 'Important' });
|
||||
// Sample 4
|
||||
$.jGrowl("A message that will live a little longer.", { life: 10000 });
|
||||
// Sample 5
|
||||
$.jGrowl("A message with a beforeOpen callback and a different opening animation.", {
|
||||
beforeClose: function(e,m) {
|
||||
alert('About to close this notification!');
|
||||
},
|
||||
animateOpen: {
|
||||
height: 'show'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
| Option | Default | Description |
|
||||
|------------------|--------------------------------------|------------------------------------------------------------|
|
||||
| pool | 0 | Limit the number of messages appearing at a given time to the number in the pool. |
|
||||
| header | empty | Optional header to prefix the message, this is often helpful for associating messages to each other. |
|
||||
| group | empty | A css class to be applied to notifications when they are created, useful for 'grouping' notifications by a css selector. |
|
||||
| sticky | false | When set to true a message will stick to the screen until it is intentionally closed by the user. |
|
||||
| position | top-right | Designates a class which is applied to the jGrowl container and controls its position on the screen. By Default there are five options available, top-left, top-right, bottom-left, bottom-right, center. This must be changed in the defaults before the startup method is called. |
|
||||
| appendTo | body | The element where our jGrowl messages are appended to. The default is `body` but feel free to define another one. |
|
||||
| glue | after | Designates whether a jGrowl notification should be appended to the container after all notifications, or whether it should be prepended to the container before all notifications. Options are after or before. |
|
||||
| theme | default | A CSS class designating custom styling for this particular message, intended for use with jQuery UI. |
|
||||
| themeState | highlight | A CSS class designating custom styling for this particular message and its state, intended for use with jQuery UI. |
|
||||
| corners | 10px | If the corners jQuery plugin is include this option specifies the curvature radius to be used for the notifications as they are created. |
|
||||
| check | 250 | The frequency that jGrowl should check for messages to be scrubbed from the screen.This must be changed in the defaults before the startup method is called. |
|
||||
| life | 3000 | The lifespan of a non-sticky message on the screen. |
|
||||
| closeDuration | normal | The animation speed used to close a notification. |
|
||||
| openDuration | normal | The animation speed used to open a notification. |
|
||||
| easing | swing | The easing method to be used with the animation for opening and closing a notification. |
|
||||
| closer | true | Whether or not the close-all button should be used when more then one notification appears on the screen. Optionally this property can be set to a function which will be used as a callback when the close all button is clicked. This must be changed in the defaults before the startup method is called. |
|
||||
| closeTemplate | × | This content is used for the individual notification close links that are added to the corner of a notification. This must be changed in the defaults before the startup method is called. |
|
||||
| closerTemplate | <div>[ close all ]</div> | This content is used for the close-all link that is added to the bottom of a jGrowl container when it contains more than one notification. This must be changed in the defaults before the startup method is called. |
|
||||
| log | function(e,m,o) {} | Callback to be used before anything is done with the notification. This is intended to be used if the user would like to have some type of logging mechanism for all notifications passed to jGrowl. This callback receives the notification's DOM context, the notification's message and its option object. |
|
||||
| beforeOpen | function(e,m,o) {} | Callback to be used before a new notification is opened. This callback receives the notification's DOM context, the notification's message and its option object. |
|
||||
| afterOpen | function(e,m,o) {} | Callback to be used after a new notification is opened. This callback receives the notification's DOM context, the notification's message and its option object. |
|
||||
| open | function(e,m,o) {} | Callback to be used when a new notification is opened. This callback receives the notification's DOM context, the notifications message and its option object. |
|
||||
| beforeClose | function(e,m,o) {} | Callback to be used before a new notification is closed. This callback receives the notification's DOM context, the notification's message and its option object. |
|
||||
| close | function(e,m,o) {} | Callback to be used when a new notification is closed. This callback receives the notification's DOM context, the notification's message and its option object. |
|
||||
| click | function(e,m,o) {} | Callback to be used when a notification is clicked. This callback receives the notification's DOM context, the notification's message and its option object. |
|
||||
| animateOpen | { opacity: 'show' } | The animation properties to use when opening a new notification (default to fadeOut). |
|
||||
| animateClose | { opacity: 'hide' } | The animation properties to use when closing a new notification (defaults to fadeIn). |
|
32
library/jgrowl/bower.json
Normal file
32
library/jgrowl/bower.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "jGrowl",
|
||||
"version": "1.4.5",
|
||||
"homepage": "https://github.com/stanlemon/jGrowl",
|
||||
"authors": [
|
||||
"Stan Lemon <stosh1985@gmail.com>"
|
||||
],
|
||||
"description": "jGrowl is an unobtrusive notification system for web applications.",
|
||||
"main": [
|
||||
"jquery.jgrowl.css",
|
||||
"jquery.jgrowl.js"
|
||||
],
|
||||
"keywords": [
|
||||
"growl",
|
||||
"jgrowl",
|
||||
"jquery",
|
||||
"toaster",
|
||||
"notification",
|
||||
"message"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.4"
|
||||
},
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
103
library/jgrowl/examples/appendTo.html
Normal file
103
library/jgrowl/examples/appendTo.html
Normal file
@ -0,0 +1,103 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml" debug="true">
|
||||
<head>
|
||||
<title>jGrowl Examples</title>
|
||||
<link rel="stylesheet" href="../jquery.jgrowl.css" type="text/css"/>
|
||||
<style type="text/css">
|
||||
html,
|
||||
body {
|
||||
font-family: "Helvetica neue", Helvetica, Arial, sans-serif;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.jGrowl .manilla {
|
||||
background-color: #FFF1C2;
|
||||
color: navy;
|
||||
}
|
||||
|
||||
.jGrowl .flora {
|
||||
background: #E6F7D4 url(flora-notification.png) no-repeat;
|
||||
-moz-border-radius: 0px;
|
||||
-webkit-border-radius: 0px;
|
||||
opacity: 1;
|
||||
filter: alpha(opacity = 100);
|
||||
width: 270px;
|
||||
height: 90px;
|
||||
padding: 0px;
|
||||
overflow: hidden;
|
||||
border-color: #5ab500;
|
||||
}
|
||||
|
||||
.jGrowl .flora .message {
|
||||
padding: 5px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.jGrowl .flora .header {
|
||||
background: url(flora-header.png) no-repeat;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.jGrowl .flora .close {
|
||||
background: url(flora-close.png) no-repeat;
|
||||
padding: 5px;
|
||||
color: transparent;
|
||||
padding: 0px;
|
||||
margin: 5px;
|
||||
width: 17px;
|
||||
}
|
||||
|
||||
#dummyNav {
|
||||
position: fixed;
|
||||
background: green;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
|
||||
<script type="text/javascript" src="../jquery.jgrowl.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// In case you don't have firebug...
|
||||
if(typeof console === "undefined") {
|
||||
console = { log: function() { } };
|
||||
}
|
||||
|
||||
(function($){
|
||||
|
||||
$(function(){
|
||||
|
||||
$.jGrowl.defaults.closerTemplate = "<div>[ xxxxx ]</div>";
|
||||
$.jGrowl.defaults.appendTo = "div#dummyNav";
|
||||
|
||||
$.jGrowl("This message is sticky and clickable", {
|
||||
sticky: true,
|
||||
click: function(msg) {
|
||||
alert("You clicked me");
|
||||
}
|
||||
});
|
||||
|
||||
$.jGrowl("This message is sticky and clickable");
|
||||
$.jGrowl("This message is sticky and clickable");
|
||||
$.jGrowl("This message is sticky and clickable");
|
||||
$.jGrowl("This message is sticky and clickable");
|
||||
$.jGrowl("This message is sticky and clickable");
|
||||
$.jGrowl("This message is sticky and clickable");
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="dummyNav">I'm the dummy navigation</div>
|
||||
|
||||
<h1>Append To Another DOM-Object</h1>
|
||||
</body>
|
||||
</html>
|
91
library/jgrowl/examples/bootstrap.html
Normal file
91
library/jgrowl/examples/bootstrap.html
Normal file
@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>jGrowl and Bootstrap</title>
|
||||
|
||||
<link rel="stylesheet" href="../jquery.jgrowl.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
|
||||
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
||||
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<style>
|
||||
body {
|
||||
padding-top: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="jGrowl-container1" class="jGrowl top-right"></div>
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">jGrowl</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="active"><a href="#">Home</a></li>
|
||||
<li><a href="#about">About</a></li>
|
||||
<li><a href="#contact">Contact</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div>
|
||||
<h1>jGrowl and Bootstrap</h1>
|
||||
<p class="lead">This is a Bootstrap starter template with jGrowl integrated. As you can see it's extremely easy to make jGrowl look and feel like part of Bootstrap.</p>
|
||||
</div>
|
||||
<div id="jGrowl-container2" class="jGrowl"></div>
|
||||
</div><!-- /.container -->
|
||||
|
||||
|
||||
<!-- Bootstrap core JavaScript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
|
||||
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../jquery.jgrowl.js"></script>
|
||||
<script type="text/javascript">
|
||||
(function($){
|
||||
$(function(){
|
||||
$.jGrowl.defaults.closerTemplate = '<div class="alert alert-info">Close All</div>';
|
||||
|
||||
var alertTypes = ['success', 'info', 'warning', 'danger'];
|
||||
|
||||
for (var i=0; i<10; i++) {
|
||||
setTimeout(function(){
|
||||
var alertType = alertTypes[Math.floor(Math.random()*alertTypes.length)];
|
||||
|
||||
$('#jGrowl-container1').jGrowl({
|
||||
header: alertType.substring(0, 1).toUpperCase() + alertType.substring(1) + ' Notification',
|
||||
message: 'Hello world ',
|
||||
group: 'alert-' + alertType,
|
||||
life: 5000
|
||||
});
|
||||
}, i*2000);
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
171
library/jgrowl/examples/jgrowl.html
Executable file
171
library/jgrowl/examples/jgrowl.html
Executable file
@ -0,0 +1,171 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml" debug="true">
|
||||
<head>
|
||||
<title>jGrowl Examples</title>
|
||||
<link rel="stylesheet" href="../jquery.jgrowl.css" type="text/css"/>
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: "Helvetica neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.jGrowl .manilla {
|
||||
background-color: #FFF1C2;
|
||||
color: navy;
|
||||
}
|
||||
|
||||
.jGrowl .flora {
|
||||
background: #E6F7D4 url(flora-notification.png) no-repeat;
|
||||
-moz-border-radius: 0px;
|
||||
-webkit-border-radius: 0px;
|
||||
opacity: 1;
|
||||
filter: alpha(opacity = 100);
|
||||
width: 270px;
|
||||
height: 90px;
|
||||
padding: 0px;
|
||||
overflow: hidden;
|
||||
border-color: #5ab500;
|
||||
}
|
||||
|
||||
.jGrowl .flora .message {
|
||||
padding: 5px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.jGrowl .flora .header {
|
||||
background: url(flora-header.png) no-repeat;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.jGrowl .flora .close {
|
||||
background: url(flora-close.png) no-repeat;
|
||||
padding: 5px;
|
||||
color: transparent;
|
||||
padding: 0px;
|
||||
margin: 5px;
|
||||
width: 17px;
|
||||
}
|
||||
|
||||
#random {
|
||||
padding: 20px;
|
||||
width: 1500px;
|
||||
background-color: #ff7070;
|
||||
}
|
||||
|
||||
#logs {
|
||||
margin-top: 30px;
|
||||
background-color: #efefef;
|
||||
border: 1px solid #aaa;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
|
||||
<script type="text/javascript" src="../jquery.jgrowl.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// In case you don't have firebug...
|
||||
if(typeof console === "undefined") {
|
||||
console = { log: function() { } };
|
||||
}
|
||||
|
||||
(function($){
|
||||
|
||||
$(function(){
|
||||
|
||||
$.jGrowl.defaults.pool = 5;
|
||||
|
||||
$.jGrowl.defaults.closerTemplate = '<div>hide all notifications</div>';
|
||||
|
||||
// This value can be true, false or a function to be used as a callback when the closer is clciked
|
||||
$.jGrowl.defaults.closer = function() {
|
||||
console.log("Closing everything!", this);
|
||||
};
|
||||
|
||||
// A callback for logging notifications.
|
||||
$.jGrowl.defaults.log = function(e,m,o) {
|
||||
$('#logs').append("<div><strong>#" + $(e).attr('id') + "</strong> <em>" + (new Date()).getTime() + "</em>: " + m + " (" + o.theme + ")</div>")
|
||||
}
|
||||
|
||||
$.jGrowl("Sticky notifications don't have an end of life", { sticky: true });
|
||||
$.jGrowl("Custom theme, custom animations, header, longer life and a whole bunch of callbacks...", {
|
||||
header: 'Header',
|
||||
life: 5000,
|
||||
theme: 'manilla',
|
||||
speed: 'slow',
|
||||
beforeOpen: function(e,m,o) {
|
||||
console.log("I am going to be opened!", this);
|
||||
},
|
||||
open: function(e,m,o) {
|
||||
console.log("I have been opened!", this);
|
||||
},
|
||||
beforeClose: function(e,m,o) {
|
||||
console.log("I am going to be closed!", this);
|
||||
},
|
||||
close: function(e,m,o) {
|
||||
console.log("I have been closed!", this);
|
||||
},
|
||||
animateOpen: {
|
||||
height: "show",
|
||||
width: "show"
|
||||
},
|
||||
animateClose: {
|
||||
height: "hide",
|
||||
width: "show"
|
||||
}
|
||||
});
|
||||
|
||||
$.jGrowl("This message will not open because we have a callback that returns false.", {
|
||||
beforeOpen: function() {
|
||||
console.log("Going to open a notification, but not really...");
|
||||
},
|
||||
open: function() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$.jGrowl("This message will not close because we have a callback that returns false.", {
|
||||
beforeClose: function() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$('#test1').jGrowl("Testing a custom container.", {
|
||||
closer: false,
|
||||
sticky: true,
|
||||
glue: 'before'
|
||||
});
|
||||
|
||||
$('#test1').jGrowl("This will be prepended before the last message.", {
|
||||
glue: 'before'
|
||||
});
|
||||
|
||||
$.jGrowl("This message is sticky and clickable", {
|
||||
sticky: true,
|
||||
click: function(msg) {
|
||||
alert("You clicked me");
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>jGrowl Tests</h1>
|
||||
|
||||
<p><a href="javascript:void(0);" onclick="$.jGrowl('One more message...');">Create a new message.</a></p>
|
||||
|
||||
<p><a href="javascript:void(0);" onclick="$('#test1').jGrowl('shutdown');">Shutdown bottom-left container.</a></p>
|
||||
|
||||
<div id="random">An extra wide node, watch as the jGrowl containers stay put in the corners of the screen..</div>
|
||||
|
||||
<div id="logs"><h3>Log:</h3></div>
|
||||
|
||||
<div id="test1" class="bottom-left"></div>
|
||||
</body>
|
||||
</html>
|
40
library/jgrowl/jgrowl.jquery.json
Normal file
40
library/jgrowl/jgrowl.jquery.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "jgrowl",
|
||||
"filename": "jquery.jgrowl.min.js",
|
||||
"title": "jGrowl",
|
||||
"description": "jGrowl is a jQuery plugin that raises unobtrusive messages within the browser, similar to the way that OS X's Growl Framework works. The idea is simple, deliver notifications to the end user in a noticeable way that doesn't obstruct the work flow and yet keeps the user informed.",
|
||||
"keywords": [
|
||||
"growl",
|
||||
"jgrowl",
|
||||
"jquery",
|
||||
"toaster",
|
||||
"notification",
|
||||
"message"
|
||||
],
|
||||
"version": "1.4.3",
|
||||
"author": {
|
||||
"name": "Stan Lemon",
|
||||
"email": "stosh1985@gmail.com",
|
||||
"url": "http://stanlemon.net"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Stan Lemon",
|
||||
"email": "stosh1985@gmail.com",
|
||||
"url": "http://stanlemon.net"
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/stanlemon/jGrowl/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"bugs": "https://github.com/stanlemon/jGrowl/issues",
|
||||
"homepage": "https://github.com/stanlemon/jGrowl",
|
||||
"docs": "https://github.com/stanlemon/jGrowl",
|
||||
"download": "https://github.com/stanlemon/jGrowl/archive/master.zip",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.4"
|
||||
}
|
||||
}
|
@ -1 +1,100 @@
|
||||
.jGrowl{z-index:9999;color:#fff;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;position:fixed}.jGrowl.top-left{left:0;top:0}.jGrowl.top-right{right:0;top:0}.jGrowl.bottom-left{left:0;bottom:0}.jGrowl.bottom-right{right:0;bottom:0}.jGrowl.center{top:0;width:50%;left:25%}.jGrowl.center .jGrowl-closer,.jGrowl.center .jGrowl-notification{margin-left:auto;margin-right:auto}.jGrowl-notification{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;text-align:left;display:none;border-radius:5px;min-height:40px}.jGrowl-notification .ui-state-highlight,.jGrowl-notification .ui-widget-content .ui-state-highlight,.jGrowl-notification .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}.jGrowl-notification .jGrowl-header{font-weight:700;font-size:.85em}.jGrowl-notification .jGrowl-close{background-color:transparent;color:inherit;border:none;z-index:99;float:right;font-weight:700;font-size:1em;cursor:pointer}.jGrowl-closer{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;display:none;border-radius:5px;padding-top:4px;padding-bottom:4px;cursor:pointer;font-size:.9em;font-weight:700;text-align:center}.jGrowl-closer .ui-state-highlight,.jGrowl-closer .ui-widget-content .ui-state-highlight,.jGrowl-closer .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}@media print{.jGrowl{display:none}}
|
||||
.jGrowl {
|
||||
z-index: 9999;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
position: fixed;
|
||||
}
|
||||
.jGrowl.top-left {
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
.jGrowl.top-right {
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
.jGrowl.bottom-left {
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
.jGrowl.bottom-right {
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
.jGrowl.center {
|
||||
top: 0px;
|
||||
width: 50%;
|
||||
left: 25%;
|
||||
}
|
||||
.jGrowl.center .jGrowl-notification,
|
||||
.jGrowl.center .jGrowl-closer {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.jGrowl-notification {
|
||||
background-color: #000000;
|
||||
opacity: 0.9;
|
||||
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));
|
||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));
|
||||
zoom: 1;
|
||||
width: 250px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
text-align: left;
|
||||
display: none;
|
||||
border-radius: 5px;
|
||||
min-height: 40px;
|
||||
}
|
||||
.jGrowl-notification .ui-state-highlight,
|
||||
.jGrowl-notification .ui-widget-content .ui-state-highlight,
|
||||
.jGrowl-notification .ui-widget-header .ui-state-highlight {
|
||||
border: 1px solid #000;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
.jGrowl-notification .jGrowl-header {
|
||||
font-weight: bold;
|
||||
font-size: .85em;
|
||||
}
|
||||
.jGrowl-notification .jGrowl-close {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
border: none;
|
||||
z-index: 99;
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
.jGrowl-closer {
|
||||
background-color: #000000;
|
||||
opacity: 0.9;
|
||||
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));
|
||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));
|
||||
zoom: 1;
|
||||
width: 250px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
text-align: left;
|
||||
display: none;
|
||||
border-radius: 5px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
cursor: pointer;
|
||||
font-size: .9em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
.jGrowl-closer .ui-state-highlight,
|
||||
.jGrowl-closer .ui-widget-content .ui-state-highlight,
|
||||
.jGrowl-closer .ui-widget-header .ui-state-highlight {
|
||||
border: 1px solid #000;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
/** Hide jGrowl when printing **/
|
||||
@media print {
|
||||
.jGrowl {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
399
library/jgrowl/jquery.jgrowl.js
Executable file
399
library/jgrowl/jquery.jgrowl.js
Executable file
@ -0,0 +1,399 @@
|
||||
/**
|
||||
* jGrowl 1.4.5
|
||||
*
|
||||
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
|
||||
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
|
||||
*
|
||||
* Written by Stan Lemon <stosh1985@gmail.com>
|
||||
* Last updated: 2015.02.01
|
||||
*
|
||||
* jGrowl is a jQuery plugin implementing unobtrusive userland notifications. These
|
||||
* notifications function similarly to the Growl Framework available for
|
||||
* Mac OS X (http://growl.info).
|
||||
*
|
||||
* To Do:
|
||||
* - Move library settings to containers and allow them to be changed per container
|
||||
*
|
||||
* Changes in 1.4.5
|
||||
* - Fixed arguement list for click callback, thanks @timotheeg
|
||||
*
|
||||
* Changes in 1.4.4
|
||||
* - Revert word-break changes, thanks @curtisgibby
|
||||
*
|
||||
* Changes in 1.4.3
|
||||
* - Fixed opactiy in LESS for older version of IE
|
||||
*
|
||||
* Changes in 1.4.2
|
||||
* - Added word-break to less/css
|
||||
*
|
||||
* Changes in 1.4.1
|
||||
* - Added appendTo option
|
||||
* - jQuery compatibility updates
|
||||
* - Add check for closing a notification before it opens
|
||||
*
|
||||
* Changes in 1.4.0
|
||||
* - Removed IE6 support
|
||||
* - Added LESS support
|
||||
*
|
||||
* Changes in 1.3.0
|
||||
* - Added non-vendor border-radius to stylesheet
|
||||
* - Added grunt for generating minified js and css
|
||||
* - Added npm package info
|
||||
* - Added bower package info
|
||||
* - Updates for jshint
|
||||
*
|
||||
* Changes in 1.2.13
|
||||
* - Fixed clearing interval when the container shuts down
|
||||
*
|
||||
* Changes in 1.2.12
|
||||
* - Added compressed versions using UglifyJS and Sqwish
|
||||
* - Improved README with configuration options explanation
|
||||
* - Added a source map
|
||||
*
|
||||
* Changes in 1.2.11
|
||||
* - Fix artifacts left behind by the shutdown method and text-cleanup
|
||||
*
|
||||
* Changes in 1.2.10
|
||||
* - Fix beforeClose to be called in click event
|
||||
*
|
||||
* Changes in 1.2.9
|
||||
* - Fixed BC break in jQuery 2.0 beta
|
||||
*
|
||||
* Changes in 1.2.8
|
||||
* - Fixes for jQuery 1.9 and the MSIE6 check, note that with jQuery 2.0 support
|
||||
* jGrowl intends to drop support for IE6 altogether
|
||||
*
|
||||
* Changes in 1.2.6
|
||||
* - Fixed js error when a notification is opening and closing at the same time
|
||||
*
|
||||
* Changes in 1.2.5
|
||||
* - Changed wrapper jGrowl's options usage to "o" instead of $.jGrowl.defaults
|
||||
* - Added themeState option to control 'highlight' or 'error' for jQuery UI
|
||||
* - Ammended some CSS to provide default positioning for nested usage.
|
||||
* - Changed some CSS to be prefixed with jGrowl- to prevent namespacing issues
|
||||
* - Added two new options - openDuration and closeDuration to allow
|
||||
* better control of notification open and close speeds, respectively
|
||||
* Patch contributed by Jesse Vincet.
|
||||
* - Added afterOpen callback. Patch contributed by Russel Branca.
|
||||
*
|
||||
* Changes in 1.2.4
|
||||
* - Fixed IE bug with the close-all button
|
||||
* - Fixed IE bug with the filter CSS attribute (special thanks to gotwic)
|
||||
* - Update IE opacity CSS
|
||||
* - Changed font sizes to use "em", and only set the base style
|
||||
*
|
||||
* Changes in 1.2.3
|
||||
* - The callbacks no longer use the container as context, instead they use the actual notification
|
||||
* - The callbacks now receive the container as a parameter after the options parameter
|
||||
* - beforeOpen and beforeClose now check the return value, if it's false - the notification does
|
||||
* not continue. The open callback will also halt execution if it returns false.
|
||||
* - Fixed bug where containers would get confused
|
||||
* - Expanded the pause functionality to pause an entire container.
|
||||
*
|
||||
* Changes in 1.2.2
|
||||
* - Notification can now be theme rolled for jQuery UI, special thanks to Jeff Chan!
|
||||
*
|
||||
* Changes in 1.2.1
|
||||
* - Fixed instance where the interval would fire the close method multiple times.
|
||||
* - Added CSS to hide from print media
|
||||
* - Fixed issue with closer button when div { position: relative } is set
|
||||
* - Fixed leaking issue with multiple containers. Special thanks to Matthew Hanlon!
|
||||
*
|
||||
* Changes in 1.2.0
|
||||
* - Added message pooling to limit the number of messages appearing at a given time.
|
||||
* - Closing a notification is now bound to the notification object and triggered by the close button.
|
||||
*
|
||||
* Changes in 1.1.2
|
||||
* - Added iPhone styled example
|
||||
* - Fixed possible IE7 bug when determining if the ie6 class shoudl be applied.
|
||||
* - Added template for the close button, so that it's content could be customized.
|
||||
*
|
||||
* Changes in 1.1.1
|
||||
* - Fixed CSS styling bug for ie6 caused by a mispelling
|
||||
* - Changes height restriction on default notifications to min-height
|
||||
* - Added skinned examples using a variety of images
|
||||
* - Added the ability to customize the content of the [close all] box
|
||||
* - Added jTweet, an example of using jGrowl + Twitter
|
||||
*
|
||||
* Changes in 1.1.0
|
||||
* - Multiple container and instances.
|
||||
* - Standard $.jGrowl() now wraps $.fn.jGrowl() by first establishing a generic jGrowl container.
|
||||
* - Instance methods of a jGrowl container can be called by $.fn.jGrowl(methodName)
|
||||
* - Added glue preferenced, which allows notifications to be inserted before or after nodes in the container
|
||||
* - Added new log callback which is called before anything is done for the notification
|
||||
* - Corner's attribute are now applied on an individual notification basis.
|
||||
*
|
||||
* Changes in 1.0.4
|
||||
* - Various CSS fixes so that jGrowl renders correctly in IE6.
|
||||
*
|
||||
* Changes in 1.0.3
|
||||
* - Fixed bug with options persisting across notifications
|
||||
* - Fixed theme application bug
|
||||
* - Simplified some selectors and manipulations.
|
||||
* - Added beforeOpen and beforeClose callbacks
|
||||
* - Reorganized some lines of code to be more readable
|
||||
* - Removed unnecessary this.defaults context
|
||||
* - If corners plugin is present, it's now customizable.
|
||||
* - Customizable open animation.
|
||||
* - Customizable close animation.
|
||||
* - Customizable animation easing.
|
||||
* - Added customizable positioning (top-left, top-right, bottom-left, bottom-right, center)
|
||||
*
|
||||
* Changes in 1.0.2
|
||||
* - All CSS styling is now external.
|
||||
* - Added a theme parameter which specifies a secondary class for styling, such
|
||||
* that notifications can be customized in appearance on a per message basis.
|
||||
* - Notification life span is now customizable on a per message basis.
|
||||
* - Added the ability to disable the global closer, enabled by default.
|
||||
* - Added callbacks for when a notification is opened or closed.
|
||||
* - Added callback for the global closer.
|
||||
* - Customizable animation speed.
|
||||
* - jGrowl now set itself up and tears itself down.
|
||||
*
|
||||
* Changes in 1.0.1:
|
||||
* - Removed dependency on metadata plugin in favor of .data()
|
||||
* - Namespaced all events
|
||||
*/
|
||||
(function($) {
|
||||
/** jGrowl Wrapper - Establish a base jGrowl Container for compatibility with older releases. **/
|
||||
$.jGrowl = function( m , o ) {
|
||||
// To maintain compatibility with older version that only supported one instance we'll create the base container.
|
||||
if ( $('#jGrowl').length === 0 )
|
||||
$('<div id="jGrowl"></div>').addClass( (o && o.position) ? o.position : $.jGrowl.defaults.position ).appendTo( (o && o.appendTo) ? o.appendTo : $.jGrowl.defaults.appendTo );
|
||||
|
||||
// Create a notification on the container.
|
||||
$('#jGrowl').jGrowl(m,o);
|
||||
};
|
||||
|
||||
|
||||
/** Raise jGrowl Notification on a jGrowl Container **/
|
||||
$.fn.jGrowl = function( m , o ) {
|
||||
// Short hand for passing in just an object to this method
|
||||
if ( o === undefined && $.isPlainObject(m) ) {
|
||||
o = m;
|
||||
m = o.message;
|
||||
}
|
||||
|
||||
if ( $.isFunction(this.each) ) {
|
||||
var args = arguments;
|
||||
|
||||
return this.each(function() {
|
||||
/** Create a jGrowl Instance on the Container if it does not exist **/
|
||||
if ( $(this).data('jGrowl.instance') === undefined ) {
|
||||
$(this).data('jGrowl.instance', $.extend( new $.fn.jGrowl(), { notifications: [], element: null, interval: null } ));
|
||||
$(this).data('jGrowl.instance').startup( this );
|
||||
}
|
||||
|
||||
/** Optionally call jGrowl instance methods, or just raise a normal notification **/
|
||||
if ( $.isFunction($(this).data('jGrowl.instance')[m]) ) {
|
||||
$(this).data('jGrowl.instance')[m].apply( $(this).data('jGrowl.instance') , $.makeArray(args).slice(1) );
|
||||
} else {
|
||||
$(this).data('jGrowl.instance').create( m , o );
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.extend( $.fn.jGrowl.prototype , {
|
||||
|
||||
/** Default JGrowl Settings **/
|
||||
defaults: {
|
||||
pool: 0,
|
||||
header: '',
|
||||
group: '',
|
||||
sticky: false,
|
||||
position: 'top-right',
|
||||
appendTo: 'body',
|
||||
glue: 'after',
|
||||
theme: 'default',
|
||||
themeState: 'highlight',
|
||||
corners: '10px',
|
||||
check: 250,
|
||||
life: 3000,
|
||||
closeDuration: 'normal',
|
||||
openDuration: 'normal',
|
||||
easing: 'swing',
|
||||
closer: true,
|
||||
closeTemplate: '×',
|
||||
closerTemplate: '<div>[ close all ]</div>',
|
||||
log: function() {},
|
||||
beforeOpen: function() {},
|
||||
afterOpen: function() {},
|
||||
open: function() {},
|
||||
beforeClose: function() {},
|
||||
close: function() {},
|
||||
click: function() {},
|
||||
animateOpen: {
|
||||
opacity: 'show'
|
||||
},
|
||||
animateClose: {
|
||||
opacity: 'hide'
|
||||
}
|
||||
},
|
||||
|
||||
notifications: [],
|
||||
|
||||
/** jGrowl Container Node **/
|
||||
element: null,
|
||||
|
||||
/** Interval Function **/
|
||||
interval: null,
|
||||
|
||||
/** Create a Notification **/
|
||||
create: function( message , options ) {
|
||||
var o = $.extend({}, this.defaults, options);
|
||||
|
||||
/* To keep backward compatibility with 1.24 and earlier, honor 'speed' if the user has set it */
|
||||
if (typeof o.speed !== 'undefined') {
|
||||
o.openDuration = o.speed;
|
||||
o.closeDuration = o.speed;
|
||||
}
|
||||
|
||||
this.notifications.push({ message: message , options: o });
|
||||
|
||||
o.log.apply( this.element , [this.element,message,o] );
|
||||
},
|
||||
|
||||
render: function( n ) {
|
||||
var self = this;
|
||||
var message = n.message;
|
||||
var o = n.options;
|
||||
|
||||
// Support for jQuery theme-states, if this is not used it displays a widget header
|
||||
o.themeState = (o.themeState === '') ? '' : 'ui-state-' + o.themeState;
|
||||
|
||||
var notification = $('<div/>')
|
||||
.addClass('jGrowl-notification alert ' + o.themeState + ' ui-corner-all' + ((o.group !== undefined && o.group !== '') ? ' ' + o.group : ''))
|
||||
.append($('<button/>').addClass('jGrowl-close').html(o.closeTemplate))
|
||||
.append($('<div/>').addClass('jGrowl-header').html(o.header))
|
||||
.append($('<div/>').addClass('jGrowl-message').html(message))
|
||||
.data("jGrowl", o).addClass(o.theme).children('.jGrowl-close').bind("click.jGrowl", function() {
|
||||
$(this).parent().trigger('jGrowl.beforeClose');
|
||||
return false;
|
||||
})
|
||||
.parent();
|
||||
|
||||
|
||||
/** Notification Actions **/
|
||||
$(notification).bind("mouseover.jGrowl", function() {
|
||||
$('.jGrowl-notification', self.element).data("jGrowl.pause", true);
|
||||
}).bind("mouseout.jGrowl", function() {
|
||||
$('.jGrowl-notification', self.element).data("jGrowl.pause", false);
|
||||
}).bind('jGrowl.beforeOpen', function() {
|
||||
if ( o.beforeOpen.apply( notification , [notification,message,o,self.element] ) !== false ) {
|
||||
$(this).trigger('jGrowl.open');
|
||||
}
|
||||
}).bind('jGrowl.open', function() {
|
||||
if ( o.open.apply( notification , [notification,message,o,self.element] ) !== false ) {
|
||||
if ( o.glue == 'after' ) {
|
||||
$('.jGrowl-notification:last', self.element).after(notification);
|
||||
} else {
|
||||
$('.jGrowl-notification:first', self.element).before(notification);
|
||||
}
|
||||
|
||||
$(this).animate(o.animateOpen, o.openDuration, o.easing, function() {
|
||||
// Fixes some anti-aliasing issues with IE filters.
|
||||
if ($.support.opacity === false)
|
||||
this.style.removeAttribute('filter');
|
||||
|
||||
if ( $(this).data("jGrowl") !== null && typeof $(this).data("jGrowl") !== 'undefined') // Happens when a notification is closing before it's open.
|
||||
$(this).data("jGrowl").created = new Date();
|
||||
|
||||
$(this).trigger('jGrowl.afterOpen');
|
||||
});
|
||||
}
|
||||
}).bind('jGrowl.afterOpen', function() {
|
||||
o.afterOpen.apply( notification , [notification,message,o,self.element] );
|
||||
}).bind('click', function() {
|
||||
o.click.apply( notification, [notification,message,o,self.element] );
|
||||
}).bind('jGrowl.beforeClose', function() {
|
||||
if ( o.beforeClose.apply( notification , [notification,message,o,self.element] ) !== false )
|
||||
$(this).trigger('jGrowl.close');
|
||||
}).bind('jGrowl.close', function() {
|
||||
// Pause the notification, lest during the course of animation another close event gets called.
|
||||
$(this).data('jGrowl.pause', true);
|
||||
$(this).animate(o.animateClose, o.closeDuration, o.easing, function() {
|
||||
if ( $.isFunction(o.close) ) {
|
||||
if ( o.close.apply( notification , [notification,message,o,self.element] ) !== false )
|
||||
$(this).remove();
|
||||
} else {
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
}).trigger('jGrowl.beforeOpen');
|
||||
|
||||
/** Optional Corners Plugin **/
|
||||
if ( o.corners !== '' && $.fn.corner !== undefined ) $(notification).corner( o.corners );
|
||||
|
||||
/** Add a Global Closer if more than one notification exists **/
|
||||
if ($('.jGrowl-notification:parent', self.element).length > 1 &&
|
||||
$('.jGrowl-closer', self.element).length === 0 && this.defaults.closer !== false ) {
|
||||
$(this.defaults.closerTemplate).addClass('jGrowl-closer ' + this.defaults.themeState + ' ui-corner-all').addClass(this.defaults.theme)
|
||||
.appendTo(self.element).animate(this.defaults.animateOpen, this.defaults.speed, this.defaults.easing)
|
||||
.bind("click.jGrowl", function() {
|
||||
$(this).siblings().trigger("jGrowl.beforeClose");
|
||||
|
||||
if ( $.isFunction( self.defaults.closer ) ) {
|
||||
self.defaults.closer.apply( $(this).parent()[0] , [$(this).parent()[0]] );
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** Update the jGrowl Container, removing old jGrowl notifications **/
|
||||
update: function() {
|
||||
$(this.element).find('.jGrowl-notification:parent').each( function() {
|
||||
if ($(this).data("jGrowl") !== undefined && $(this).data("jGrowl").created !== undefined &&
|
||||
($(this).data("jGrowl").created.getTime() + parseInt($(this).data("jGrowl").life, 10)) < (new Date()).getTime() &&
|
||||
$(this).data("jGrowl").sticky !== true &&
|
||||
($(this).data("jGrowl.pause") === undefined || $(this).data("jGrowl.pause") !== true) ) {
|
||||
|
||||
// Pause the notification, lest during the course of animation another close event gets called.
|
||||
$(this).trigger('jGrowl.beforeClose');
|
||||
}
|
||||
});
|
||||
|
||||
if (this.notifications.length > 0 &&
|
||||
(this.defaults.pool === 0 || $(this.element).find('.jGrowl-notification:parent').length < this.defaults.pool) )
|
||||
this.render( this.notifications.shift() );
|
||||
|
||||
if ($(this.element).find('.jGrowl-notification:parent').length < 2 ) {
|
||||
$(this.element).find('.jGrowl-closer').animate(this.defaults.animateClose, this.defaults.speed, this.defaults.easing, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** Setup the jGrowl Notification Container **/
|
||||
startup: function(e) {
|
||||
this.element = $(e).addClass('jGrowl').append('<div class="jGrowl-notification"></div>');
|
||||
this.interval = setInterval( function() {
|
||||
// some error in chage ^^
|
||||
var instance = $(e).data('jGrowl.instance');
|
||||
if (undefined !== instance) {
|
||||
instance.update();
|
||||
}
|
||||
}, parseInt(this.defaults.check, 10));
|
||||
},
|
||||
|
||||
/** Shutdown jGrowl, removing it and clearing the interval **/
|
||||
shutdown: function() {
|
||||
$(this.element).removeClass('jGrowl')
|
||||
.find('.jGrowl-notification').trigger('jGrowl.close')
|
||||
.parent().empty()
|
||||
;
|
||||
|
||||
clearInterval(this.interval);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
$(this.element).find('.jGrowl-notification').each(function(){
|
||||
$(this).trigger('jGrowl.beforeClose');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/** Reference the Defaults Object for compatibility with older versions of jGrowl **/
|
||||
$.jGrowl.defaults = $.fn.jGrowl.prototype.defaults;
|
||||
|
||||
})(jQuery);
|
File diff suppressed because one or more lines are too long
1
library/jgrowl/jquery.jgrowl.min.css
vendored
Normal file
1
library/jgrowl/jquery.jgrowl.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.jGrowl{z-index:9999;color:#fff;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;position:fixed}.jGrowl.top-left{left:0;top:0}.jGrowl.top-right{right:0;top:0}.jGrowl.bottom-left{left:0;bottom:0}.jGrowl.bottom-right{right:0;bottom:0}.jGrowl.center{top:0;width:50%;left:25%}.jGrowl.center .jGrowl-closer,.jGrowl.center .jGrowl-notification{margin-left:auto;margin-right:auto}.jGrowl-notification{background-color:#000;opacity:.9;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));zoom:1;width:250px;padding:10px;margin:10px;text-align:left;display:none;border-radius:5px;min-height:40px}.jGrowl-notification .ui-state-highlight,.jGrowl-notification .ui-widget-content .ui-state-highlight,.jGrowl-notification .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}.jGrowl-notification .jGrowl-header{font-weight:700;font-size:.85em}.jGrowl-notification .jGrowl-close{background-color:transparent;color:inherit;border:none;z-index:99;float:right;font-weight:700;font-size:1em;cursor:pointer}.jGrowl-closer{background-color:#000;opacity:.9;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));zoom:1;width:250px;padding:10px;margin:10px;display:none;border-radius:5px;padding-top:4px;padding-bottom:4px;cursor:pointer;font-size:.9em;font-weight:700;text-align:center}.jGrowl-closer .ui-state-highlight,.jGrowl-closer .ui-widget-content .ui-state-highlight,.jGrowl-closer .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}@media print{.jGrowl{display:none}}
|
File diff suppressed because one or more lines are too long
101
library/jgrowl/less/jgrowl.core.less
Executable file
101
library/jgrowl/less/jgrowl.core.less
Executable file
@ -0,0 +1,101 @@
|
||||
|
||||
.jGrowl {
|
||||
z-index: 9999;
|
||||
color: @jgrowl-color;
|
||||
font-size: @jgrowl-fontSize;
|
||||
font-family: @jgrowl-fontFamily;
|
||||
position: fixed;
|
||||
|
||||
&.top-left {
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
&.top-right {
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
&.bottom-left {
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
&.bottom-right {
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
&.center {
|
||||
top: 0px;
|
||||
width: 50%;
|
||||
left: 25%;
|
||||
|
||||
.jGrowl-notification,
|
||||
.jGrowl-closer {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notification() {
|
||||
background-color: @jgrowl-backgroundColor;
|
||||
opacity: @jgrowl-opacity;
|
||||
// These are for older versions of Internet Explorer that don't support opacity
|
||||
filter: e(%("progid:DXImageTransform.Microsoft.Alpha(Opacity=(%d*100))",@jgrowl-opacity));
|
||||
-ms-filter: e(%("progid:DXImageTransform.Microsoft.Alpha(Opacity=(%d*100))",@jgrowl-opacity));
|
||||
zoom: 1;
|
||||
width: @jgrowl-width;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
text-align: left;
|
||||
display: none;
|
||||
border-radius: @jgrowl-borderRadius;
|
||||
|
||||
.ui-state-highlight,
|
||||
.ui-widget-content .ui-state-highlight,
|
||||
.ui-widget-header .ui-state-highlight {
|
||||
border: 1px solid #000;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.jGrowl-notification {
|
||||
.notification;
|
||||
min-height: @jgrowl-height;
|
||||
|
||||
.jGrowl-header {
|
||||
font-weight: bold;
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
.jGrowl-close {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
border: none;
|
||||
z-index: 99;
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.jGrowl-closer {
|
||||
.notification;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
cursor: pointer;
|
||||
font-size: .9em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/** Hide jGrowl when printing **/
|
||||
@media print {
|
||||
.jGrowl {
|
||||
display: none;
|
||||
}
|
||||
}
|
2
library/jgrowl/less/jgrowl.less
Normal file
2
library/jgrowl/less/jgrowl.less
Normal file
@ -0,0 +1,2 @@
|
||||
@import "jgrowl.variables.less";
|
||||
@import "jgrowl.core.less";
|
8
library/jgrowl/less/jgrowl.variables.less
Normal file
8
library/jgrowl/less/jgrowl.variables.less
Normal file
@ -0,0 +1,8 @@
|
||||
@jgrowl-width: 250px;
|
||||
@jgrowl-height: 40px;
|
||||
@jgrowl-backgroundColor: #000;
|
||||
@jgrowl-color: #fff;
|
||||
@jgrowl-fontSize: 12px;
|
||||
@jgrowl-fontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@jgrowl-opacity: .90;
|
||||
@jgrowl-borderRadius: 5px;
|
23
library/jgrowl/package.json
Normal file
23
library/jgrowl/package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "jgrowl",
|
||||
"version": "1.4.5",
|
||||
"author": {
|
||||
"name" : "Stan Lemon",
|
||||
"email" : "stosh1985@gmail.com",
|
||||
"url" : "http://stanlemon.net"
|
||||
},
|
||||
"src": "jquery.jgrowl.js",
|
||||
"style": "jquery.jgrowl.css",
|
||||
"repository" : {
|
||||
"type" : "git",
|
||||
"url" : "http://github.com/stanlemon/jGrowl.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-contrib-jshint": "~0.6.3",
|
||||
"grunt-contrib-uglify": "~0.4.0",
|
||||
"grunt-contrib-cssmin": "~0.9.0",
|
||||
"grunt-contrib-less": "~0.11.0",
|
||||
"grunt-contrib-watch": "~0.6.1"
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*!
|
||||
* justifiedGallery - v3.7.0
|
||||
* justifiedGallery - v4.0.0-alpha
|
||||
* http://miromannino.github.io/Justified-Gallery/
|
||||
* Copyright (c) 2018 Miro Mannino
|
||||
* Copyright (c) 2019 Miro Mannino
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
(function (factory) {
|
||||
@ -150,7 +150,7 @@
|
||||
*/
|
||||
JustifiedGallery.prototype.showImg = function ($entry, callback) {
|
||||
if (this.settings.cssAnimation) {
|
||||
$entry.addClass('entry-visible');
|
||||
$entry.addClass('jg-entry-visible');
|
||||
if (callback) callback();
|
||||
} else {
|
||||
$entry.stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
|
||||
@ -166,8 +166,15 @@
|
||||
* @returns {String} the extracted src
|
||||
*/
|
||||
JustifiedGallery.prototype.extractImgSrcFromImage = function ($image) {
|
||||
var imageSrc = (typeof $image.data('safe-src') !== 'undefined') ? $image.data('safe-src') : $image.attr('src');
|
||||
$image.data('jg.originalSrc', imageSrc);
|
||||
var imageSrc = $image.data('safe-src');
|
||||
var imageSrcLoc = 'date-safe-src';
|
||||
if (typeof imageSrc === 'undefined') {
|
||||
imageSrc = $image.attr('src');
|
||||
imageSrcLoc = 'src';
|
||||
}
|
||||
$image.data('jg.originalSrc', imageSrc); // this is saved for the destroy method
|
||||
$image.data('jg.src', imageSrc); // this will change overtime
|
||||
$image.data('jg.originalSrcLoc', imageSrcLoc); // this is saved for the destroy method
|
||||
return imageSrc;
|
||||
};
|
||||
|
||||
@ -207,26 +214,29 @@
|
||||
$image.css('margin-top', - imgHeight / 2);
|
||||
|
||||
// Image reloading for an high quality of thumbnails
|
||||
var imageSrc = $image.attr('src');
|
||||
var newImageSrc = this.newSrc(imageSrc, imgWidth, imgHeight, $image[0]);
|
||||
var imageSrc = $image.data('jg.src');
|
||||
if (imageSrc) {
|
||||
imageSrc = this.newSrc(imageSrc, imgWidth, imgHeight, $image[0]);
|
||||
|
||||
$image.one('error', function () {
|
||||
$image.attr('src', $image.data('jg.originalSrc')); //revert to the original thumbnail, we got it.
|
||||
this.resetImgSrc($image); //revert to the original thumbnail
|
||||
});
|
||||
|
||||
var loadNewImage = function () {
|
||||
if (imageSrc !== newImageSrc) { //load the new image after the fadeIn
|
||||
$image.attr('src', newImageSrc);
|
||||
}
|
||||
// if (imageSrc !== newImageSrc) {
|
||||
$image.attr('src', imageSrc);
|
||||
// }
|
||||
};
|
||||
|
||||
if ($entry.data('jg.loaded') === 'skipped') {
|
||||
this.onImageEvent(imageSrc, $.proxy(function() {
|
||||
this.showImg($entry, loadNewImage);
|
||||
this.onImageEvent(imageSrc, (function() {
|
||||
this.showImg($entry, loadNewImage); //load the new image after the fadeIn
|
||||
$entry.data('jg.loaded', true);
|
||||
}, this));
|
||||
}).bind(this));
|
||||
} else {
|
||||
this.showImg($entry, loadNewImage);
|
||||
this.showImg($entry, loadNewImage); //load the new image after the fadeIn
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -371,7 +381,7 @@
|
||||
for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
|
||||
$entry = this.buildingRow.entriesBuff[i];
|
||||
if (this.settings.cssAnimation)
|
||||
$entry.removeClass('entry-visible');
|
||||
$entry.removeClass('jg-entry-visible');
|
||||
else {
|
||||
$entry.stop().fadeTo(0, 0.1);
|
||||
$entry.find('> img, > a > img').fadeTo(0, 0);
|
||||
@ -486,13 +496,6 @@
|
||||
this.$gallery.height(height);
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean} a boolean saying if the scrollbar is active or not
|
||||
*/
|
||||
function hasScrollBar() {
|
||||
return $("body").height() > $(window).height();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the width of the gallery container, to know if a new justification is needed
|
||||
*/
|
||||
@ -503,7 +506,6 @@
|
||||
if (!this.$gallery.is(":visible")) return;
|
||||
|
||||
var galleryWidth = parseFloat(this.$gallery.width());
|
||||
if (hasScrollBar() === this.scrollBarOn) {
|
||||
if (Math.abs(galleryWidth - this.galleryWidth) > this.settings.refreshSensitivity) {
|
||||
this.galleryWidth = galleryWidth;
|
||||
this.rewind();
|
||||
@ -513,10 +515,6 @@
|
||||
// Restart to analyze
|
||||
this.startImgAnalyzer(true);
|
||||
}
|
||||
} else {
|
||||
this.scrollBarOn = hasScrollBar();
|
||||
this.galleryWidth = galleryWidth;
|
||||
}
|
||||
}, this), this.settings.refreshTime);
|
||||
};
|
||||
|
||||
@ -574,6 +572,13 @@
|
||||
this.clearBuildingRow();
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {Array} all entries matched by `settings.selector`
|
||||
*/
|
||||
JustifiedGallery.prototype.getAllEntries = function () {
|
||||
return this.$gallery.children(this.settings.selector).toArray();
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the entries searching it from the justified gallery HTML element
|
||||
*
|
||||
@ -587,7 +592,7 @@
|
||||
newEntries = $(this.lastFetchedEntry).nextAll(this.settings.selector).toArray();
|
||||
} else {
|
||||
this.entries = [];
|
||||
newEntries = this.$gallery.children(this.settings.selector).toArray();
|
||||
newEntries = this.getAllEntries();
|
||||
}
|
||||
|
||||
if (newEntries.length > 0) {
|
||||
@ -698,6 +703,17 @@
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Revert the image src to the default value.
|
||||
*/
|
||||
JustifiedGallery.prototype.resetImgSrc = function ($img) {
|
||||
if ($img.data('jg.originalSrcLoc') == 'src') {
|
||||
$img.attr('src', $img.data('jg.originalSrc'));
|
||||
} else {
|
||||
$img.attr('src', '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the Justified Gallery instance.
|
||||
*
|
||||
@ -709,8 +725,10 @@
|
||||
*/
|
||||
JustifiedGallery.prototype.destroy = function () {
|
||||
clearInterval(this.checkWidthIntervalId);
|
||||
this.stopImgAnalyzerStarter();
|
||||
|
||||
$.each(this.entries, $.proxy(function(_, entry) {
|
||||
// Get fresh entries list since filtered entries are absent in `this.entries`
|
||||
$.each(this.getAllEntries(), $.proxy(function(_, entry) {
|
||||
var $entry = $(entry);
|
||||
|
||||
// Reset entry style
|
||||
@ -719,16 +737,20 @@
|
||||
$entry.css('top', '');
|
||||
$entry.css('left', '');
|
||||
$entry.data('jg.loaded', undefined);
|
||||
$entry.removeClass('jg-entry');
|
||||
$entry.removeClass('jg-entry jg-filtered jg-entry-visible');
|
||||
|
||||
// Reset image style
|
||||
var $img = this.imgFromEntry($entry);
|
||||
if ($img) {
|
||||
$img.css('width', '');
|
||||
$img.css('height', '');
|
||||
$img.css('margin-left', '');
|
||||
$img.css('margin-top', '');
|
||||
$img.attr('src', $img.data('jg.originalSrc'));
|
||||
this.resetImgSrc($img);
|
||||
$img.data('jg.originalSrc', undefined);
|
||||
$img.data('jg.originalSrcLoc', undefined);
|
||||
$img.data('jg.src', undefined);
|
||||
}
|
||||
|
||||
// Remove caption
|
||||
this.removeCaptionEventsHandlers($entry);
|
||||
@ -746,6 +768,7 @@
|
||||
this.$gallery.css('height', '');
|
||||
this.$gallery.removeClass('justified-gallery');
|
||||
this.$gallery.data('jg.controller', undefined);
|
||||
this.settings.triggerEvent.call(this, 'jg.destroy');
|
||||
};
|
||||
|
||||
/**
|
||||
@ -760,6 +783,12 @@
|
||||
var availableWidth = this.galleryWidth - 2 * this.border - (
|
||||
(this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
|
||||
var imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
|
||||
|
||||
this.buildingRow.entriesBuff.push($entry);
|
||||
this.buildingRow.aspectRatio += imgAspectRatio;
|
||||
this.buildingRow.width += imgAspectRatio * this.settings.rowHeight;
|
||||
this.lastAnalyzedIndex = i;
|
||||
|
||||
if (availableWidth / (this.buildingRow.aspectRatio + imgAspectRatio) < this.settings.rowHeight) {
|
||||
this.flushRow(false);
|
||||
|
||||
@ -768,12 +797,6 @@
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.buildingRow.entriesBuff.push($entry);
|
||||
this.buildingRow.aspectRatio += imgAspectRatio;
|
||||
this.buildingRow.width += imgAspectRatio * this.settings.rowHeight;
|
||||
this.lastAnalyzedIndex = i;
|
||||
|
||||
} else if ($entry.data('jg.loaded') !== 'error') {
|
||||
return;
|
||||
}
|
||||
@ -873,13 +896,16 @@
|
||||
|
||||
// Image src
|
||||
var imageSrc = that.extractImgSrcFromImage($image);
|
||||
$image.attr('src', imageSrc);
|
||||
|
||||
/* If we have the height and the width, we don't wait that the image is loaded, but we start directly
|
||||
* with the justification */
|
||||
if (that.settings.waitThumbnailsLoad === false) {
|
||||
var width = parseFloat($image.prop('width'));
|
||||
var height = parseFloat($image.prop('height'));
|
||||
/* If we have the height and the width, we don't wait that the image is loaded,
|
||||
but we start directly with the justification */
|
||||
if (that.settings.waitThumbnailsLoad === false || !imageSrc) {
|
||||
var width = parseFloat($image.attr('width'));
|
||||
var height = parseFloat($image.attr('height'));
|
||||
if ($image.prop('tagName') === 'svg') {
|
||||
width = parseFloat($image[0].getBBox().width);
|
||||
height = parseFloat($image[0].getBBox().height);
|
||||
}
|
||||
if (!isNaN(width) && !isNaN(height)) {
|
||||
$entry.data('jg.width', width);
|
||||
$entry.data('jg.height', height);
|
||||
@ -1146,12 +1172,13 @@
|
||||
It follows the specifications of the Array.prototype.filter() function of JavaScript.
|
||||
*/
|
||||
selector: 'a, div:not(.spinner)', // The selector that is used to know what are the entries of the gallery
|
||||
imgSelector: '> img, > a > img', // The selector that is used to know what are the images of each entry
|
||||
imgSelector: '> img, > a > img, > svg, > a > svg', // The selector that is used to know what are the images of each entry
|
||||
triggerEvent: function (event) { // This is called to trigger events, the default behavior is to call $.trigger
|
||||
this.$gallery.trigger(event); // Consider that 'this' is this set to the JustifiedGallery object, so it can
|
||||
} // access to fields such as $gallery, useful to trigger events with jQuery.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Justified Gallery plugin for jQuery
|
||||
*
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,7 +1,7 @@
|
||||
/*!
|
||||
* justifiedGallery - v3.7.0
|
||||
* justifiedGallery - v4.0.0-alpha
|
||||
* http://miromannino.github.io/Justified-Gallery/
|
||||
* Copyright (c) 2018 Miro Mannino
|
||||
* Copyright (c) 2019 Miro Mannino
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
.justified-gallery {
|
||||
@ -26,7 +26,13 @@
|
||||
.justified-gallery > figure > img,
|
||||
.justified-gallery > a > a > img,
|
||||
.justified-gallery > div > a > img,
|
||||
.justified-gallery > figure > a > img {
|
||||
.justified-gallery > figure > a > img,
|
||||
.justified-gallery > a > svg,
|
||||
.justified-gallery > div > svg,
|
||||
.justified-gallery > figure > svg,
|
||||
.justified-gallery > a > a > svg,
|
||||
.justified-gallery > div > a > svg,
|
||||
.justified-gallery > figure > a > svg {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
@ -63,13 +69,15 @@
|
||||
-o-transition: opacity 500ms ease-in;
|
||||
transition: opacity 500ms ease-in;
|
||||
}
|
||||
.justified-gallery > .entry-visible {
|
||||
.justified-gallery > .jg-entry-visible {
|
||||
filter: "alpha(opacity=100)";
|
||||
opacity: 1;
|
||||
background: none;
|
||||
}
|
||||
.justified-gallery > .entry-visible > img,
|
||||
.justified-gallery > .entry-visible > a > img {
|
||||
.justified-gallery > .jg-entry-visible > img,
|
||||
.justified-gallery > .jg-entry-visible > a > img,
|
||||
.justified-gallery > .jg-entry-visible > svg,
|
||||
.justified-gallery > .jg-entry-visible > a > svg {
|
||||
filter: "alpha(opacity=100)";
|
||||
opacity: 1;
|
||||
-webkit-transition: opacity 500ms ease-in;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*!
|
||||
* justifiedGallery - v3.7.0
|
||||
* justifiedGallery - v4.0.0-alpha
|
||||
* http://miromannino.github.io/Justified-Gallery/
|
||||
* Copyright (c) 2018 Miro Mannino
|
||||
* Copyright (c) 2019 Miro Mannino
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
.justified-gallery {
|
||||
@ -26,7 +26,13 @@
|
||||
.justified-gallery > figure > img,
|
||||
.justified-gallery > a > a > img,
|
||||
.justified-gallery > div > a > img,
|
||||
.justified-gallery > figure > a > img {
|
||||
.justified-gallery > figure > a > img,
|
||||
.justified-gallery > a > svg,
|
||||
.justified-gallery > div > svg,
|
||||
.justified-gallery > figure > svg,
|
||||
.justified-gallery > a > a > svg,
|
||||
.justified-gallery > div > a > svg,
|
||||
.justified-gallery > figure > a > svg {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
@ -63,13 +69,15 @@
|
||||
-o-transition: opacity 500ms ease-in;
|
||||
transition: opacity 500ms ease-in;
|
||||
}
|
||||
.justified-gallery > .entry-visible {
|
||||
.justified-gallery > .jg-entry-visible {
|
||||
filter: "alpha(opacity=100)";
|
||||
opacity: 1;
|
||||
background: none;
|
||||
}
|
||||
.justified-gallery > .entry-visible > img,
|
||||
.justified-gallery > .entry-visible > a > img {
|
||||
.justified-gallery > .jg-entry-visible > img,
|
||||
.justified-gallery > .jg-entry-visible > a > img,
|
||||
.justified-gallery > .jg-entry-visible > svg,
|
||||
.justified-gallery > .jg-entry-visible > a > svg {
|
||||
filter: "alpha(opacity=100)";
|
||||
opacity: 1;
|
||||
-webkit-transition: opacity 500ms ease-in;
|
||||
|
2740
util/hmessages.po
2740
util/hmessages.po
File diff suppressed because it is too large
Load Diff
@ -52,7 +52,7 @@ function po2php_run($argc,$argv) {
|
||||
if ($l[0]=="#") $l="";
|
||||
if (substr($l,0,15)=='"Plural-Forms: '){
|
||||
$match=Array();
|
||||
preg_match("|nplurals=([0-9]*); *plural=(.*)[;\\\\]|", $l, $match);
|
||||
preg_match("|nplurals=([0-9]*);\s*plural=(.*)[;\\\\]|", $l, $match);
|
||||
$cond = str_replace('n','$n',$match[2]);
|
||||
$out .= 'if(! function_exists("' . 'string_plural_select_' . $lang .'")) {' . "\n";
|
||||
$out .= 'function string_plural_select_' . $lang . '($n){'."\n";
|
||||
|
131
util/storageconv
Executable file
131
util/storageconv
Executable file
@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
// Hubzilla thumbnails storage convertor
|
||||
function usage() {
|
||||
echo <<< EOT
|
||||
Hubzilla thumbnails storage convertor
|
||||
|
||||
Usage:
|
||||
util/storageconv stats # show current stats
|
||||
util/storageconv fs # move thumbnails from SQL to filesystem
|
||||
util/storageconv db # move thumbnails from filesystem to SQL
|
||||
|
||||
EOT;
|
||||
}
|
||||
|
||||
require_once('include/cli_startup.php');
|
||||
|
||||
cli_startup();
|
||||
|
||||
if($argc == 1) {
|
||||
usage();
|
||||
killme();
|
||||
}
|
||||
|
||||
if($argc == 2) {
|
||||
|
||||
$storage = (intval(get_config('system','filesystem_storage_thumbnails', 0)) > 0 ? 1 : 0);
|
||||
echo 'Current storage set to: ' . ($storage ? 'filesystem' : 'SQL database') . PHP_EOL;
|
||||
switch($argv[1]) {
|
||||
case 'stats':
|
||||
$x = q("SELECT COUNT(resource_id) AS qty FROM photo WHERE photo_usage = 0 AND os_storage = 1 AND imgscale = 0");
|
||||
echo 'Local images: ' . $x[0]['qty'] . PHP_EOL;
|
||||
$x = q("SELECT COUNT(id) AS qty FROM photo WHERE resource_id IN (SELECT DISTINCT resource_id FROM photo WHERE photo_usage = 0 and os_storage = 1) AND imgscale > 0");
|
||||
echo 'Thumbnails total: ' . $x[0]['qty'] . PHP_EOL;
|
||||
$x = q("SELECT COUNT(id) AS qty FROM photo WHERE resource_id IN (SELECT DISTINCT resource_id FROM photo WHERE photo_usage = 0 and os_storage = 1) AND os_storage != %d AND imgscale > 0",
|
||||
$storage
|
||||
);
|
||||
echo 'Thumbnails to convert: ' . $x[0]['qty'] . PHP_EOL;
|
||||
break;
|
||||
|
||||
case 'fs':
|
||||
if($storage == 0) {
|
||||
echo 'Please set system.filesystem_storage_thumbnails to 1 before move thumbnails to filesystem storage' . PHP_EOL;
|
||||
break;
|
||||
}
|
||||
|
||||
$x = q("SELECT DISTINCT resource_id, content FROM photo WHERE photo_usage = 0 AND os_storage = 1 AND imgscale = 0");
|
||||
|
||||
if($x) {
|
||||
foreach($x as $xx) {
|
||||
|
||||
$n = q("SELECT id, imgscale, content FROM photo WHERE resource_id = '%s' AND os_storage != %d AND imgscale > 0",
|
||||
dbesc($xx['resource_id']),
|
||||
$storage
|
||||
);
|
||||
|
||||
foreach($n as $nn) {
|
||||
|
||||
echo '.';
|
||||
|
||||
$filename = dbunescbin($xx['content']) . '-' . $nn['imgscale'];
|
||||
if(! file_put_contents($filename, dbunescbin($nn['content']))) {
|
||||
echo 'Failed to save file ' . $filename . PHP_EOL;
|
||||
continue;
|
||||
}
|
||||
|
||||
$z = q("UPDATE photo SET content = '%s', os_storage = 1 WHERE id = %d",
|
||||
dbescbin($filename),
|
||||
intval($nn['id'])
|
||||
);
|
||||
if(! $z) {
|
||||
@unlink($filename);
|
||||
echo 'Failed to update metadata for saved file ' . $filename . PHP_EOL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'db':
|
||||
if($storage == 1) {
|
||||
echo 'Please set system.filesystem_storage_thumbnails to 0 before move thumbnails to SQL database storage' . PHP_EOL;
|
||||
break;
|
||||
}
|
||||
|
||||
$x = q("SELECT DISTINCT resource_id FROM photo WHERE photo_usage = 0 AND os_storage = 1 AND imgscale = 0");
|
||||
|
||||
if($x) {
|
||||
foreach($x as $xx) {
|
||||
|
||||
$n = q("SELECT id, content FROM photo WHERE resource_id = '%s' AND os_storage != %d AND imgscale > 0",
|
||||
dbesc($xx['resource_id']),
|
||||
$storage
|
||||
);
|
||||
|
||||
foreach($n as $nn) {
|
||||
|
||||
echo '.';
|
||||
|
||||
$filename = dbunescbin($nn['content']);
|
||||
$content = file_get_contents($filename);
|
||||
if($content) {
|
||||
|
||||
$z = q("UPDATE photo SET content = '%s', os_storage = 0 WHERE id = %d",
|
||||
dbescbin($content),
|
||||
intval($nn['id'])
|
||||
);
|
||||
if(! $z) {
|
||||
echo 'Failed to update stored file metadata ' . $filename . PHP_EOL;
|
||||
continue;
|
||||
}
|
||||
|
||||
@unlink($filename);
|
||||
}
|
||||
else
|
||||
echo 'Can not read file contents ' . $filename . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
usage();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
echo PHP_EOL;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user