Merge branch '2021.06-rc' of https://github.com/friendica/friendica into 2021.06-CHANGELOG
This commit is contained in:
commit
e4553ad156
|
@ -8,7 +8,7 @@ coverage:
|
|||
round: down
|
||||
range: "70...100"
|
||||
status:
|
||||
project: off
|
||||
patch: off
|
||||
project: false
|
||||
patch: false
|
||||
|
||||
comment: off
|
||||
comment: false
|
||||
|
|
141
.drone.yml
141
.drone.yml
|
@ -52,21 +52,81 @@ trigger:
|
|||
- pull_request
|
||||
|
||||
steps:
|
||||
- name: Restore cache
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: "filesystem"
|
||||
restore: true
|
||||
cache_key: '{{ .Repo.Name }}_phpcs_{{ arch }}_{{ os }}'
|
||||
archive_format: "gzip"
|
||||
mount:
|
||||
- '.composer'
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /tmp/cache
|
||||
- name: Install dependencies
|
||||
image: composer
|
||||
commands:
|
||||
- export COMPOSER_HOME=.composer
|
||||
- ./bin/composer.phar run cs:install
|
||||
- name: Rebuild cache
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: "filesystem"
|
||||
rebuild: true
|
||||
cache_key: '{{ .Repo.Name }}_phpcs_{{ arch }}_{{ os }}'
|
||||
archive_format: "gzip"
|
||||
mount:
|
||||
- '.composer'
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /tmp/cache
|
||||
- name: Run coding standards check
|
||||
image: friendicaci/php-cs
|
||||
commands:
|
||||
- export CHANGED_FILES="$(git diff --name-status ${DRONE_COMMIT_BEFORE}..${DRONE_COMMIT_AFTER} | grep ^A | cut -f2)"
|
||||
- /check-php-cs.sh
|
||||
|
||||
volumes:
|
||||
- name: cache
|
||||
host:
|
||||
path: /tmp/drone-cache
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: php7.3-mariadb
|
||||
|
||||
steps:
|
||||
- name: Restore cache
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: "filesystem"
|
||||
restore: true
|
||||
cache_key: '{{ .Repo.Name }}_php73_{{ arch }}_{{ os }}'
|
||||
archive_format: "gzip"
|
||||
mount:
|
||||
- '.composer'
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /tmp/cache
|
||||
- name: Composer install
|
||||
image: friendicaci/php7.3:php7.3.28
|
||||
commands:
|
||||
- export COMPOSER_HOME=.composer
|
||||
- ./bin/composer.phar validate
|
||||
- ./bin/composer.phar install --prefer-dist
|
||||
- name: Rebuild cache
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: "filesystem"
|
||||
rebuild: true
|
||||
cache_key: '{{ .Repo.Name }}_php73_{{ arch }}_{{ os }}'
|
||||
archive_format: "gzip"
|
||||
mount:
|
||||
- '.composer'
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /tmp/cache
|
||||
- name: Test Friendica
|
||||
image: friendicaci/php7.3:php7.3.28
|
||||
environment:
|
||||
|
@ -79,8 +139,6 @@ steps:
|
|||
MEMCACHED_HOST: "memcached"
|
||||
MEMCACHE_HOST: "memcached"
|
||||
commands:
|
||||
- composer validate
|
||||
- composer install --prefer-dist
|
||||
- cp config/local-sample.config.php config/local.config.php
|
||||
- if ! bin/wait-for-connection $MYSQL_HOST $MYSQL_PORT 300; then echo "[ERROR] Waited 300 seconds, no response" >&2; exit 1; fi
|
||||
- mysql -h$MYSQL_HOST -P$MYSQL_PORT -p$MYSQL_PASSWORD -u$MYSQL_USER $MYSQL_DATABASE < database.sql
|
||||
|
@ -102,12 +160,47 @@ services:
|
|||
|
||||
- name: redis
|
||||
image: redis
|
||||
|
||||
volumes:
|
||||
- name: cache
|
||||
host:
|
||||
path: /tmp/drone-cache
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: php7.4-mariadb
|
||||
|
||||
steps:
|
||||
- name: Restore cache
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: "filesystem"
|
||||
restore: true
|
||||
cache_key: '{{ .Repo.Name }}_php74_{{ arch }}_{{ os }}'
|
||||
archive_format: "gzip"
|
||||
mount:
|
||||
- '.composer'
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /tmp/cache
|
||||
- name: Composer install
|
||||
image: friendicaci/php7.4:php7.4.18
|
||||
commands:
|
||||
- export COMPOSER_HOME=.composer
|
||||
- ./bin/composer.phar validate
|
||||
- ./bin/composer.phar install --prefer-dist
|
||||
- name: Rebuild cache
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: "filesystem"
|
||||
rebuild: true
|
||||
cache_key: '{{ .Repo.Name }}_php74_{{ arch }}_{{ os }}'
|
||||
archive_format: "gzip"
|
||||
mount:
|
||||
- '.composer'
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /tmp/cache
|
||||
- name: Test Friendica
|
||||
image: friendicaci/php7.4:php7.4.18
|
||||
environment:
|
||||
|
@ -122,8 +215,6 @@ steps:
|
|||
XDEBUG_MODE: "coverage"
|
||||
commands:
|
||||
- phpenmod xdebug
|
||||
- composer validate
|
||||
- composer install --prefer-dist
|
||||
- cp config/local-sample.config.php config/local.config.php
|
||||
- if ! bin/wait-for-connection $MYSQL_HOST $MYSQL_PORT 300; then echo "[ERROR] Waited 300 seconds, no response" >&2; exit 1; fi
|
||||
- mysql -h$MYSQL_HOST -P$MYSQL_PORT -p$MYSQL_PASSWORD -u$MYSQL_USER $MYSQL_DATABASE < database.sql
|
||||
|
@ -155,12 +246,47 @@ services:
|
|||
|
||||
- name: redis
|
||||
image: redis
|
||||
|
||||
volumes:
|
||||
- name: cache
|
||||
host:
|
||||
path: /tmp/drone-cache
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: php8.0-mariadb
|
||||
|
||||
steps:
|
||||
- name: Restore cache
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: "filesystem"
|
||||
restore: true
|
||||
cache_key: '{{ .Repo.Name }}_php80_{{ arch }}_{{ os }}'
|
||||
archive_format: "gzip"
|
||||
mount:
|
||||
- '.composer'
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /tmp/cache
|
||||
- name: Composer install
|
||||
image: friendicaci/php8.0:php8.0.5
|
||||
commands:
|
||||
- export COMPOSER_HOME=.composer
|
||||
- ./bin/composer.phar validate
|
||||
- ./bin/composer.phar install --prefer-dist
|
||||
- name: Rebuild cache
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: "filesystem"
|
||||
rebuild: true
|
||||
cache_key: '{{ .Repo.Name }}_php80_{{ arch }}_{{ os }}'
|
||||
archive_format: "gzip"
|
||||
mount:
|
||||
- '.composer'
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /tmp/cache
|
||||
- name: Test Friendica
|
||||
image: friendicaci/php8.0:php8.0.5
|
||||
environment:
|
||||
|
@ -173,8 +299,6 @@ steps:
|
|||
MEMCACHED_HOST: "memcached"
|
||||
MEMCACHE_HOST: "memcached"
|
||||
commands:
|
||||
- composer validate
|
||||
- composer install --prefer-dist
|
||||
- cp config/local-sample.config.php config/local.config.php
|
||||
- if ! bin/wait-for-connection $MYSQL_HOST $MYSQL_PORT 300; then echo "[ERROR] Waited 300 seconds, no response" >&2; exit 1; fi
|
||||
- mysql -h$MYSQL_HOST -P$MYSQL_PORT -p$MYSQL_PASSWORD -u$MYSQL_USER $MYSQL_DATABASE < database.sql
|
||||
|
@ -196,3 +320,8 @@ services:
|
|||
|
||||
- name: redis
|
||||
image: redis
|
||||
|
||||
volumes:
|
||||
- name: cache
|
||||
host:
|
||||
path: /tmp/drone-cache
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -16,7 +16,8 @@ robots.txt
|
|||
/config/addon.ini.php
|
||||
|
||||
#ignore documentation, it should be newly built
|
||||
/doc/html
|
||||
/doc/api
|
||||
/doc/cache
|
||||
|
||||
#ignore reports, should be generated with every build
|
||||
report/
|
||||
|
|
6
Vagrantfile
vendored
6
Vagrantfile
vendored
|
@ -34,10 +34,14 @@ Vagrant.configure(2) do |config|
|
|||
#
|
||||
# # Customize the amount of memory on the VM:
|
||||
vb.memory = server_memory
|
||||
|
||||
unless Vagrant.has_plugin?("vagrant-vbguest")
|
||||
raise 'vagrant-vbguest plugin is not installed! Install with "vagrant plugin install vagrant-vbguest"'
|
||||
end
|
||||
end
|
||||
|
||||
# Enable provisioning with a shell script.
|
||||
config.vm.provision "shell", path: "./bin/dev/vagrant_provision.sh"
|
||||
config.vm.provision "shell", path: "./bin/dev/vagrant_provision.sh", privileged: true
|
||||
# run: "always"
|
||||
# run: "once"
|
||||
end
|
||||
|
|
Binary file not shown.
|
@ -1,13 +1,23 @@
|
|||
#!/bin/bash
|
||||
#Script to setup the vagrant instance for running friendica
|
||||
# Script to setup the vagrant instance for running friendica
|
||||
#
|
||||
#DO NOT RUN on your physical machine as this won't be of any use
|
||||
#and f.e. deletes your /var/www/ folder!
|
||||
echo "Friendica configuration settings"
|
||||
sudo apt-get update
|
||||
# DO NOT RUN on your physical machine as this won't be of any use
|
||||
# and f.e. deletes your /var/www/ folder!
|
||||
#
|
||||
# Run as root by vagrant
|
||||
#
|
||||
##
|
||||
|
||||
# Install virtualbox guest additions
|
||||
sudo apt-get install virtualbox-guest-x11
|
||||
ADMIN_NICK="admin"
|
||||
ADMIN_PASSW="admin"
|
||||
|
||||
USER_NICK="user"
|
||||
USER_PASSW="user"
|
||||
|
||||
##
|
||||
|
||||
echo "Friendica configuration settings"
|
||||
apt-get update
|
||||
|
||||
#Selfsigned cert
|
||||
echo ">>> Installing *.xip.io self-signed SSL"
|
||||
|
@ -23,32 +33,32 @@ localityName=New Haven/
|
|||
commonName=$DOMAIN/
|
||||
subjectAltName=DNS:$EXTRADOMAIN
|
||||
"
|
||||
sudo mkdir -p "$SSL_DIR"
|
||||
sudo openssl genrsa -out "$SSL_DIR/xip.io.key" 4096
|
||||
sudo openssl req -new -subj "$(echo -n "$SUBJ" | tr "\n" "/")" -key "$SSL_DIR/xip.io.key" -out "$SSL_DIR/xip.io.csr" -passin pass:$PASSPHRASE
|
||||
sudo openssl x509 -req -days 365 -in "$SSL_DIR/xip.io.csr" -signkey "$SSL_DIR/xip.io.key" -out "$SSL_DIR/xip.io.crt"
|
||||
mkdir -p "$SSL_DIR"
|
||||
openssl genrsa -out "$SSL_DIR/xip.io.key" 4096
|
||||
openssl req -new -subj "$(echo -n "$SUBJ" | tr "\n" "/")" -key "$SSL_DIR/xip.io.key" -out "$SSL_DIR/xip.io.csr" -passin pass:$PASSPHRASE
|
||||
openssl x509 -req -days 365 -in "$SSL_DIR/xip.io.csr" -signkey "$SSL_DIR/xip.io.key" -out "$SSL_DIR/xip.io.crt"
|
||||
|
||||
|
||||
#Install apache2
|
||||
echo ">>> Installing Apache2 webserver"
|
||||
sudo apt-get install -y apache2
|
||||
sudo a2enmod rewrite actions ssl
|
||||
sudo cp /vagrant/bin/dev/vagrant_vhost.sh /usr/local/bin/vhost
|
||||
sudo chmod guo+x /usr/local/bin/vhost
|
||||
sudo vhost -s 192.168.22.10.xip.io -d /var/www -p /etc/ssl/xip.io -c xip.io -a friendica.local
|
||||
sudo a2dissite 000-default
|
||||
sudo service apache2 restart
|
||||
apt-get install -qq apache2
|
||||
a2enmod rewrite actions ssl
|
||||
cp /vagrant/bin/dev/vagrant_vhost.sh /usr/local/bin/vhost
|
||||
chmod guo+x /usr/local/bin/vhost
|
||||
vhost -s 192.168.22.10.xip.io -d /var/www -p /etc/ssl/xip.io -c xip.io -a friendica.local
|
||||
a2dissite 000-default
|
||||
service apache2 restart
|
||||
|
||||
#Install php
|
||||
echo ">>> Installing PHP7"
|
||||
sudo apt-get install -y php libapache2-mod-php php-cli php-mysql php-curl php-gd php-mbstring php-xml imagemagick php-imagick php-zip
|
||||
sudo systemctl restart apache2
|
||||
apt-get install -qq php libapache2-mod-php php-cli php-mysql php-curl php-gd php-mbstring php-xml imagemagick php-imagick php-zip
|
||||
systemctl restart apache2
|
||||
|
||||
#Install mysql
|
||||
echo ">>> Installing Mysql"
|
||||
sudo debconf-set-selections <<< "mariadb-server mariadb-server/root_password password root"
|
||||
sudo debconf-set-selections <<< "mariadb-server mariadb-server/root_password_again password root"
|
||||
sudo apt-get install -qq mariadb-server
|
||||
debconf-set-selections <<< "mariadb-server mariadb-server/root_password password root"
|
||||
debconf-set-selections <<< "mariadb-server mariadb-server/root_password_again password root"
|
||||
apt-get install -qq mariadb-server
|
||||
# enable remote access
|
||||
# setting the mysql bind-address to allow connections from everywhere
|
||||
sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
|
||||
|
@ -66,41 +76,60 @@ $MYSQL -uroot -proot -e "FLUSH PRIVILEGES"
|
|||
systemctl restart mysql
|
||||
|
||||
|
||||
|
||||
#configure rudimentary mail server (local delivery only)
|
||||
#add Friendica accounts for local user accounts, use email address like vagrant@friendica.local, read the email with 'mail'.
|
||||
echo ">>> Installing 'Local Only' postfix"
|
||||
debconf-set-selections <<< "postfix postfix/mailname string friendica.local"
|
||||
debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Local Only'"
|
||||
sudo apt-get install -y postfix mailutils libmailutils-dev
|
||||
sudo echo -e "friendica1: vagrant\nfriendica2: vagrant\nfriendica3: vagrant\nfriendica4: vagrant\nfriendica5: vagrant" >> /etc/aliases && sudo newaliases
|
||||
apt-get install -qq postfix mailutils libmailutils-dev
|
||||
echo -e "friendica1: vagrant\nfriendica2: vagrant\nfriendica3: vagrant\nfriendica4: vagrant\nfriendica5: vagrant" >> /etc/aliases && newaliases
|
||||
|
||||
# Friendica needs git for fetching some dependencies
|
||||
sudo apt-get install -y git
|
||||
echo ">>> Installing git"
|
||||
apt-get install -qq git
|
||||
|
||||
#make the vagrant directory the docroot
|
||||
sudo rm -rf /var/www/
|
||||
sudo ln -fs /vagrant /var/www
|
||||
echo ">>> Symlink /var/www to /vagrant"
|
||||
rm -rf /var/www/
|
||||
ln -fs /vagrant /var/www
|
||||
|
||||
# install deps with composer
|
||||
sudo apt install unzip
|
||||
echo ">>> Installing php requirements"
|
||||
apt install unzip
|
||||
cd /var/www
|
||||
sudo -u www-data php bin/composer.phar install
|
||||
php bin/composer.phar install
|
||||
|
||||
# initial config file for friendica in vagrant
|
||||
cp /vagrant/mods/local.config.vagrant.php /vagrant/config/local.config.php
|
||||
|
||||
echo ">>> Setup Friendica"
|
||||
|
||||
# copy the .htaccess-dist file to .htaccess so that rewrite rules work
|
||||
cp /vagrant/.htaccess-dist /vagrant/.htaccess
|
||||
|
||||
# create the friendica database
|
||||
echo "create database friendica DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" | $MYSQL -u root -proot
|
||||
# import test database
|
||||
$MYSQL -uroot -proot friendica < /vagrant/friendica_test_data.sql
|
||||
# import test database (disabled because too old)
|
||||
#$MYSQL -uroot -proot friendica < /vagrant/friendica_test_data.sql
|
||||
|
||||
# install friendica
|
||||
bin/console autoinstall -f /vagrant/mods/local.config.vagrant.php
|
||||
|
||||
# add users
|
||||
# (disable a bunch of validation because this is a dev install, deh, it needs invalid emails and stupid passwords)
|
||||
bin/console config system disable_email_validation 1
|
||||
bin/console config system disable_password_exposed 1
|
||||
bin/console user add "$ADMIN_NICK" "$ADMIN_NICK" "$ADMIN_NICK@friendica.local" en
|
||||
bin/console user password "$ADMIN_NICK" "$ADMIN_PASSW"
|
||||
bin/console user add "$USER_NICK" "$USER_NICK" "$USER_NICK@friendica.local" en
|
||||
bin/console user password "$USER_NICK" "$USER_PASSW"
|
||||
|
||||
# set the admin
|
||||
bin/console config config admin_email ""$ADMIN_NICK@friendica.local""
|
||||
|
||||
|
||||
# create cronjob - activate if you have enough memory in you dev VM
|
||||
echo "*/10 * * * * cd /vagrant; /usr/bin/php bin/worker.php" >> friendicacron
|
||||
sudo crontab friendicacron
|
||||
sudo rm friendicacron
|
||||
# cronjob runs as www-data user
|
||||
echo ">>> Installing cronjob"
|
||||
echo "*/10 * * * * www-data cd /vagrant; /usr/bin/php bin/worker.php" >> /etc/cron.d/friendica
|
||||
|
||||
# friendica needs write access to /tmp
|
||||
sudo chmod 777 /tmp
|
||||
chmod 777 /tmp
|
||||
|
|
76
build.xml
76
build.xml
|
@ -1,76 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project name="friendica" default="test">
|
||||
<!-- ====================================================== -->
|
||||
<!-- Target: clean-test -->
|
||||
<!-- deletes directories with old test reports -->
|
||||
<!-- ====================================================== -->
|
||||
<target name="clean-test">
|
||||
<delete dir="report" />
|
||||
</target>
|
||||
|
||||
<!-- ====================================================== -->
|
||||
<!-- Target: prepare-test -->
|
||||
<!-- creates directories for test reports -->
|
||||
<!-- ====================================================== -->
|
||||
<target name="prepare-test" depends="clean-test">
|
||||
<mkdir dir="report" />
|
||||
</target>
|
||||
|
||||
<!-- =================================== -->
|
||||
<!-- Target: test -->
|
||||
<!-- this target runs all test files -->
|
||||
<!-- =================================== -->
|
||||
<target name="test" depends="prepare-test">
|
||||
<!-- coverage-setup database="./report/coverage-database">
|
||||
<fileset dir=".">
|
||||
<include name="**/*.php" />
|
||||
<exclude name="*test.php"/>
|
||||
<exclude name="index.php"/>
|
||||
<exclude name="library/**"/>
|
||||
<exclude name="doc/**"/>
|
||||
<exclude name=".."/>
|
||||
</fileset>
|
||||
</coverage-setup -->
|
||||
<phpunit printsummary="true">
|
||||
<batchtest>
|
||||
<fileset dir="tests">
|
||||
<include name="*test.php" />
|
||||
</fileset>
|
||||
</batchtest>
|
||||
<formatter type="xml" todir="report" outfile="testlog.xml" />
|
||||
</phpunit>
|
||||
<phpunitreport infile="report/testlog.xml" todir="report" />
|
||||
<!-- coverage-report outfile="report/coverage-database">
|
||||
<report todir="report" styledir="/home/phing/etc" />
|
||||
</coverage-report -->
|
||||
</target>
|
||||
|
||||
<!-- ===================================================== -->
|
||||
<!-- Target: clean-doc -->
|
||||
<!-- this target removes documentation from a previous run -->
|
||||
<!-- ===================================================== -->
|
||||
<target name="doc-clean">
|
||||
<echo msg="Removing old documentation..." />
|
||||
<delete dir="./doc/api/" />
|
||||
<echo msg="Generate documentation directory..." />
|
||||
<mkdir dir="./doc/api/" />
|
||||
</target>
|
||||
|
||||
<!-- ====================================== -->
|
||||
<!-- Target: doc -->
|
||||
<!-- this target builds all documentation -->
|
||||
<!-- ====================================== -->
|
||||
<target name="doc" depends="doc-clean">
|
||||
<echo msg="Building documentation..." />
|
||||
<docblox title="Friendica API" destdir="./doc/api">
|
||||
<fileset dir=".">
|
||||
<include name="**/*.php" />
|
||||
<include name="README"/>
|
||||
<include name="INSTALL.txt"/>
|
||||
<include name="LICENSE"/>
|
||||
</fileset>
|
||||
</docblox>
|
||||
</target>
|
||||
|
||||
</project>
|
91
composer.lock
generated
91
composer.lock
generated
|
@ -736,16 +736,16 @@
|
|||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "1.8.1",
|
||||
"version": "1.8.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "35ea11d335fd638b5882ff1725228b3d35496ab1"
|
||||
"reference": "dc960a912984efb74d0a90222870c72c87f10c91"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1",
|
||||
"reference": "35ea11d335fd638b5882ff1725228b3d35496ab1",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91",
|
||||
"reference": "dc960a912984efb74d0a90222870c72c87f10c91",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -803,7 +803,7 @@
|
|||
"uri",
|
||||
"url"
|
||||
],
|
||||
"time": "2021-03-21T16:25:00+00:00"
|
||||
"time": "2021-04-26T09:17:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/html-to-markdown",
|
||||
|
@ -1119,16 +1119,16 @@
|
|||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "1.26.0",
|
||||
"version": "1.26.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33"
|
||||
"reference": "c6b00f05152ae2c9b04a448f99c7590beb6042f5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/2209ddd84e7ef1256b7af205d0717fb62cfc9c33",
|
||||
"reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/c6b00f05152ae2c9b04a448f99c7590beb6042f5",
|
||||
"reference": "c6b00f05152ae2c9b04a448f99c7590beb6042f5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1197,7 +1197,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-12-14T12:56:38+00:00"
|
||||
"time": "2021-05-28T08:32:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/fast-route",
|
||||
|
@ -1591,19 +1591,11 @@
|
|||
},
|
||||
{
|
||||
"name": "npm-asset/jgrowl",
|
||||
"version": "1.4.6",
|
||||
"version": "1.4.8",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://registry.npmjs.org/jgrowl/-/jgrowl-1.4.6.tgz",
|
||||
"shasum": "2736e332aaee73ccf0a14a5f0066391a0a13f4a3"
|
||||
},
|
||||
"require-dev": {
|
||||
"npm-asset/grunt": "~0.4.2",
|
||||
"npm-asset/grunt-contrib-cssmin": "~0.9.0",
|
||||
"npm-asset/grunt-contrib-jshint": "~0.6.3",
|
||||
"npm-asset/grunt-contrib-less": "~0.11.0",
|
||||
"npm-asset/grunt-contrib-uglify": "~0.4.0",
|
||||
"npm-asset/grunt-contrib-watch": "~0.6.1"
|
||||
"url": "https://registry.npmjs.org/jgrowl/-/jgrowl-1.4.8.tgz",
|
||||
"shasum": "4ba40ffb93757a7e1d9b262d916be299d03df3a4"
|
||||
},
|
||||
"type": "npm-asset-library",
|
||||
"extra": {
|
||||
|
@ -1616,8 +1608,13 @@
|
|||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/stanlemon/jGrowl.git"
|
||||
},
|
||||
"npm-asset-scripts": []
|
||||
"npm-asset-scripts": {
|
||||
"build": "grunt"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Stan Lemon",
|
||||
|
@ -1627,7 +1624,7 @@
|
|||
],
|
||||
"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 ",
|
||||
"homepage": "https://github.com/stanlemon/jGrowl#readme",
|
||||
"time": "2017-07-21T02:36:34+00:00"
|
||||
"time": "2021-05-20T17:11:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/jquery",
|
||||
|
@ -2224,16 +2221,16 @@
|
|||
},
|
||||
{
|
||||
"name": "paragonie/certainty",
|
||||
"version": "v2.8.0",
|
||||
"version": "v2.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/certainty.git",
|
||||
"reference": "94fc99f8c1f5bdce960713d6b63a108a64d90dfa"
|
||||
"reference": "e2a1da558f95074545ad811d60359c74500a5e24"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/certainty/zipball/94fc99f8c1f5bdce960713d6b63a108a64d90dfa",
|
||||
"reference": "94fc99f8c1f5bdce960713d6b63a108a64d90dfa",
|
||||
"url": "https://api.github.com/repos/paragonie/certainty/zipball/e2a1da558f95074545ad811d60359c74500a5e24",
|
||||
"reference": "e2a1da558f95074545ad811d60359c74500a5e24",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2241,12 +2238,12 @@
|
|||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^6|^7",
|
||||
"paragonie/constant_time_encoding": "^1|^2",
|
||||
"paragonie/sodium_compat": "^1.11",
|
||||
"paragonie/sodium_compat": "^1.13",
|
||||
"php": "^5.5|^7|^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "^1",
|
||||
"phpunit/phpunit": "^4|^5|^6|^7"
|
||||
"composer/composer": "^1|>=2.0.14",
|
||||
"phpunit/phpunit": "^4|^5|^6|^7|^8|^9"
|
||||
},
|
||||
"bin": [
|
||||
"bin/certainty-cert-symlink"
|
||||
|
@ -2282,7 +2279,7 @@
|
|||
"ssl",
|
||||
"tls"
|
||||
],
|
||||
"time": "2020-10-15T08:10:12+00:00"
|
||||
"time": "2021-05-25T18:27:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
|
@ -2442,16 +2439,16 @@
|
|||
},
|
||||
{
|
||||
"name": "paragonie/sodium_compat",
|
||||
"version": "v1.14.0",
|
||||
"version": "v1.16.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/sodium_compat.git",
|
||||
"reference": "a1cfe0b21faf9c0b61ac0c6188c4af7fd6fd0db3"
|
||||
"reference": "2e856afe80bfc968b47da1f4a7e1ea8f03d06b38"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/a1cfe0b21faf9c0b61ac0c6188c4af7fd6fd0db3",
|
||||
"reference": "a1cfe0b21faf9c0b61ac0c6188c4af7fd6fd0db3",
|
||||
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/2e856afe80bfc968b47da1f4a7e1ea8f03d06b38",
|
||||
"reference": "2e856afe80bfc968b47da1f4a7e1ea8f03d06b38",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2520,7 +2517,7 @@
|
|||
"secret-key cryptography",
|
||||
"side-channel resistant"
|
||||
],
|
||||
"time": "2020-12-03T16:26:19+00:00"
|
||||
"time": "2021-05-25T12:58:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "patrickschur/language-detection",
|
||||
|
@ -2625,16 +2622,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "2.0.30",
|
||||
"version": "2.0.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36"
|
||||
"reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/136b9ca7eebef78be14abf90d65c5e57b6bc5d36",
|
||||
"reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/233a920cb38636a43b18d428f9a8db1f0a1a08f4",
|
||||
"reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2726,7 +2723,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-12-17T05:42:04+00:00"
|
||||
"time": "2021-04-06T13:56:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pragmarx/google2fa",
|
||||
|
@ -3058,16 +3055,16 @@
|
|||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
|
||||
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -3091,7 +3088,7 @@
|
|||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for logging libraries",
|
||||
|
@ -3101,7 +3098,7 @@
|
|||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"time": "2020-03-23T09:12:05+00:00"
|
||||
"time": "2021-05-03T11:20:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
|
|
33
database.sql
33
database.sql
|
@ -1,6 +1,6 @@
|
|||
-- ------------------------------------------
|
||||
-- Friendica 2021.06-rc (Siberian Iris)
|
||||
-- DB_UPDATE_VERSION 1419
|
||||
-- DB_UPDATE_VERSION 1421
|
||||
-- ------------------------------------------
|
||||
|
||||
|
||||
|
@ -819,6 +819,33 @@ CREATE TABLE IF NOT EXISTS `manage` (
|
|||
FOREIGN KEY (`mid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='table of accounts that can manage each other';
|
||||
|
||||
--
|
||||
-- TABLE notification
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `notification` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`uid` mediumint unsigned COMMENT 'Owner User id',
|
||||
`vid` smallint unsigned COMMENT 'Id of the verb table entry that contains the activity verbs',
|
||||
`type` tinyint unsigned COMMENT '',
|
||||
`actor-id` int unsigned COMMENT 'Link to the contact table with uid=0 of the actor that caused the notification',
|
||||
`target-uri-id` int unsigned COMMENT 'Item-uri id of the related post',
|
||||
`parent-uri-id` int unsigned COMMENT 'Item-uri id of the parent of the related post',
|
||||
`created` datetime COMMENT '',
|
||||
`seen` boolean DEFAULT '0' COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `uid_vid_type_actor-id_target-uri-id` (`uid`,`vid`,`type`,`actor-id`,`target-uri-id`),
|
||||
INDEX `vid` (`vid`),
|
||||
INDEX `actor-id` (`actor-id`),
|
||||
INDEX `target-uri-id` (`target-uri-id`),
|
||||
INDEX `parent-uri-id` (`parent-uri-id`),
|
||||
INDEX `seen_uid` (`seen`,`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`vid`) REFERENCES `verb` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
FOREIGN KEY (`actor-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`target-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`parent-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='notifications';
|
||||
|
||||
--
|
||||
-- TABLE notify
|
||||
--
|
||||
|
@ -1685,7 +1712,9 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
`parent-post`.`author-id` AS `parent-author-id`,
|
||||
`parent-post-author`.`url` AS `parent-author-link`,
|
||||
`parent-post-author`.`name` AS `parent-author-name`,
|
||||
`parent-post-author`.`network` AS `parent-author-network`
|
||||
`parent-post-author`.`network` AS `parent-author-network`,
|
||||
`parent-post-author`.`blocked` AS `parent-author-blocked`,
|
||||
`parent-post-author`.`hidden` AS `parent-author-hidden`
|
||||
FROM `post-user`
|
||||
STRAIGHT_JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-user`.`parent-uri-id` AND `post-thread-user`.`uid` = `post-user`.`uid`
|
||||
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-user`.`contact-id`
|
||||
|
|
|
@ -122,7 +122,6 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en
|
|||
These emdpoints are planned to be implemented somewhere in the future.
|
||||
|
||||
- [`PATCH /api/v1/accounts/update_credentials`](https://docs.joinmastodon.org/methods/accounts/)
|
||||
- [`GET /api/v1/instance/activity`](https://docs.joinmastodon.org/methods/instance#weekly-activity)
|
||||
|
||||
## Dummy endpoints
|
||||
|
||||
|
@ -139,7 +138,7 @@ They refer to features that don't exist in Friendica yet.
|
|||
## Non supportable endpoints
|
||||
|
||||
These endpoints won't be implemented at the moment.
|
||||
They refer to features that don't exist in Friendica yet.
|
||||
They refer to features or data that don't exist in Friendica yet.
|
||||
|
||||
- [`POST /api/v1/accounts`](https://docs.joinmastodon.org/methods/accounts/)
|
||||
- [`POST /api/v1/accounts/:id/pin`](https://docs.joinmastodon.org/methods/accounts/)
|
||||
|
@ -164,6 +163,7 @@ They refer to features that don't exist in Friendica yet.
|
|||
- [`POST /api/v1/filters/:id`](https://docs.joinmastodon.org/methods/accounts/filters/)
|
||||
- [`PUT /api/v1/filters/:id`](https://docs.joinmastodon.org/methods/accounts/filters/)
|
||||
- [`DELETE /api/v1/filters/:id`](https://docs.joinmastodon.org/methods/accounts/filters/)
|
||||
- [`GET /api/v1/instance/activity`](https://docs.joinmastodon.org/methods/instance#weekly-activity)
|
||||
- [`POST /api/v1/markers`](https://docs.joinmastodon.org/methods/timelines/markers/)
|
||||
- [`GET /api/v1/polls/:id`](https://docs.joinmastodon.org/methods/statuses/polls/)
|
||||
- [`POST /api/v1/polls/:id/votes`](https://docs.joinmastodon.org/methods/statuses/polls/)
|
||||
|
|
13
doc/FAQ.md
13
doc/FAQ.md
|
@ -33,9 +33,10 @@ User
|
|||
If this FAQ does not answer your question you can always reach out to the community via the following options:
|
||||
|
||||
* Friendica Support Forum: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* XMPP: support(at)forum.friendi.ca
|
||||
* IRC: [#friendica at freenode.net](https://webchat.freenode.net/?settings=#friendica)
|
||||
* Matrix: [#friendica-en:matrix.org](https://matrix.to/#/#friendica-en:matrix.org) or [#friendi.ca:matrix.org](https://matrix.to/#/#friendi.ca:matrix.org)
|
||||
* Community chat rooms (the IRC, Matrix and XMPP rooms are bridged) these public chats are logged [from IRC](https://gnusociarg.nsupdate.info/2021/%23friendica/) and [Matrix](https://view.matrix.org/alias/%23friendi.ca:matrix.org/)
|
||||
* XMPP: support(at)forum.friendi.ca
|
||||
* IRC: #friendica at libera.chat or [#friendica at freenode.net](https://webchat.freenode.net/?settings=#friendica)
|
||||
* Matrix: [#friendica-en:matrix.org](https://matrix.to/#/#friendica-en:matrix.org) or [#friendi.ca:matrix.org](https://matrix.to/#/#friendi.ca:matrix.org)
|
||||
* [Mailing List](http://mailman.friendi.ca/mailman/listinfo/support-friendi.ca)
|
||||
<!--- * [XMPP](xmpp:support@forum.friendi.ca?join)
|
||||
https://github.com/github/markup/issues/202
|
||||
|
@ -182,6 +183,7 @@ Example: Friendica Support
|
|||
### What friendica clients can I use?
|
||||
|
||||
Friendica is using a [Twitter/GNU Social compatible API](help/api), which means you can use any Twitter/GNU Social client for your platform as long as you can change the API path in its settings.
|
||||
Since the 2021.06 release, Friendica also supports the Mastodon API.
|
||||
Here is a list of known working clients:
|
||||
|
||||
* Android
|
||||
|
@ -195,6 +197,11 @@ Here is a list of known working clients:
|
|||
* [Choqok](https://choqok.kde.org)
|
||||
* Windows
|
||||
* [Friendica Mobile](https://www.microsoft.com/de-DE/store/p/friendica-mobile/9nblggh0fhmn?rtc=1) (Windows 10)
|
||||
* [Husky](https://husky.fwgs.ru)
|
||||
* [Subway Tooter](https://github.com/tateisu/SubwayTooter)
|
||||
* [Tusky](https://tusky.app)
|
||||
* [twitlatte](https://github.com/moko256/twitlatte)
|
||||
* [Yuito](https://github.com/accelforce/Yuito)
|
||||
|
||||
Depending on the features of the client you might encounter some glitches in usability, like being limited in the length of your postings to 140 characters and having no access to the [permission settings](help/Groups-and-Privacy).
|
||||
|
||||
|
|
|
@ -68,9 +68,10 @@ Friendica Documentation and Resources
|
|||
* Ways to get Support
|
||||
* Friendica Support Forum: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* [Mailing List Archive](http://mailman.friendi.ca/mailman/listinfo/support-friendi.ca) you can subscribe to the list by sending an email to ``support-request(at)friendi.ca?subject=subscribe``
|
||||
* XMPP/Jabber MUC: support(at)forum.friendi.ca
|
||||
* IRC: [#friendica at freenode.net](https://webchat.freenode.net/?settings=#friendica)
|
||||
* Matrix: [#friendi.ca](https://matrix.to/#/#friendi.ca:matrix.org) or [#friendica-en](https://matrix.to/#/#friendica-en:matrix.org) at matrix.org
|
||||
* Community chat rooms (the IRC, Matrix and XMPP rooms are bridget) these public chats are logged [from IRC](https://gnusociarg.nsupdate.info/2021/%23friendica/) and [Matrix](https://view.matrix.org/alias/%23friendi.ca:matrix.org/)
|
||||
* XMPP/Jabber MUC: support(at)forum.friendi.ca
|
||||
* IRC: [#friendica at freenode.net](https://webchat.freenode.net/?settings=#friendica)
|
||||
* Matrix: [#friendi.ca](https://matrix.to/#/#friendi.ca:matrix.org) or [#friendica-en](https://matrix.to/#/#friendica-en:matrix.org) at matrix.org
|
||||
|
||||
**About**
|
||||
|
||||
|
|
|
@ -36,9 +36,10 @@ Wenn Du Deinen Account nicht nutzen kannst, kannst Du einen Account auf einer ö
|
|||
Wenn du dir keinen weiteren Friendica Account einrichten willst, kannst du auch gerne über einen der folgenden alternativen Kanäle Hilfe suchen:
|
||||
|
||||
* Friendica Support Forum: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* XMPP: support(at)forum.friendi.ca
|
||||
* IRC: [#friendica at freenode.net](https://webchat.freenode.net/?settings=#friendica)
|
||||
* Matrix: [#friendica-en:matrix.org](https://matrix.to/#/#friendica-en:matrix.org) or [#friendi.ca:matrix.org](https://matrix.to/#/#friendi.ca:matrix.org)
|
||||
* Chats der Friendica Community (die IRC, Matrix und XMPP Räume sind mit einer Brücke verbunden) Logs dieser öffentlichen Chaträume können [hier aus dem IRC](https://gnusociarg.nsupdate.info/2021/%23frie) und [hier aus der Matrix](https://view.matrix.org/alias/%23friendi.ca:matrix.org/) gefunden werden.
|
||||
* XMPP: support(at)forum.friendi.ca
|
||||
* IRC: #friendica aut libera.chat oder [#friendica auf freenode.net](https://webchat.freenode.net/?settings=#friendica)
|
||||
* Matrix: [#friendica-en:matrix.org](https://matrix.to/#/#friendica-en:matrix.org) or [#friendi.ca:matrix.org](https://matrix.to/#/#friendi.ca:matrix.org)
|
||||
* [Mailing List](http://mailman.friendi.ca/mailman/listinfo/support-friendi.ca)
|
||||
<!--- * [XMPP](xmpp:support@forum.friendi.ca?join)
|
||||
https://github.com/github/markup/issues/202
|
||||
|
@ -192,6 +193,7 @@ Beispiel: Friendica Support
|
|||
### Gibt es Clients für Friendica?
|
||||
|
||||
Friendica verwendet eine [Twitter/GNU Social](help/api) kompatible API.
|
||||
Seit der Version 2021.06 unterstützt Friendica außerdem die Mastodon API.
|
||||
Das bedeutet, dass du jeden Twitter/GNU Social Client verwenden kannst in dem du den API Pfad entsprechend änderst.
|
||||
|
||||
Hier ist eine Liste von Clients bei denen dies möglich ist, bzw. die speziell für Friendica entwickelt werden:
|
||||
|
@ -205,6 +207,11 @@ Hier ist eine Liste von Clients bei denen dies möglich ist, bzw. die speziell f
|
|||
* Mustard and Mustard-Mod
|
||||
* SailfishOS
|
||||
* [Friendly](https://openrepos.net/content/fabrixxm/friendly#comment-form)
|
||||
* [Husky](https://husky.fwgs.ru)
|
||||
* [Subway Tooter](https://github.com/tateisu/SubwayTooter)
|
||||
* [Tusky](https://tusky.app)
|
||||
* [twitlatte](https://github.com/moko256/twitlatte)
|
||||
* [Yuito](https://github.com/accelforce/Yuito)
|
||||
* Linux
|
||||
* Hotot
|
||||
* Choqok
|
||||
|
|
|
@ -64,9 +64,10 @@ Friendica - Dokumentation und Ressourcen
|
|||
* Support Kanäle
|
||||
* Friendica Support Forum: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* [Mailing Listen Archiv](http://mailman.friendi.ca/mailman/listinfo/support-friendi.ca) zum Abonnieren der Liste eine E-Mail an ``support-request(at)friendi.ca?subject=subscribe`` senden
|
||||
* XMPP/Jabber MUC: support(at)forum.friendi.ca
|
||||
* IRC: [#friendica auf irc.freenode.net](https://webchat.freenode.net/?settings=#friendica)
|
||||
* Matrix: [#friendi.ca](https://matrix.to/#/#friendi.ca:matrix.org) oder [#friendica-en](https://matrix.to/#/#friendica-en:matrix.org) auf matrix.org
|
||||
* Chats der Friendica Community (die IRC, Matrix und XMPP Räume sind mit einer Brücke verbunden) Logs dieser öffentlichen Chaträume können [hier aus dem IRC](https://gnusociarg.nsupdate.info/2021/%23frie) und [hier aus der Matrix](https://view.matrix.org/alias/%23friendi.ca:matrix.org/) gefunden werden.
|
||||
* XMPP/Jabber MUC: support(at)forum.friendi.ca
|
||||
* IRC: #friendica auf libera.chat oder [#friendica auf irc.freenode.net](https://webchat.freenode.net/?settings=#friendica)
|
||||
* Matrix: [#friendi.ca](https://matrix.to/#/#friendi.ca:matrix.org) oder [#friendica-en](https://matrix.to/#/#friendica-en:matrix.org) auf matrix.org
|
||||
|
||||
**Über diese Seite**
|
||||
|
||||
|
|
|
@ -1186,12 +1186,12 @@ function api_statuses_update($type)
|
|||
INNER JOIN `user` ON `user`.`uid` = `photo`.`uid` WHERE `resource-id` IN
|
||||
(SELECT `resource-id` FROM `photo` WHERE `id` = ?) AND `photo`.`uid` = ?
|
||||
ORDER BY `photo`.`width` DESC LIMIT 2", $id, api_user()));
|
||||
|
||||
|
||||
if (!empty($media)) {
|
||||
$ressources[] = $media[0]['resource-id'];
|
||||
$phototypes = Images::supportedTypes();
|
||||
$ext = $phototypes[$media[0]['type']];
|
||||
|
||||
|
||||
$attachment = ['type' => Post\Media::IMAGE, 'mimetype' => $media[0]['type'],
|
||||
'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . '.' . $ext,
|
||||
'size' => $media[0]['datasize'],
|
||||
|
@ -1199,7 +1199,7 @@ function api_statuses_update($type)
|
|||
'description' => $media[0]['desc'] ?? '',
|
||||
'width' => $media[0]['width'],
|
||||
'height' => $media[0]['height']];
|
||||
|
||||
|
||||
if (count($media) > 1) {
|
||||
$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . '.' . $ext;
|
||||
$attachment['preview-width'] = $media[1]['width'];
|
||||
|
|
|
@ -737,8 +737,6 @@ function conversation_fetch_comments($thread_items, bool $pinned, array $activit
|
|||
}
|
||||
}
|
||||
|
||||
$name = $row['causer-contact-type'] == Contact::TYPE_RELAY ? $row['causer-link'] : $row['causer-name'];
|
||||
|
||||
switch ($row['post-reason']) {
|
||||
case Item::PR_TO:
|
||||
$row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'to')];
|
||||
|
@ -769,9 +767,9 @@ function conversation_fetch_comments($thread_items, bool $pinned, array $activit
|
|||
if (($row['gravity'] == GRAVITY_PARENT) && !empty($row['causer-id'])) {
|
||||
$causer = ['uid' => 0, 'id' => $row['causer-id'],
|
||||
'network' => $row['causer-network'], 'url' => $row['causer-link']];
|
||||
$row['reshared'] = DI::l10n()->t('%s reshared this.', '<a href="'. htmlentities(Contact::magicLinkByContact($causer)) .'">' . htmlentities($name) . '</a>');
|
||||
$row['reshared'] = DI::l10n()->t('%s reshared this.', '<a href="'. htmlentities(Contact::magicLinkByContact($causer)) .'">' . htmlentities($row['causer-name']) . '</a>');
|
||||
}
|
||||
$row['direction'] = ['direction' => 3, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Reshared') : DI::l10n()->t('Reshared by %s', $name))];
|
||||
$row['direction'] = ['direction' => 3, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Reshared') : DI::l10n()->t('Reshared by %s <%s>', $row['causer-name'], $row['causer-link']))];
|
||||
break;
|
||||
case Item::PR_COMMENT:
|
||||
$row['direction'] = ['direction' => 5, 'title' => DI::l10n()->t('%s is participating in this thread.', $row['author-name'])];
|
||||
|
@ -783,10 +781,10 @@ function conversation_fetch_comments($thread_items, bool $pinned, array $activit
|
|||
$row['direction'] = ['direction' => 9, 'title' => DI::l10n()->t('Global')];
|
||||
break;
|
||||
case Item::PR_RELAY:
|
||||
$row['direction'] = ['direction' => 10, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Relayed') : DI::l10n()->t('Relayed by %s.', $name))];
|
||||
$row['direction'] = ['direction' => 10, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Relayed') : DI::l10n()->t('Relayed by %s <%s>', $row['causer-name'], $row['causer-link']))];
|
||||
break;
|
||||
case Item::PR_FETCHED:
|
||||
$row['direction'] = ['direction' => 2, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Fetched') : DI::l10n()->t('Fetched because of %s', $name))];
|
||||
$row['direction'] = ['direction' => 2, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Fetched') : DI::l10n()->t('Fetched because of %s <%s>', $row['causer-name'], $row['causer-link']))];
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -184,9 +184,9 @@ function notification($params)
|
|||
// First go for the general message
|
||||
|
||||
// "George Bull's post"
|
||||
if ($params['activity']['origin_comment']) {
|
||||
if (!empty($params['activity']['origin_comment'])) {
|
||||
$message = $l10n->t('%1$s replied to you on %2$s\'s %3$s %4$s');
|
||||
} elseif ($params['activity']['explicit_tagged']) {
|
||||
} elseif (!empty($params['activity']['explicit_tagged'])) {
|
||||
$message = $l10n->t('%1$s tagged you on %2$s\'s %3$s %4$s');
|
||||
} else {
|
||||
$message = $l10n->t('%1$s commented on %2$s\'s %3$s %4$s');
|
||||
|
@ -197,10 +197,10 @@ function notification($params)
|
|||
// Then look for the special cases
|
||||
|
||||
// "your post"
|
||||
if ($params['activity']['origin_thread']) {
|
||||
if ($params['activity']['origin_comment']) {
|
||||
if (!empty($params['activity']['origin_thread'])) {
|
||||
if (!empty($params['activity']['origin_comment'])) {
|
||||
$message = $l10n->t('%1$s replied to you on your %2$s %3$s');
|
||||
} elseif ($params['activity']['explicit_tagged']) {
|
||||
} elseif (!empty($params['activity']['explicit_tagged'])) {
|
||||
$message = $l10n->t('%1$s tagged you on your %2$s %3$s');
|
||||
} else {
|
||||
$message = $l10n->t('%1$s commented on your %2$s %3$s');
|
||||
|
@ -209,9 +209,9 @@ function notification($params)
|
|||
$dest_str = sprintf($message, $params['source_name'], $item_post_type, $title);
|
||||
// "their post"
|
||||
} elseif ($item['author-link'] == $params['source_link']) {
|
||||
if ($params['activity']['origin_comment']) {
|
||||
if (!empty($params['activity']['origin_comment'])) {
|
||||
$message = $l10n->t('%1$s replied to you on their %2$s %3$s');
|
||||
} elseif ($params['activity']['explicit_tagged']) {
|
||||
} elseif (!empty($params['activity']['explicit_tagged'])) {
|
||||
$message = $l10n->t('%1$s tagged you on their %2$s %3$s');
|
||||
} else {
|
||||
$message = $l10n->t('%1$s commented on their %2$s %3$s');
|
||||
|
@ -224,7 +224,7 @@ function notification($params)
|
|||
// So, we cannot have different subjects for notifications of the same thread.
|
||||
// Before this we have the name of the replier on the subject rendering
|
||||
// different subjects for messages on the same thread.
|
||||
if ($params['activity']['explicit_tagged']) {
|
||||
if (!empty($params['activity']['explicit_tagged'])) {
|
||||
$subject = $l10n->t('%s %s tagged you', $subjectPrefix, $params['source_name']);
|
||||
|
||||
$preamble = $l10n->t('%1$s tagged you at %2$s', $params['source_name'], $sitename);
|
||||
|
|
|
@ -84,7 +84,7 @@ function cal_init(App $a)
|
|||
'$about' => BBCode::convert($a->profile['about']),
|
||||
]);
|
||||
|
||||
$cal_widget = Widget\CalendarExport::getHTML();
|
||||
$cal_widget = Widget\CalendarExport::getHTML($user['uid']);
|
||||
|
||||
if (empty(DI::page()['aside'])) {
|
||||
DI::page()['aside'] = '';
|
||||
|
|
|
@ -128,6 +128,7 @@ function dfrn_poll_init(App $a)
|
|||
$_SESSION['visitor_handle'] = $r[0]['addr'];
|
||||
$_SESSION['visitor_visiting'] = $r[0]['uid'];
|
||||
$_SESSION['my_url'] = $r[0]['url'];
|
||||
$_SESSION['remote_comment'] = $r[0]['subscribe'];
|
||||
|
||||
Session::setVisitorsContacts();
|
||||
|
||||
|
@ -497,8 +498,10 @@ function dfrn_poll_content(App $a)
|
|||
$_SESSION['authenticated'] = 1;
|
||||
$_SESSION['visitor_id'] = $r[0]['id'];
|
||||
$_SESSION['visitor_home'] = $r[0]['url'];
|
||||
$_SESSION['visitor_handle'] = $r[0]['addr'];
|
||||
$_SESSION['visitor_visiting'] = $r[0]['uid'];
|
||||
$_SESSION['my_url'] = $r[0]['url'];
|
||||
$_SESSION['remote_comment'] = $r[0]['subscribe'];
|
||||
|
||||
Session::setVisitorsContacts();
|
||||
|
||||
|
|
|
@ -236,6 +236,7 @@ function display_content(App $a, $update = false, $update_uid = 0)
|
|||
}
|
||||
|
||||
if (!DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
DBA::update('notification', ['seen' => true], ['parent-uri-id' => $item['parent-uri-id'], 'uid' => local_user()]);
|
||||
DBA::update('notify', ['seen' => true], ['parent-uri-id' => $item['parent-uri-id'], 'uid' => local_user()]);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ function events_init(App $a)
|
|||
DI::page()['aside'] = '';
|
||||
}
|
||||
|
||||
$cal_widget = CalendarExport::getHTML();
|
||||
$cal_widget = CalendarExport::getHTML(local_user());
|
||||
|
||||
DI::page()['aside'] .= $cal_widget;
|
||||
|
||||
|
|
38
mod/item.php
38
mod/item.php
|
@ -410,21 +410,22 @@ function item_post(App $a) {
|
|||
}
|
||||
}
|
||||
|
||||
$success = ItemHelper::replaceTag($body, $inform, local_user() ? local_user() : $profile_uid, $tag, $network);
|
||||
if ($success['replaced']) {
|
||||
$tagged[] = $tag;
|
||||
}
|
||||
// When the forum is private or the forum is addressed with a "!" make the post private
|
||||
if (!empty($success['contact']['prv']) || ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) {
|
||||
$private_forum = $success['contact']['prv'];
|
||||
$only_to_forum = ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
|
||||
$private_id = $success['contact']['id'];
|
||||
$forum_contact = $success['contact'];
|
||||
} elseif (!empty($success['contact']['forum']) && ($str_contact_allow == '<' . $success['contact']['id'] . '>')) {
|
||||
$private_forum = false;
|
||||
$only_to_forum = true;
|
||||
$private_id = $success['contact']['id'];
|
||||
$forum_contact = $success['contact'];
|
||||
if ($success = ItemHelper::replaceTag($body, $inform, local_user() ? local_user() : $profile_uid, $tag, $network)) {
|
||||
if ($success['replaced']) {
|
||||
$tagged[] = $tag;
|
||||
}
|
||||
// When the forum is private or the forum is addressed with a "!" make the post private
|
||||
if (!empty($success['contact']['prv']) || ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) {
|
||||
$private_forum = $success['contact']['prv'];
|
||||
$only_to_forum = ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
|
||||
$private_id = $success['contact']['id'];
|
||||
$forum_contact = $success['contact'];
|
||||
} elseif (!empty($success['contact']['forum']) && ($str_contact_allow == '<' . $success['contact']['id'] . '>')) {
|
||||
$private_forum = false;
|
||||
$only_to_forum = true;
|
||||
$private_id = $success['contact']['id'];
|
||||
$forum_contact = $success['contact'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -713,13 +714,6 @@ function item_post(App $a) {
|
|||
unset($datarray['self']);
|
||||
unset($datarray['api_source']);
|
||||
|
||||
if ($origin) {
|
||||
$signed = Diaspora::createCommentSignature($uid, $datarray);
|
||||
if (!empty($signed)) {
|
||||
$datarray['diaspora_signed_text'] = json_encode($signed);
|
||||
}
|
||||
}
|
||||
|
||||
$post_id = Item::insert($datarray);
|
||||
|
||||
if (!$post_id) {
|
||||
|
|
|
@ -1275,7 +1275,7 @@ function photos_content(App $a)
|
|||
}
|
||||
|
||||
if (!empty($link_item['parent']) && !empty($link_item['uid'])) {
|
||||
$condition = ["`parent` = ? AND `gravity` != ?", $link_item['parent'], GRAVITY_PARENT];
|
||||
$condition = ["`parent` = ? AND `gravity` = ?", $link_item['parent'], GRAVITY_COMMENT];
|
||||
$total = Post::count($condition);
|
||||
|
||||
$pager = new Pager(DI::l10n(), DI::args()->getQueryString());
|
||||
|
|
|
@ -326,7 +326,7 @@ function settings_post(App $a)
|
|||
$detailed_notif = (($_POST['detailed_notif'] == 1) ? 1 : 0);
|
||||
|
||||
$notify_ignored = (($_POST['notify_ignored'] == 1) ? 1 : 0);
|
||||
|
||||
|
||||
$notify = 0;
|
||||
|
||||
if (!empty($_POST['notify1'])) {
|
||||
|
|
|
@ -81,25 +81,27 @@ function tagrm_content(App $a)
|
|||
{
|
||||
$o = '';
|
||||
|
||||
$photo_return = $_SESSION['photo_return'] ?? '';
|
||||
|
||||
if (!local_user()) {
|
||||
DI::baseUrl()->redirect($_SESSION['photo_return']);
|
||||
DI::baseUrl()->redirect($photo_return);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
if ($a->argc == 3) {
|
||||
update_tags($a->argv[1], [Strings::escapeTags(trim(hex2bin($a->argv[2])))]);
|
||||
DI::baseUrl()->redirect($_SESSION['photo_return']);
|
||||
DI::baseUrl()->redirect($photo_return);
|
||||
}
|
||||
|
||||
$item_id = (($a->argc > 1) ? intval($a->argv[1]) : 0);
|
||||
if (!$item_id) {
|
||||
DI::baseUrl()->redirect($_SESSION['photo_return']);
|
||||
DI::baseUrl()->redirect($photo_return);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
$item = Post::selectFirst(['uri-id'], ['id' => $item_id, 'uid' => local_user()]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::baseUrl()->redirect($_SESSION['photo_return']);
|
||||
DI::baseUrl()->redirect($photo_return);
|
||||
}
|
||||
|
||||
$tag_text = Tag::getCSVByURIId($item['uri-id']);
|
||||
|
@ -107,7 +109,7 @@ function tagrm_content(App $a)
|
|||
$arr = explode(',', $tag_text);
|
||||
|
||||
if (empty($arr)) {
|
||||
DI::baseUrl()->redirect($_SESSION['photo_return']);
|
||||
DI::baseUrl()->redirect($photo_return);
|
||||
}
|
||||
|
||||
$o .= '<h3>' . DI::l10n()->t('Remove Item Tag') . '</h3>';
|
||||
|
|
|
@ -158,4 +158,4 @@ function unfollow_process(string $url)
|
|||
}
|
||||
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,4 +43,4 @@ function update_notes_content(App $a) {
|
|||
$text = notes_content($a, $profile_uid);
|
||||
|
||||
System::htmlUpdateExit($text);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ return [
|
|||
// ****************************************************************
|
||||
|
||||
'config' => [
|
||||
'hostname' => 'friendica.local',
|
||||
'admin_email' => 'admin@friendica.local',
|
||||
'sitename' => 'Friendica Social Network',
|
||||
'register_policy' => \Friendica\Module\Register::OPEN,
|
||||
|
@ -38,5 +39,6 @@ return [
|
|||
'default_timezone' => 'UTC',
|
||||
'language' => 'en',
|
||||
'basepath' => '/vagrant',
|
||||
'ssl_policy' => \Friendica\App\BaseURL::SSL_POLICY_SELFSIGN,
|
||||
],
|
||||
];
|
||||
|
|
24
mods/phpdoc-config.xml
Normal file
24
mods/phpdoc-config.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<phpdocumentor xmlns="https://www.phpdoc.org" configVersion="3.0">
|
||||
<paths>
|
||||
<output>../doc/api</output>
|
||||
<cache>../doc/cache</cache>
|
||||
</paths>
|
||||
<version number="3.0">
|
||||
<api>
|
||||
<source dsn="../">
|
||||
<path>src</path>
|
||||
<path>mod</path>
|
||||
<path>include</path>
|
||||
<path>static</path>
|
||||
<path>bin</path>
|
||||
<path>view</path>
|
||||
</source>
|
||||
<ignore>
|
||||
<path>vendor/**/*</path>
|
||||
<path>asset/**/*</path>
|
||||
<path>bin/dev/**/*</path>
|
||||
</ignore>
|
||||
</api>
|
||||
</version>
|
||||
</phpdocumentor>
|
|
@ -34,6 +34,7 @@ use Friendica\Core\L10n;
|
|||
use Friendica\Core\System;
|
||||
use Friendica\Core\Theme;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Module\Special\HTTPException as ModuleHTTPException;
|
||||
use Friendica\Network\HTTPException;
|
||||
|
@ -464,6 +465,11 @@ class App
|
|||
if (Core\Session::get('visitor_home') != $_GET["zrl"]) {
|
||||
Core\Session::set('my_url', $_GET['zrl']);
|
||||
Core\Session::set('authenticated', 0);
|
||||
|
||||
$remote_contact = Contact::getByURL($_GET['zrl'], false, ['subscribe']);
|
||||
if (!empty($remote_contact['subscribe'])) {
|
||||
$_SESSION['remote_comment'] = $remote_contact['subscribe'];
|
||||
}
|
||||
}
|
||||
|
||||
Model\Profile::zrlInit($this);
|
||||
|
|
|
@ -30,7 +30,6 @@ use Friendica\Core\Installer;
|
|||
use Friendica\Core\Theme;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Util\BasePath;
|
||||
use Friendica\Util\ConfigFileLoader;
|
||||
use RuntimeException;
|
||||
|
||||
class AutomaticInstallation extends Console
|
||||
|
@ -112,7 +111,7 @@ HELP;
|
|||
protected function doExecute()
|
||||
{
|
||||
// Initialise the app
|
||||
$this->out("Initializing setup...\n");
|
||||
$this->out("Initializing setup...");
|
||||
|
||||
$installer = new Installer();
|
||||
|
||||
|
@ -121,10 +120,10 @@ HELP;
|
|||
$basepath = new BasePath($basePathConf);
|
||||
$installer->setUpCache($configCache, $basepath->getPath());
|
||||
|
||||
$this->out(" Complete!\n\n");
|
||||
$this->out(" Complete!\n");
|
||||
|
||||
// Check Environment
|
||||
$this->out("Checking environment...\n");
|
||||
$this->out("Checking environment...");
|
||||
|
||||
$installer->resetChecks();
|
||||
|
||||
|
@ -133,24 +132,26 @@ HELP;
|
|||
throw new RuntimeException($errorMessage);
|
||||
}
|
||||
|
||||
$this->out(" Complete!\n\n");
|
||||
$this->out(" Complete!\n");
|
||||
|
||||
// if a config file is set,
|
||||
$config_file = $this->getOption(['f', 'file']);
|
||||
|
||||
if (!empty($config_file)) {
|
||||
|
||||
$this->out("Loading config file '$config_file'...");
|
||||
if (!file_exists($config_file)) {
|
||||
throw new RuntimeException("ERROR: Config file does not exist.\n");
|
||||
throw new RuntimeException("ERROR: Config file does not exist.");
|
||||
}
|
||||
|
||||
//reload the config cache
|
||||
$loader = new ConfigFileLoader($config_file);
|
||||
$loader->setupCache($configCache);
|
||||
|
||||
//append config file to the config cache
|
||||
$config = include($config_file);
|
||||
if (!is_array($config)) {
|
||||
throw new Exception('Error loading config file ' . $config_file);
|
||||
}
|
||||
$configCache->load($config, Cache::SOURCE_FILE);
|
||||
} else {
|
||||
// Creating config file
|
||||
$this->out("Creating config file...\n");
|
||||
$this->out("Creating config file...");
|
||||
|
||||
$save_db = $this->getOption(['s', 'savedb'], false);
|
||||
|
||||
|
@ -202,10 +203,10 @@ HELP;
|
|||
$installer->createConfig($configCache);
|
||||
}
|
||||
|
||||
$this->out("Complete!\n\n");
|
||||
$this->out(" Complete!\n");
|
||||
|
||||
// Check database connection
|
||||
$this->out("Checking database...\n");
|
||||
$this->out("Checking database...");
|
||||
|
||||
$installer->resetChecks();
|
||||
|
||||
|
@ -214,7 +215,7 @@ HELP;
|
|||
throw new RuntimeException($errorMessage);
|
||||
}
|
||||
|
||||
$this->out(" Complete!\n\n");
|
||||
$this->out(" Complete!\n");
|
||||
|
||||
// Install database
|
||||
$this->out("Inserting data into database...\n");
|
||||
|
@ -228,24 +229,24 @@ HELP;
|
|||
|
||||
if (!empty($config_file) && $config_file != 'config' . DIRECTORY_SEPARATOR . 'local.config.php') {
|
||||
// Copy config file
|
||||
$this->out("Copying config file...\n");
|
||||
if (!copy($basePathConf . DIRECTORY_SEPARATOR . $config_file, $basePathConf . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'local.config.php')) {
|
||||
$this->out("Copying config file...");
|
||||
if (!copy($config_file, $basePathConf . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'local.config.php')) {
|
||||
throw new RuntimeException("ERROR: Saving config file failed. Please copy '$config_file' to '" . $basePathConf . "'" . DIRECTORY_SEPARATOR . "config" . DIRECTORY_SEPARATOR . "local.config.php' manually.\n");
|
||||
}
|
||||
}
|
||||
|
||||
$this->out(" Complete!\n\n");
|
||||
$this->out(" Complete!\n");
|
||||
|
||||
// Install theme
|
||||
$this->out("Installing theme\n");
|
||||
$this->out("Installing theme");
|
||||
if (!empty($this->config->get('system', 'theme'))) {
|
||||
Theme::install($this->config->get('system', 'theme'));
|
||||
$this->out(" Complete\n\n");
|
||||
$this->out(" Complete\n");
|
||||
} else {
|
||||
$this->out(" Theme setting is empty. Please check the file 'config/local.config.php'\n\n");
|
||||
$this->out(" Theme setting is empty. Please check the file 'config/local.config.php'\n");
|
||||
}
|
||||
|
||||
$this->out("\nInstallation is finished\n");
|
||||
$this->out("\nInstallation is finished");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -127,16 +127,6 @@ class Item
|
|||
$tag_type = substr($tag, 0, 1);
|
||||
//is it already replaced?
|
||||
if (strpos($tag, '[url=')) {
|
||||
// Checking for the alias that is used for OStatus
|
||||
$pattern = '/[@!]\[url\=(.*?)\](.*?)\[\/url\]/ism';
|
||||
if (preg_match($pattern, $tag, $matches)) {
|
||||
$data = Contact::getByURL($matches[1], false, ['alias', 'nick']);
|
||||
|
||||
if ($data['alias'] != '') {
|
||||
$newtag = '@[url=' . $data['alias'] . ']' . $data['nick'] . '[/url]';
|
||||
}
|
||||
}
|
||||
|
||||
return $replaced;
|
||||
}
|
||||
|
||||
|
|
|
@ -2196,9 +2196,7 @@ class BBCode
|
|||
}
|
||||
}
|
||||
|
||||
$success = Item::replaceTag($body, $inform, $profile_uid, $tag, $network);
|
||||
|
||||
if ($success['replaced']) {
|
||||
if (($success = Item::replaceTag($body, $inform, $profile_uid, $tag, $network)) && $success['replaced']) {
|
||||
$tagged[] = $tag;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
|
||||
namespace Friendica\Content\Widget;
|
||||
|
||||
use Friendica\Content\Feature;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\User;
|
||||
|
||||
/**
|
||||
* TagCloud widget
|
||||
|
@ -34,36 +34,27 @@ class CalendarExport
|
|||
{
|
||||
/**
|
||||
* Get the events widget.
|
||||
* @param int $uid
|
||||
*
|
||||
* @return string Formated HTML of the calendar widget.
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function getHTML() {
|
||||
$a = DI::app();
|
||||
|
||||
if (empty($a->data['user'])) {
|
||||
return;
|
||||
public static function getHTML(int $uid = 0) {
|
||||
if (empty($uid)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$owner_uid = intval($a->data['user']['uid']);
|
||||
|
||||
// The permission testing is a little bit tricky because we have to respect many cases.
|
||||
|
||||
// It's not the private events page (we don't get the $owner_uid for /events).
|
||||
if (!local_user() && !$owner_uid) {
|
||||
return;
|
||||
$user = User::getById($uid, ['nickname']);
|
||||
if (empty($user['nickname'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// $a->data is only available if the profile page is visited. If the visited page is not part
|
||||
// of the profile page it should be the personal /events page. So we can use $a->user.
|
||||
$user = ($a->data['user']['nickname'] ?? '') ?: $a->user['nickname'];
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate("widget/events.tpl");
|
||||
$return = Renderer::replaceMacros($tpl, [
|
||||
'$etitle' => DI::l10n()->t("Export"),
|
||||
'$export_ical' => DI::l10n()->t("Export calendar as ical"),
|
||||
'$export_csv' => DI::l10n()->t("Export calendar as csv"),
|
||||
'$user' => $user
|
||||
'$user' => $user['nickname']
|
||||
]);
|
||||
|
||||
return $return;
|
||||
|
|
|
@ -127,8 +127,8 @@ class TagCloud
|
|||
private static function tagCalc(array $arr)
|
||||
{
|
||||
$tags = [];
|
||||
$min = 1e9;
|
||||
$max = -1e9;
|
||||
$min = 1000000000.0;
|
||||
$max = -1000000000.0;
|
||||
$x = 0;
|
||||
|
||||
if (!$arr) {
|
||||
|
@ -145,7 +145,7 @@ class TagCloud
|
|||
}
|
||||
|
||||
usort($tags, 'self::tagsSort');
|
||||
$range = max(.01, $max - $min) * 1.0001;
|
||||
$range = max(0.01, $max - $min) * 1.0001;
|
||||
|
||||
for ($x = 0; $x < count($tags); $x ++) {
|
||||
$tags[$x][2] = 1 + floor(9 * ($tags[$x][1] - $min) / $range);
|
||||
|
|
|
@ -465,7 +465,7 @@ class Installer
|
|||
|
||||
$status = $this->checkFunction('proc_open',
|
||||
DI::l10n()->t('Program execution functions'),
|
||||
DI::l10n()->t('Error: Program execution functions required but not enabled.'),
|
||||
DI::l10n()->t('Error: Program execution functions (proc_open) required but not enabled.'),
|
||||
true
|
||||
);
|
||||
$returnVal = $returnVal ? $status : false;
|
||||
|
|
|
@ -241,6 +241,12 @@ class DBStructure
|
|||
// Assign all field that are present in the table
|
||||
foreach ($fieldnames as $field) {
|
||||
if (isset($data[$field])) {
|
||||
// Limit the length of varchar, varbinary, char and binrary fields
|
||||
if (is_string($data[$field]) && preg_match("/char\((\d*)\)/", $definition[$table]['fields'][$field]['type'], $result)) {
|
||||
$data[$field] = mb_substr($data[$field], 0, $result[1]);
|
||||
} elseif (is_string($data[$field]) && preg_match("/binary\((\d*)\)/", $definition[$table]['fields'][$field]['type'], $result)) {
|
||||
$data[$field] = substr($data[$field], 0, $result[1]);
|
||||
}
|
||||
$fields[$field] = $data[$field];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,22 +25,18 @@ use Friendica\BaseFactory;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Notification as ModelNotification;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
||||
class Notification extends BaseFactory
|
||||
{
|
||||
public function createFromNotifyId(int $id)
|
||||
public function createFromNotificationId(int $id)
|
||||
{
|
||||
$notification = DBA::selectFirst('notify', [], ['id' => $id]);
|
||||
$notification = DBA::selectFirst('notification', [], ['id' => $id]);
|
||||
if (!DBA::isResult($notification)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cid = Contact::getIdForURL($notification['url'], 0, false);
|
||||
if (empty($cid)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
follow = Someone followed you
|
||||
follow_request = Someone requested to follow you
|
||||
|
@ -51,32 +47,30 @@ class Notification extends BaseFactory
|
|||
status = Someone you enabled notifications for has posted a status
|
||||
*/
|
||||
|
||||
switch ($notification['type']) {
|
||||
case ModelNotification\Type::INTRO:
|
||||
$type = 'follow_request';
|
||||
break;
|
||||
|
||||
case ModelNotification\Type::WALL:
|
||||
case ModelNotification\Type::COMMENT:
|
||||
case ModelNotification\Type::MAIL:
|
||||
case ModelNotification\Type::TAG_SELF:
|
||||
case ModelNotification\Type::POKE:
|
||||
$type = 'mention';
|
||||
break;
|
||||
|
||||
case ModelNotification\Type::SHARE:
|
||||
$type = 'status';
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
if (($notification['vid'] == Verb::getID(Activity::FOLLOW)) && ($notification['type'] == Post\UserNotification::NOTIF_NONE)) {
|
||||
$contact = Contact::getById($notification['actor-id'], ['pending']);
|
||||
$type = $contact['pending'] ? $type = 'follow_request' : 'follow';
|
||||
} elseif (($notification['vid'] == Verb::getID(Activity::ANNOUNCE)) &&
|
||||
in_array($notification['type'], [Post\UserNotification::NOTIF_DIRECT_COMMENT, Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT])) {
|
||||
$type = 'reblog';
|
||||
} elseif (in_array($notification['vid'], [Verb::getID(Activity::LIKE), Verb::getID(Activity::DISLIKE)]) &&
|
||||
in_array($notification['type'], [Post\UserNotification::NOTIF_DIRECT_COMMENT, Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT])) {
|
||||
$type = 'favourite';
|
||||
} elseif ($notification['type'] == Post\UserNotification::NOTIF_SHARED) {
|
||||
$type = 'status';
|
||||
} elseif (in_array($notification['type'], [Post\UserNotification::NOTIF_EXPLICIT_TAGGED,
|
||||
Post\UserNotification::NOTIF_IMPLICIT_TAGGED, Post\UserNotification::NOTIF_DIRECT_COMMENT,
|
||||
Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT, Post\UserNotification::NOTIF_THREAD_COMMENT])) {
|
||||
$type = 'mention';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
$account = DI::mstdnAccount()->createFromContactId($cid);
|
||||
$account = DI::mstdnAccount()->createFromContactId($notification['actor-id'], $notification['uid']);
|
||||
|
||||
if (!empty($notification['uri-id'])) {
|
||||
if (!empty($notification['target-uri-id'])) {
|
||||
try {
|
||||
$status = DI::mstdnStatus()->createFromUriId($notification['uri-id'], $notification['uid']);
|
||||
$status = DI::mstdnStatus()->createFromUriId($notification['target-uri-id'], $notification['uid']);
|
||||
} catch (\Throwable $th) {
|
||||
$status = null;
|
||||
}
|
||||
|
@ -84,6 +78,6 @@ class Notification extends BaseFactory
|
|||
$status = null;
|
||||
}
|
||||
|
||||
return new \Friendica\Object\Api\Mastodon\Notification($id, $type, $notification['date'], $account, $status);
|
||||
return new \Friendica\Object\Api\Mastodon\Notification($id, $type, $notification['created'], $account, $status);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ class Status extends BaseFactory
|
|||
Post::exists(['thr-parent-id' => $uriId, 'uid' => $uid, 'origin' => true, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::LIKE), 'deleted' => false]),
|
||||
Post::exists(['thr-parent-id' => $uriId, 'uid' => $uid, 'origin' => true, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::ANNOUNCE), 'deleted' => false]),
|
||||
Post\ThreadUser::getIgnored($uriId, $uid),
|
||||
(bool)$item['starred'],
|
||||
(bool)($item['starred'] && ($item['gravity'] == GRAVITY_PARENT)),
|
||||
Post\ThreadUser::getPinned($uriId, $uid)
|
||||
);
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ use Friendica\Core\Cache\Duration;
|
|||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Database\DBStructure;
|
||||
use Friendica\DI;
|
||||
use Friendica\Network\Probe;
|
||||
use Friendica\Protocol\ActivityNamespace;
|
||||
|
@ -349,6 +350,9 @@ class APContact
|
|||
DBA::delete('apcontact', ['url' => $url]);
|
||||
}
|
||||
|
||||
// Limit the length on incoming fields
|
||||
$apcontact = DBStructure::getFieldsForTable('apcontact', $apcontact);
|
||||
|
||||
if (DBA::exists('apcontact', ['url' => $apcontact['url']])) {
|
||||
DBA::update('apcontact', $apcontact, ['url' => $apcontact['url']]);
|
||||
} else {
|
||||
|
@ -357,7 +361,7 @@ class APContact
|
|||
|
||||
Logger::info('Updated profile', ['url' => $url]);
|
||||
|
||||
return $apcontact;
|
||||
return DBA::selectFirst('apcontact', [], ['url' => $apcontact['url']]) ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -271,7 +271,7 @@ class Contact
|
|||
|
||||
// Update the contact in the background if needed
|
||||
$updated = max($contact['success_update'], $contact['created'], $contact['updated'], $contact['last-update'], $contact['failure_update']);
|
||||
if (($updated < DateTimeFormat::utc('now -7 days')) && in_array($contact['network'], Protocol::FEDERATED)) {
|
||||
if (($updated < DateTimeFormat::utc('now -7 days')) && in_array($contact['network'], Protocol::FEDERATED) && !self::isLocalById($contact['id'])) {
|
||||
Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id']);
|
||||
}
|
||||
|
||||
|
@ -566,18 +566,13 @@ class Contact
|
|||
*/
|
||||
public static function createSelfFromUserId($uid)
|
||||
{
|
||||
// Only create the entry if it doesn't exist yet
|
||||
if (DBA::exists('contact', ['uid' => $uid, 'self' => true])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'pubkey', 'prvkey'],
|
||||
['uid' => $uid, 'account_expired' => false]);
|
||||
if (!DBA::isResult($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$return = DBA::insert('contact', [
|
||||
$contact = [
|
||||
'uid' => $user['uid'],
|
||||
'created' => DateTimeFormat::utcNow(),
|
||||
'self' => 1,
|
||||
|
@ -602,7 +597,23 @@ class Contact
|
|||
'uri-date' => DateTimeFormat::utcNow(),
|
||||
'avatar-date' => DateTimeFormat::utcNow(),
|
||||
'closeness' => 0
|
||||
]);
|
||||
];
|
||||
|
||||
$return = true;
|
||||
|
||||
// Only create the entry if it doesn't exist yet
|
||||
if (!DBA::exists('contact', ['uid' => $uid, 'self' => true])) {
|
||||
$return = DBA::insert('contact', $contact);
|
||||
}
|
||||
|
||||
// Create the public contact
|
||||
if (!DBA::exists('contact', ['nurl' => $contact['nurl'], 'uid' => 0])) {
|
||||
$contact['self'] = false;
|
||||
$contact['uid'] = 0;
|
||||
$contact['prvkey'] = null;
|
||||
|
||||
DBA::insert('contact', $contact, Database::INSERT_IGNORE);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
@ -704,6 +715,8 @@ class Contact
|
|||
DBA::update('contact', $fields, ['id' => $self['id']]);
|
||||
|
||||
// Update the public contact as well
|
||||
$fields['prvkey'] = null;
|
||||
$fields['self'] = false;
|
||||
DBA::update('contact', $fields, ['uid' => 0, 'nurl' => $self['nurl']]);
|
||||
|
||||
// Update the profile
|
||||
|
@ -1941,7 +1954,7 @@ class Contact
|
|||
return false;
|
||||
}
|
||||
|
||||
if (Contact::isLocal($ret['url'])) {
|
||||
if (self::isLocal($ret['url'])) {
|
||||
Logger::info('Local contacts are not updated here.');
|
||||
return true;
|
||||
}
|
||||
|
@ -2523,6 +2536,8 @@ class Contact
|
|||
// Ensure to always have the correct network type, independent from the connection request method
|
||||
self::updateFromProbe($contact['id']);
|
||||
|
||||
Post\UserNotification::insertNotication($contact['id'], Verb::getID(Activity::FOLLOW), $importer['uid']);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// send email notification to owner?
|
||||
|
@ -2554,6 +2569,8 @@ class Contact
|
|||
|
||||
self::updateAvatar($contact_id, $photo, true);
|
||||
|
||||
Post\UserNotification::insertNotication($contact_id, Verb::getID(Activity::FOLLOW), $importer['uid']);
|
||||
|
||||
$contact_record = DBA::selectFirst('contact', ['id', 'network', 'name', 'url', 'photo'], ['id' => $contact_id]);
|
||||
|
||||
/// @TODO Encapsulate this into a function/method
|
||||
|
|
|
@ -78,19 +78,22 @@ class Relation
|
|||
{
|
||||
$contact = Contact::getByURL($url);
|
||||
if (empty($contact)) {
|
||||
Logger::info('Contact not found', ['url' => $url]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self::isDiscoverable($url, $contact)) {
|
||||
Logger::info('Contact is not discoverable', ['url' => $url]);
|
||||
return;
|
||||
}
|
||||
|
||||
$uid = User::getIdForURL($url);
|
||||
if (!empty($uid)) {
|
||||
// Fetch the followers/followings locally
|
||||
Logger::info('Fetch the followers/followings locally', ['url' => $url]);
|
||||
$followers = self::getContacts($uid, [Contact::FOLLOWER, Contact::FRIEND]);
|
||||
$followings = self::getContacts($uid, [Contact::SHARING, Contact::FRIEND]);
|
||||
} else {
|
||||
} elseif (!Contact::isLocal($url)) {
|
||||
Logger::info('Fetch the followers/followings by polling the endpoints', ['url' => $url]);
|
||||
$apcontact = APContact::getByURL($url, false);
|
||||
|
||||
if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) {
|
||||
|
@ -104,6 +107,10 @@ class Relation
|
|||
} else {
|
||||
$followings = [];
|
||||
}
|
||||
} else {
|
||||
Logger::notice('Contact seems to be local but could not be found here', ['url' => $url]);
|
||||
$followers = [];
|
||||
$followings = [];
|
||||
}
|
||||
|
||||
if (empty($followers) && empty($followings)) {
|
||||
|
|
|
@ -802,6 +802,7 @@ class GServer
|
|||
/**
|
||||
* Parses Nodeinfo 2
|
||||
*
|
||||
* @see https://git.feneas.org/jaywink/nodeinfo2
|
||||
* @param string $nodeinfo_url address of the nodeinfo path
|
||||
* @return array Server data
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
|
@ -850,7 +851,9 @@ class GServer
|
|||
if (!empty($nodeinfo['protocols'])) {
|
||||
$protocols = [];
|
||||
foreach ($nodeinfo['protocols'] as $protocol) {
|
||||
$protocols[$protocol] = true;
|
||||
if (is_string($protocol)) {
|
||||
$protocols[$protocol] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($protocols['dfrn'])) {
|
||||
|
|
|
@ -180,16 +180,18 @@ class Item
|
|||
if (!empty($fields['body'])) {
|
||||
Post\Media::insertFromAttachmentData($item['uri-id'], $fields['body']);
|
||||
|
||||
if ($item['author-network'] != Protocol::DFRN) {
|
||||
Post\Media::insertFromRelevantUrl($item['uri-id'], $fields['body']);
|
||||
}
|
||||
|
||||
$content_fields = ['raw-body' => trim($fields['raw-body'] ?? $fields['body'])];
|
||||
|
||||
// Remove all media attachments from the body and store them in the post-media table
|
||||
// @todo On shared postings (Diaspora style and commented reshare) don't fetch content from the shared part
|
||||
$content_fields['raw-body'] = Post\Media::insertFromBody($item['uri-id'], $content_fields['raw-body']);
|
||||
$content_fields['raw-body'] = self::setHashtags($content_fields['raw-body']);
|
||||
|
||||
if ($item['author-network'] != Protocol::DFRN) {
|
||||
Post\Media::insertFromRelevantUrl($item['uri-id'], $content_fields['raw-body']);
|
||||
}
|
||||
|
||||
Post\Content::update($item['uri-id'], $content_fields);
|
||||
}
|
||||
|
||||
if (!empty($fields['file'])) {
|
||||
|
@ -991,14 +993,14 @@ class Item
|
|||
|
||||
Post\Media::insertFromAttachmentData($item['uri-id'], $item['body']);
|
||||
|
||||
if (!DBA::exists('contact', ['id' => $item['author-id'], 'network' => Protocol::DFRN])) {
|
||||
Post\Media::insertFromRelevantUrl($item['uri-id'], $item['body']);
|
||||
}
|
||||
|
||||
// Remove all media attachments from the body and store them in the post-media table
|
||||
$item['raw-body'] = Post\Media::insertFromBody($item['uri-id'], $item['raw-body']);
|
||||
$item['raw-body'] = self::setHashtags($item['raw-body']);
|
||||
|
||||
if (!DBA::exists('contact', ['id' => $item['author-id'], 'network' => Protocol::DFRN])) {
|
||||
Post\Media::insertFromRelevantUrl($item['uri-id'], $item['raw-body']);
|
||||
}
|
||||
|
||||
// Check for hashtags in the body and repair or add hashtag links
|
||||
$item['body'] = self::setHashtags($item['body']);
|
||||
|
||||
|
@ -1018,6 +1020,30 @@ class Item
|
|||
|
||||
if (empty($item['event-id'])) {
|
||||
unset($item['event-id']);
|
||||
|
||||
$ev = Event::fromBBCode($item['body']);
|
||||
if ((!empty($ev['desc']) || !empty($ev['summary'])) && !empty($ev['start'])) {
|
||||
Logger::info('Event found.');
|
||||
$ev['cid'] = $item['contact-id'];
|
||||
$ev['uid'] = $item['uid'];
|
||||
$ev['uri'] = $item['uri'];
|
||||
$ev['edited'] = $item['edited'];
|
||||
$ev['private'] = $item['private'];
|
||||
$ev['guid'] = $item['guid'];
|
||||
$ev['plink'] = $item['plink'];
|
||||
$ev['network'] = $item['network'];
|
||||
$ev['protocol'] = $item['protocol'];
|
||||
$ev['direction'] = $item['direction'];
|
||||
$ev['source'] = $item['source'];
|
||||
|
||||
$event = DBA::selectFirst('event', ['id'], ['uri' => $item['uri'], 'uid' => $item['uid']]);
|
||||
if (DBA::isResult($event)) {
|
||||
$ev['id'] = $event['id'];
|
||||
}
|
||||
|
||||
$item['event-id'] = Event::store($ev);
|
||||
Logger::info('Event was stored', ['id' => $item['event-id']]);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($item['causer-id'])) {
|
||||
|
@ -1034,7 +1060,14 @@ class Item
|
|||
Post\Content::insert($item['uri-id'], $item);
|
||||
}
|
||||
|
||||
// Diaspora signature
|
||||
// Create Diaspora signature
|
||||
if ($item['origin'] && empty($item['diaspora_signed_text'])) {
|
||||
$signed = Diaspora::createCommentSignature($uid, $item);
|
||||
if (!empty($signed)) {
|
||||
$item['diaspora_signed_text'] = json_encode($signed);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($item['diaspora_signed_text'])) {
|
||||
DBA::replace('diaspora-interaction', ['uri-id' => $item['uri-id'], 'interaction' => $item['diaspora_signed_text']]);
|
||||
}
|
||||
|
@ -1322,19 +1355,26 @@ class Item
|
|||
/**
|
||||
* Store a public item defined by their URI-ID for the given users
|
||||
*
|
||||
* @param integer $uri_id URI-ID of the given item
|
||||
* @param integer $uid The user that will receive the item entry
|
||||
* @param array $fields Additional fields to be stored
|
||||
* @param integer $uri_id URI-ID of the given item
|
||||
* @param integer $uid The user that will receive the item entry
|
||||
* @param array $fields Additional fields to be stored
|
||||
* @param integer $source_uid User id of the source post
|
||||
* @return integer stored item id
|
||||
*/
|
||||
public static function storeForUserByUriId(int $uri_id, int $uid, array $fields = [])
|
||||
public static function storeForUserByUriId(int $uri_id, int $uid, array $fields = [], int $source_uid = 0)
|
||||
{
|
||||
$item = Post::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $uri_id, 'uid' => 0]);
|
||||
if (!DBA::isResult($item)) {
|
||||
if ($uid == $source_uid) {
|
||||
Logger::warning('target UID must not be be equal to the source UID', ['uri-id' => $uri_id, 'uid' => $uid]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (($item['private'] == self::PRIVATE) || !in_array($item['network'], Protocol::FEDERATED)) {
|
||||
$item = Post::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $uri_id, 'uid' => $source_uid]);
|
||||
if (!DBA::isResult($item)) {
|
||||
Logger::warning('Item could not be fetched', ['uri-id' => $uri_id, 'uid' => $source_uid]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (($source_uid == 0) && (($item['private'] == self::PRIVATE) || !in_array($item['network'], Protocol::FEDERATED))) {
|
||||
Logger::notice('Item is private or not from a federated network. It will not be stored for the user.', ['uri-id' => $uri_id, 'uid' => $uid, 'private' => $item['private'], 'network' => $item['network']]);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1343,8 +1383,25 @@ class Item
|
|||
|
||||
$item = array_merge($item, $fields);
|
||||
|
||||
$is_reshare = ($item['gravity'] == GRAVITY_ACTIVITY) && ($item['verb'] == Activity::ANNOUNCE);
|
||||
|
||||
if ((($item['gravity'] == GRAVITY_PARENT) || $is_reshare) &&
|
||||
DI::pConfig()->get($uid, 'system', 'accept_only_sharer') &&
|
||||
!Contact::isSharingByURL($item['author-link'], $uid) &&
|
||||
!Contact::isSharingByURL($item['owner-link'], $uid)) {
|
||||
Logger::info('Contact is not a follower, thread will not be stored', ['author' => $item['author-link'], 'uid' => $uid]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((($item['gravity'] == GRAVITY_COMMENT) || $is_reshare) && !Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $uid])) {
|
||||
// Only do an auto complete with the source uid "0" to prevent privavy problems
|
||||
$causer = $item['causer-id'] ?: $item['author-id'];
|
||||
$result = self::storeForUserByUriId($item['thr-parent-id'], $uid, ['causer-id' => $causer, 'post-reason' => self::PR_FETCHED]);
|
||||
Logger::info('Fetched thread parent', ['uri-id' => $item['thr-parent-id'], 'uid' => $uid, 'causer' => $causer, 'result' => $result]);
|
||||
}
|
||||
|
||||
$stored = self::storeForUser($item, $uid);
|
||||
Logger::info('Public item stored for user', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'stored' => $stored]);
|
||||
Logger::info('Item stored for user', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'source-uid' => $source_uid, 'stored' => $stored]);
|
||||
return $stored;
|
||||
}
|
||||
|
||||
|
@ -1364,11 +1421,18 @@ class Item
|
|||
}
|
||||
|
||||
unset($item['id']);
|
||||
unset($item['parent']);
|
||||
unset($item['mention']);
|
||||
unset($item['starred']);
|
||||
unset($item['unseen']);
|
||||
unset($item['psid']);
|
||||
unset($item['pinned']);
|
||||
unset($item['ignored']);
|
||||
unset($item['pubmail']);
|
||||
unset($item['forum_mode']);
|
||||
|
||||
unset($item['event-id']);
|
||||
unset($item['hidden']);
|
||||
unset($item['notification-type']);
|
||||
|
||||
$item['uid'] = $uid;
|
||||
$item['origin'] = 0;
|
||||
|
@ -1394,8 +1458,6 @@ class Item
|
|||
$item['contact-id'] = $self['id'];
|
||||
}
|
||||
|
||||
/// @todo Handling of "event-id"
|
||||
|
||||
$notify = false;
|
||||
if ($item['gravity'] == GRAVITY_PARENT) {
|
||||
$contact = DBA::selectFirst('contact', [], ['id' => $item['contact-id'], 'self' => false]);
|
||||
|
@ -1407,9 +1469,9 @@ class Item
|
|||
$distributed = self::insert($item, $notify, true);
|
||||
|
||||
if (!$distributed) {
|
||||
Logger::info("Distributed public item wasn't stored", ['uri-id' => $item['uri-id'], 'user' => $uid]);
|
||||
Logger::info("Distributed item wasn't stored", ['uri-id' => $item['uri-id'], 'user' => $uid]);
|
||||
} else {
|
||||
Logger::info('Distributed public item was stored', ['uri-id' => $item['uri-id'], 'user' => $uid, 'stored' => $distributed]);
|
||||
Logger::info('Distributed item was stored', ['uri-id' => $item['uri-id'], 'user' => $uid, 'stored' => $distributed]);
|
||||
}
|
||||
return $distributed;
|
||||
}
|
||||
|
@ -2739,9 +2801,10 @@ class Item
|
|||
*
|
||||
* @param string $body
|
||||
* @param string $url
|
||||
* @param int $type
|
||||
* @return bool
|
||||
*/
|
||||
public static function containsLink(string $body, string $url)
|
||||
public static function containsLink(string $body, string $url, int $type = 0)
|
||||
{
|
||||
// Make sure that for example site parameters aren't used when testing if the link is contained in the body
|
||||
$urlparts = parse_url($url);
|
||||
|
@ -2749,6 +2812,12 @@ class Item
|
|||
unset($urlparts['fragment']);
|
||||
$url = Network::unparseURL($urlparts);
|
||||
|
||||
// Remove media links to only search in embedded content
|
||||
// @todo Check images for image link, audio for audio links, ...
|
||||
if (in_array($type, [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE])) {
|
||||
$body = preg_replace("/\[url=[^\[\]]*\](.*)\[\/url\]/Usi", ' $1 ', $body);
|
||||
}
|
||||
|
||||
if (strpos($body, $url)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -2777,7 +2846,7 @@ class Item
|
|||
|
||||
// @todo In the future we should make a single for the template engine with all media in it. This allows more flexibilty.
|
||||
foreach ($attachments['visual'] as $attachment) {
|
||||
if (self::containsLink($item['body'], $attachment['url'])) {
|
||||
if (self::containsLink($item['body'], $attachment['url'], $attachment['type'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2802,7 +2871,7 @@ class Item
|
|||
'mime' => $attachment['mimetype'],
|
||||
],
|
||||
]);
|
||||
if ($item['post-type'] == Item::PT_VIDEO) {
|
||||
if (($item['post-type'] ?? null) == Item::PT_VIDEO) {
|
||||
$leading .= $media;
|
||||
} else {
|
||||
$trailing .= $media;
|
||||
|
@ -2955,7 +3024,7 @@ class Item
|
|||
|
||||
// @todo Use a template
|
||||
$rendered = BBCode::convertAttachment('', BBCode::INTERNAL, false, $data);
|
||||
} elseif (!self::containsLink($content, $data['url'])) {
|
||||
} elseif (!self::containsLink($content, $data['url'], Post\Media::HTML)) {
|
||||
$rendered = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/link.tpl'), [
|
||||
'$url' => $data['url'],
|
||||
'$title' => $data['title'],
|
||||
|
@ -3288,18 +3357,18 @@ class Item
|
|||
{
|
||||
$shared = BBCode::fetchShareAttributes($item['body']);
|
||||
if (empty($shared['link'])) {
|
||||
return $item['body'];
|
||||
return $item['body'];
|
||||
}
|
||||
|
||||
|
||||
$id = self::fetchByLink($shared['link']);
|
||||
Logger::info('Fetched shared post', ['uri-id' => $item['uri-id'], 'id' => $id, 'author' => $shared['profile'], 'url' => $shared['link'], 'guid' => $shared['guid'], 'callstack' => System::callstack()]);
|
||||
if (!$id) {
|
||||
return $item['body'];
|
||||
return $item['body'];
|
||||
}
|
||||
|
||||
$shared_item = Post::selectFirst(['author-name', 'author-link', 'author-avatar', 'plink', 'created', 'guid', 'title', 'body'], ['id' => $id]);
|
||||
if (!DBA::isResult($shared_item)) {
|
||||
return $item['body'];
|
||||
return $item['body'];
|
||||
}
|
||||
|
||||
$shared_content = BBCode::getShareOpeningTag($shared_item['author-name'], $shared_item['author-link'], $shared_item['author-avatar'], $shared_item['plink'], $shared_item['created'], $shared_item['guid']);
|
||||
|
|
|
@ -36,15 +36,14 @@ use Friendica\Worker\Delivery;
|
|||
class Mail
|
||||
{
|
||||
/**
|
||||
* Insert received private message
|
||||
* Insert private message
|
||||
*
|
||||
* @param array $msg
|
||||
* @param bool $notifiction
|
||||
* @return int|boolean Message ID or false on error
|
||||
*/
|
||||
public static function insert($msg)
|
||||
public static function insert($msg, $notifiction = true)
|
||||
{
|
||||
$user = User::getById($msg['uid']);
|
||||
|
||||
if (!isset($msg['reply'])) {
|
||||
$msg['reply'] = DBA::exists('mail', ['parent-uri' => $msg['parent-uri']]);
|
||||
}
|
||||
|
@ -63,6 +62,10 @@ class Mail
|
|||
|
||||
$msg['created'] = (!empty($msg['created']) ? DateTimeFormat::utc($msg['created']) : DateTimeFormat::utcNow());
|
||||
|
||||
$msg['author-id'] = Contact::getIdForURL($msg['from-url'], 0, false);
|
||||
$msg['uri-id'] = ItemURI::insert(['uri' => $msg['uri'], 'guid' => $msg['guid']]);
|
||||
$msg['parent-uri-id'] = ItemURI::getIdByURI($msg['parent-uri']);
|
||||
|
||||
DBA::lock('mail');
|
||||
|
||||
if (DBA::exists('mail', ['uri' => $msg['uri'], 'uid' => $msg['uid']])) {
|
||||
|
@ -71,12 +74,8 @@ class Mail
|
|||
return false;
|
||||
}
|
||||
|
||||
$msg['author-id'] = Contact::getIdForURL($msg['from-url'], 0, false);
|
||||
$msg['uri-id'] = ItemURI::insert(['uri' => $msg['uri'], 'guid' => $msg['guid']]);
|
||||
$msg['parent-uri-id'] = ItemURI::getIdByURI($msg['parent-uri']);
|
||||
|
||||
if ($msg['reply']) {
|
||||
$reply = DBA::selectFirst('mail', ['uri', 'uri-id'], ['parent-uri' => $mail['parent-uri'], 'reply' => false]);
|
||||
$reply = DBA::selectFirst('mail', ['uri', 'uri-id'], ['parent-uri' => $msg['parent-uri'], 'reply' => false]);
|
||||
|
||||
$msg['thr-parent'] = $reply['uri'];
|
||||
$msg['thr-parent-id'] = $reply['uri-id'];
|
||||
|
@ -95,19 +94,22 @@ class Mail
|
|||
DBA::update('conv', ['updated' => DateTimeFormat::utcNow()], ['id' => $msg['convid']]);
|
||||
}
|
||||
|
||||
// send notifications.
|
||||
$notif_params = [
|
||||
'type' => Notification\Type::MAIL,
|
||||
'otype' => Notification\ObjectType::MAIL,
|
||||
'verb' => Activity::POST,
|
||||
'uid' => $user['uid'],
|
||||
'cid' => $msg['contact-id'],
|
||||
'link' => DI::baseUrl() . '/message/' . $msg['id'],
|
||||
];
|
||||
if ($notifiction) {
|
||||
$user = User::getById($msg['uid']);
|
||||
// send notifications.
|
||||
$notif_params = [
|
||||
'type' => Notification\Type::MAIL,
|
||||
'otype' => Notification\ObjectType::MAIL,
|
||||
'verb' => Activity::POST,
|
||||
'uid' => $user['uid'],
|
||||
'cid' => $msg['contact-id'],
|
||||
'link' => DI::baseUrl() . '/message/' . $msg['id'],
|
||||
];
|
||||
|
||||
notification($notif_params);
|
||||
notification($notif_params);
|
||||
|
||||
Logger::info('Mail is processed, notification was sent.', ['id' => $msg['id'], 'uri' => $msg['uri']]);
|
||||
Logger::info('Mail is processed, notification was sent.', ['id' => $msg['id'], 'uri' => $msg['uri']]);
|
||||
}
|
||||
|
||||
return $msg['id'];
|
||||
}
|
||||
|
@ -195,9 +197,7 @@ class Mail
|
|||
$replyto = $convuri;
|
||||
}
|
||||
|
||||
$post_id = null;
|
||||
$success = DBA::insert(
|
||||
'mail',
|
||||
$post_id = self::insert(
|
||||
[
|
||||
'uid' => local_user(),
|
||||
'guid' => $guid,
|
||||
|
@ -214,13 +214,9 @@ class Mail
|
|||
'uri' => $uri,
|
||||
'parent-uri' => $replyto,
|
||||
'created' => DateTimeFormat::utcNow()
|
||||
]
|
||||
], false
|
||||
);
|
||||
|
||||
if ($success) {
|
||||
$post_id = DBA::lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* When a photo was uploaded into the message using the (profile wall) ajax
|
||||
|
@ -301,8 +297,7 @@ class Mail
|
|||
return -4;
|
||||
}
|
||||
|
||||
DBA::insert(
|
||||
'mail',
|
||||
self::insert(
|
||||
[
|
||||
'uid' => $recipient['uid'],
|
||||
'guid' => $guid,
|
||||
|
@ -320,7 +315,7 @@ class Mail
|
|||
'parent-uri' => $me['url'],
|
||||
'created' => DateTimeFormat::utcNow(),
|
||||
'unknown' => 1
|
||||
]
|
||||
], false
|
||||
);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -164,15 +164,16 @@ class Post
|
|||
* @param array $fields
|
||||
* @param array $condition
|
||||
* @param array $params
|
||||
* @param bool $user_mode true = post-user-view, false = post-view
|
||||
* @return bool|array
|
||||
* @throws \Exception
|
||||
* @see DBA::select
|
||||
*/
|
||||
public static function selectFirst(array $fields = [], array $condition = [], $params = [])
|
||||
public static function selectFirst(array $fields = [], array $condition = [], $params = [], bool $user_mode = true)
|
||||
{
|
||||
$params['limit'] = 1;
|
||||
|
||||
$result = self::select($fields, $condition, $params);
|
||||
$result = self::select($fields, $condition, $params, $user_mode);
|
||||
|
||||
if (is_bool($result)) {
|
||||
return $result;
|
||||
|
|
|
@ -351,7 +351,7 @@ class Media
|
|||
|
||||
foreach ($attachments as $attachment) {
|
||||
// Only store attachments that are part of the unshared body
|
||||
if (strpos($unshared_body, $attachment['url']) !== false) {
|
||||
if (Item::containsLink($unshared_body, $attachment['url'], $attachment['type'])) {
|
||||
self::insert($attachment);
|
||||
}
|
||||
}
|
||||
|
@ -600,7 +600,7 @@ class Media
|
|||
$body = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", '', $body);
|
||||
|
||||
foreach (self::getByURIId($uriid, [self::IMAGE, self::AUDIO, self::VIDEO]) as $media) {
|
||||
if (Item::containsLink($body, $media['url'])) {
|
||||
if (Item::containsLink($body, $media['url'], $media['type'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ use Friendica\Model\Post;
|
|||
use Friendica\Util\Strings;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
|
||||
class UserNotification
|
||||
{
|
||||
|
@ -128,8 +128,8 @@ class UserNotification
|
|||
*/
|
||||
public static function setNotification(int $uri_id, int $uid)
|
||||
{
|
||||
$fields = ['id', 'uri-id', 'parent-uri-id', 'uid', 'body', 'parent', 'gravity',
|
||||
'private', 'contact-id', 'thr-parent', 'parent-uri-id', 'parent-uri', 'author-id', 'verb'];
|
||||
$fields = ['id', 'uri-id', 'parent-uri-id', 'uid', 'body', 'parent', 'gravity', 'vid', 'gravity',
|
||||
'private', 'contact-id', 'thr-parent', 'thr-parent-id', 'parent-uri-id', 'parent-uri', 'author-id', 'verb'];
|
||||
$item = Post::selectFirst($fields, ['uri-id' => $uri_id, 'uid' => $uid, 'origin' => false]);
|
||||
if (!DBA::isResult($item)) {
|
||||
return;
|
||||
|
@ -148,7 +148,7 @@ class UserNotification
|
|||
}
|
||||
|
||||
// Add every user who participated so far in this thread
|
||||
// This can only happen with participations on global items. (means: uid = 0)
|
||||
// This can only happen with participations on global items. (means: uid = 0)
|
||||
$users = DBA::p("SELECT DISTINCT(`contact-uid`) AS `uid` FROM `post-user-view`
|
||||
WHERE `contact-uid` != 0 AND `parent-uri-id` = ? AND `uid` = ?", $item['parent-uri-id'], $uid);
|
||||
while ($user = DBA::fetch($users)) {
|
||||
|
@ -177,6 +177,10 @@ class UserNotification
|
|||
|
||||
if (self::checkShared($item, $uid)) {
|
||||
$notification_type = $notification_type | self::NOTIF_SHARED;
|
||||
self::insertNoticationByItem(self::NOTIF_SHARED, $uid, $item);
|
||||
$notified = true;
|
||||
} else {
|
||||
$notified = false;
|
||||
}
|
||||
|
||||
$profiles = self::getProfileForUser($uid);
|
||||
|
@ -194,38 +198,64 @@ class UserNotification
|
|||
return;
|
||||
}
|
||||
|
||||
// Only create notifications for posts and comments, not for activities
|
||||
if (in_array($item['gravity'], [GRAVITY_PARENT, GRAVITY_COMMENT])) {
|
||||
if (self::checkImplicitMention($item, $profiles)) {
|
||||
$notification_type = $notification_type | self::NOTIF_IMPLICIT_TAGGED;
|
||||
}
|
||||
|
||||
if (self::checkExplicitMention($item, $profiles)) {
|
||||
$notification_type = $notification_type | self::NOTIF_EXPLICIT_TAGGED;
|
||||
}
|
||||
|
||||
if (self::checkCommentedThread($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_THREAD_COMMENT;
|
||||
}
|
||||
|
||||
if (self::checkDirectComment($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_DIRECT_COMMENT;
|
||||
}
|
||||
|
||||
if (self::checkDirectCommentedThread($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_DIRECT_THREAD_COMMENT;
|
||||
}
|
||||
|
||||
if (self::checkCommentedParticipation($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_COMMENT_PARTICIPATION;
|
||||
}
|
||||
|
||||
if (self::checkActivityParticipation($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_ACTIVITY_PARTICIPATION;
|
||||
if (self::checkExplicitMention($item, $profiles)) {
|
||||
$notification_type = $notification_type | self::NOTIF_EXPLICIT_TAGGED;
|
||||
if (!$notified) {
|
||||
self::insertNoticationByItem( self::NOTIF_EXPLICIT_TAGGED, $uid, $item);
|
||||
$notified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($notification_type)) {
|
||||
if (self::checkImplicitMention($item, $profiles)) {
|
||||
$notification_type = $notification_type | self::NOTIF_IMPLICIT_TAGGED;
|
||||
if (!$notified) {
|
||||
self::insertNoticationByItem(self::NOTIF_IMPLICIT_TAGGED, $uid, $item);
|
||||
$notified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (self::checkDirectComment($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_DIRECT_COMMENT;
|
||||
if (!$notified) {
|
||||
self::insertNoticationByItem(self::NOTIF_DIRECT_COMMENT, $uid, $item);
|
||||
$notified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (self::checkDirectCommentedThread($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_DIRECT_THREAD_COMMENT;
|
||||
if (!$notified) {
|
||||
self::insertNoticationByItem(self::NOTIF_DIRECT_THREAD_COMMENT, $uid, $item);
|
||||
$notified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (self::checkCommentedThread($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_THREAD_COMMENT;
|
||||
if (!$notified) {
|
||||
self::insertNoticationByItem(self::NOTIF_THREAD_COMMENT, $uid, $item);
|
||||
$notified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (self::checkCommentedParticipation($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_COMMENT_PARTICIPATION;
|
||||
if (!$notified) {
|
||||
self::insertNoticationByItem(self::NOTIF_COMMENT_PARTICIPATION, $uid, $item);
|
||||
$notified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (self::checkActivityParticipation($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::NOTIF_ACTIVITY_PARTICIPATION;
|
||||
if (!$notified) {
|
||||
self::insertNoticationByItem(self::NOTIF_ACTIVITY_PARTICIPATION, $uid, $item);
|
||||
$notified = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Only create notifications for posts and comments, not for activities
|
||||
if (empty($notification_type) || !in_array($item['gravity'], [GRAVITY_PARENT, GRAVITY_COMMENT])) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -236,6 +266,61 @@ class UserNotification
|
|||
self::update($item['uri-id'], $uid, $fields, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a notification entry for a given item array
|
||||
*
|
||||
* @param int $type User notification type
|
||||
* @param int $uid User ID
|
||||
* @param array $item Item array
|
||||
* @return boolean
|
||||
*/
|
||||
private static function insertNoticationByItem(int $type, int $uid, array $item)
|
||||
{
|
||||
if (($item['gravity'] == GRAVITY_ACTIVITY) &&
|
||||
!in_array($type, [self::NOTIF_DIRECT_COMMENT, self::NOTIF_DIRECT_THREAD_COMMENT])) {
|
||||
// Activities are only stored when performed on the user's post or comment
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = [
|
||||
'uid' => $uid,
|
||||
'vid' => $item['vid'],
|
||||
'type' => $type,
|
||||
'actor-id' => $item['author-id'],
|
||||
'parent-uri-id' => $item['parent-uri-id'],
|
||||
'created' => DateTimeFormat::utcNow(),
|
||||
];
|
||||
|
||||
if ($item['gravity'] == GRAVITY_ACTIVITY) {
|
||||
$fields['target-uri-id'] = $item['thr-parent-id'];
|
||||
} else {
|
||||
$fields['target-uri-id'] = $item['uri-id'];
|
||||
}
|
||||
|
||||
return DBA::insert('notification', $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a notification entry
|
||||
*
|
||||
* @param int $actor Contact ID of the actor
|
||||
* @param int $vid Verb ID
|
||||
* @param int $uid User ID
|
||||
* @return boolean
|
||||
*/
|
||||
public static function insertNotication(int $actor, int $vid, int $uid)
|
||||
{
|
||||
$fields = [
|
||||
'uid' => $uid,
|
||||
'vid' => $vid,
|
||||
'type' => self::NOTIF_NONE,
|
||||
'actor-id' => $actor,
|
||||
'created' => DateTimeFormat::utcNow(),
|
||||
];
|
||||
|
||||
return DBA::insert('notification', $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all profiles (contact URL) of a given user
|
||||
* @param int $uid User ID
|
||||
|
|
|
@ -145,7 +145,7 @@ class Profile
|
|||
*/
|
||||
public static function load(App $a, $nickname, array $profiledata = [], $show_connect = true)
|
||||
{
|
||||
$user = User::getByNickname($nickname);
|
||||
$user = DBA::selectFirst('user', ['uid'], ['nickname' => $nickname, 'account_removed' => false]);
|
||||
|
||||
if (!DBA::isResult($user) && empty($profiledata)) {
|
||||
Logger::log('profile error: ' . DI::args()->getQueryString(), Logger::DEBUG);
|
||||
|
@ -263,8 +263,20 @@ class Profile
|
|||
$o = '';
|
||||
$location = false;
|
||||
|
||||
// This function can also use contact information in $profile
|
||||
$is_contact = !empty($profile['cid']);
|
||||
// This function can also use contact information in $profile, but the 'cid'
|
||||
// value is going to be coming from 'owner-view', which means it's the wrong
|
||||
// contact ID for the user viewing this page. Use 'nurl' to look up the
|
||||
// correct contact table entry for the logged-in user.
|
||||
$profile_contact = [];
|
||||
|
||||
if (!empty($profile['nurl'] ?? '')) {
|
||||
if (local_user() && ($profile['uid'] ?? '') != local_user()) {
|
||||
$profile_contact = Contact::getById(Contact::getIdForURL($profile['nurl'], local_user()));
|
||||
}
|
||||
if (!empty($profile['cid']) && self::getMyURL()) {
|
||||
$profile_contact = Contact::selectFirst(['rel'], ['id' => $profile['cid']]);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($profile['nickname'])) {
|
||||
Logger::warning('Received profile with no nickname', ['profile' => $profile, 'callstack' => System::callstack(10)]);
|
||||
|
@ -292,16 +304,12 @@ class Profile
|
|||
$subscribe_feed_link = null;
|
||||
$wallmessage_link = null;
|
||||
|
||||
// Who is the logged-in user to this profile?
|
||||
$visitor_contact = [];
|
||||
if (!empty($profile['uid']) && self::getMyURL()) {
|
||||
$visitor_contact = Contact::selectFirst(['rel'], ['uid' => $profile['uid'], 'nurl' => Strings::normaliseLink(self::getMyURL())]);
|
||||
}
|
||||
|
||||
$profile_contact = [];
|
||||
if (!empty($profile['cid']) && self::getMyURL()) {
|
||||
$profile_contact = Contact::selectFirst(['rel'], ['id' => $profile['cid']]);
|
||||
}
|
||||
|
||||
$profile_is_dfrn = $profile['network'] == Protocol::DFRN;
|
||||
$profile_is_native = in_array($profile['network'], Protocol::NATIVE_SUPPORT);
|
||||
$local_user_is_self = self::getMyURL() && ($profile['url'] == self::getMyURL());
|
||||
|
@ -332,17 +340,19 @@ class Profile
|
|||
$subscribe_feed_link = 'dfrn_poll/' . $profile['nickname'];
|
||||
}
|
||||
|
||||
if (Contact::canReceivePrivateMessages($profile)) {
|
||||
if (Contact::canReceivePrivateMessages($profile_contact)) {
|
||||
if ($visitor_is_followed || $visitor_is_following) {
|
||||
$wallmessage_link = $visitor_base_path . '/message/new/' . base64_encode($profile['addr'] ?? '');
|
||||
$wallmessage_link = $visitor_base_path . '/message/new/' . $profile_contact['id'];
|
||||
} elseif ($visitor_is_authenticated && !empty($profile['unkmail'])) {
|
||||
$wallmessage_link = 'wallmessage/' . $profile['nickname'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// show edit profile to yourself
|
||||
if (!$is_contact && $local_user_is_self) {
|
||||
// show edit profile to yourself, but only if this is not meant to be
|
||||
// rendered as a "contact". i.e., if 'self' (a "contact" table column) isn't
|
||||
// set in $profile.
|
||||
if (!isset($profile['self']) && $local_user_is_self) {
|
||||
$profile['edit'] = [DI::baseUrl() . '/settings/profile', DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')];
|
||||
$profile['menu'] = [
|
||||
'chg_photo' => DI::l10n()->t('Change profile photo'),
|
||||
|
|
|
@ -312,8 +312,8 @@ class User
|
|||
*/
|
||||
public static function getIdForURL(string $url)
|
||||
{
|
||||
// Avoid any database requests when the hostname isn't even part of the url.
|
||||
if (!strpos($url, DI::baseUrl()->getHostname())) {
|
||||
// Avoid database queries when the local node hostname isn't even part of the url.
|
||||
if (!Contact::isLocal($url)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1123,6 +1123,8 @@ class User
|
|||
Photo::update(['profile' => 1], ['resource-id' => $resource_id]);
|
||||
}
|
||||
}
|
||||
|
||||
Contact::updateSelfFromUserID($uid, true);
|
||||
}
|
||||
|
||||
Hook::callAll('register_account', $uid);
|
||||
|
|
|
@ -21,8 +21,9 @@
|
|||
|
||||
namespace Friendica\Module\Api\Mastodon\Accounts;
|
||||
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\HTTPInputData;
|
||||
|
||||
/**
|
||||
* @see https://docs.joinmastodon.org/methods/accounts/
|
||||
|
@ -34,9 +35,10 @@ class UpdateCredentials extends BaseApi
|
|||
self::login(self::SCOPE_WRITE);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
||||
$data = Network::postdata();
|
||||
$data = HTTPInputData::process();
|
||||
|
||||
Logger::info('Patch data', ['data' => $data]);
|
||||
|
||||
// @todo Parse the raw data that is in the "multipart/form-data" format
|
||||
self::unsupported('patch');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class Apps extends BaseApi
|
|||
if (!empty($postdata)) {
|
||||
$postrequest = json_decode($postdata, true);
|
||||
if (!empty($postrequest) && is_array($postrequest)) {
|
||||
$request = array_merge($request, $$postrequest);
|
||||
$request = array_merge($request, $postrequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ class Bookmarks extends BaseApi
|
|||
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => $request['limit']];
|
||||
|
||||
$condition = ['pinned' => true, 'uid' => $uid];
|
||||
$condition = ['starred' => true, 'uid' => $uid];
|
||||
|
||||
if (!empty($request['max_id'])) {
|
||||
$condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $request['max_id']]);
|
||||
|
|
|
@ -78,13 +78,16 @@ class Lists extends BaseApi
|
|||
|
||||
public static function put(array $parameters = [])
|
||||
{
|
||||
$data = self::getPutData();
|
||||
$request = self::getRequest([
|
||||
'title' => '', // The title of the list to be updated.
|
||||
'replies_policy' => '', // One of: "followed", "list", or "none".
|
||||
]);
|
||||
|
||||
if (empty($data['title']) || empty($parameters['id'])) {
|
||||
if (empty($request['title']) || empty($parameters['id'])) {
|
||||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
Group::update($parameters['id'], $data['title']);
|
||||
Group::update($parameters['id'], $request['title']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -58,7 +58,12 @@ class Media extends BaseApi
|
|||
self::login(self::SCOPE_WRITE);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
||||
$data = self::getPutData();
|
||||
$request = self::getRequest([
|
||||
'file' => [], // The file to be attached, using multipart form data.
|
||||
'thumbnail' => [], // The custom thumbnail of the media to be attached, using multipart form data.
|
||||
'description' => '', // A plain-text description of the media, for accessibility purposes.
|
||||
'focus' => '', // Two floating points (x,y), comma-delimited ranging from -1.0 to 1.0
|
||||
]);
|
||||
|
||||
if (empty($parameters['id'])) {
|
||||
DI::mstdnError()->UnprocessableEntity();
|
||||
|
@ -69,7 +74,7 @@ class Media extends BaseApi
|
|||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
||||
Photo::update(['desc' => $data['description'] ?? ''], ['resource-id' => $photo['resource-id']]);
|
||||
Photo::update(['desc' => $request['description']], ['resource-id' => $photo['resource-id']]);
|
||||
|
||||
System::jsonExit(DI::mstdnAttachment()->createFromPhoto($parameters['id']));
|
||||
}
|
||||
|
|
|
@ -25,8 +25,10 @@ use Friendica\Core\System;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Notification;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
||||
/**
|
||||
* @see https://docs.joinmastodon.org/methods/notifications/
|
||||
|
@ -44,10 +46,10 @@ class Notifications extends BaseApi
|
|||
|
||||
if (!empty($parameters['id'])) {
|
||||
$id = $parameters['id'];
|
||||
if (!DBA::exists('notify', ['id' => $id, 'uid' => $uid])) {
|
||||
if (!DBA::exists('notification', ['id' => $id, 'uid' => $uid])) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
System::jsonExit(DI::mstdnNotification()->createFromNotifyId($id));
|
||||
System::jsonExit(DI::mstdnNotification()->createFromNotificationId($id));
|
||||
}
|
||||
|
||||
$request = self::getRequest([
|
||||
|
@ -63,7 +65,7 @@ class Notifications extends BaseApi
|
|||
|
||||
$params = ['order' => ['id' => true], 'limit' => $request['limit']];
|
||||
|
||||
$condition = ['uid' => $uid, 'seen' => false, 'type' => []];
|
||||
$condition = ['uid' => $uid, 'seen' => false];
|
||||
|
||||
if (!empty($request['account_id'])) {
|
||||
$contact = Contact::getById($request['account_id'], ['url']);
|
||||
|
@ -72,17 +74,40 @@ class Notifications extends BaseApi
|
|||
}
|
||||
}
|
||||
|
||||
if (!in_array('follow_request', $request['exclude_types'])) {
|
||||
$condition['type'] = array_merge($condition['type'], [Notification\Type::INTRO]);
|
||||
if (in_array('follow_request', $request['exclude_types'])) {
|
||||
$condition = DBA::mergeConditions($condition,
|
||||
["(`vid` != ? OR `type` != ? OR NOT EXISTS (SELECT `id` FROM `contact` WHERE `id` = `actor-id` AND `pending`))",
|
||||
Verb::getID(Activity::FOLLOW), Post\UserNotification::NOTIF_NONE]);
|
||||
}
|
||||
|
||||
if (!in_array('mention', $request['exclude_types'])) {
|
||||
$condition['type'] = array_merge($condition['type'],
|
||||
[Notification\Type::WALL, Notification\Type::COMMENT, Notification\Type::MAIL, Notification\Type::TAG_SELF, Notification\Type::POKE]);
|
||||
if (in_array('follow', $request['exclude_types'])) {
|
||||
$condition = DBA::mergeConditions($condition,
|
||||
["(`vid` != ? OR `type` != ? OR NOT EXISTS (SELECT `id` FROM `contact` WHERE `id` = `actor-id` AND NOT `pending`))",
|
||||
Verb::getID(Activity::FOLLOW), Post\UserNotification::NOTIF_NONE]);
|
||||
}
|
||||
|
||||
if (!in_array('status', $request['exclude_types'])) {
|
||||
$condition['type'] = array_merge($condition['type'], [Notification\Type::SHARE]);
|
||||
if (in_array('favourite', $request['exclude_types'])) {
|
||||
$condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?, ?) OR NOT `type` IN (?, ?))",
|
||||
Verb::getID(Activity::LIKE), Verb::getID(Activity::DISLIKE),
|
||||
Post\UserNotification::NOTIF_DIRECT_COMMENT, Post\UserNotification::NOTIF_THREAD_COMMENT]);
|
||||
}
|
||||
|
||||
if (in_array('reblog', $request['exclude_types'])) {
|
||||
$condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?) OR NOT `type` IN (?, ?))",
|
||||
Verb::getID(Activity::ANNOUNCE),
|
||||
Post\UserNotification::NOTIF_DIRECT_COMMENT, Post\UserNotification::NOTIF_THREAD_COMMENT]);
|
||||
}
|
||||
|
||||
if (in_array('mention', $request['exclude_types'])) {
|
||||
$condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?) OR NOT `type` IN (?, ?, ?, ?, ?))",
|
||||
Verb::getID(Activity::POST), Post\UserNotification::NOTIF_EXPLICIT_TAGGED,
|
||||
Post\UserNotification::NOTIF_IMPLICIT_TAGGED, Post\UserNotification::NOTIF_DIRECT_COMMENT,
|
||||
Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT, Post\UserNotification::NOTIF_THREAD_COMMENT]);
|
||||
}
|
||||
|
||||
if (in_array('status', $request['exclude_types'])) {
|
||||
$condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?) OR NOT `type` IN (?))",
|
||||
Verb::getID(Activity::POST), Post\UserNotification::NOTIF_SHARED]);
|
||||
}
|
||||
|
||||
if (!empty($request['max_id'])) {
|
||||
|
@ -101,9 +126,12 @@ class Notifications extends BaseApi
|
|||
|
||||
$notifications = [];
|
||||
|
||||
$notify = DBA::select('notify', ['id'], $condition, $params);
|
||||
$notify = DBA::select('notification', ['id'], $condition, $params);
|
||||
while ($notification = DBA::fetch($notify)) {
|
||||
$notifications[] = DI::mstdnNotification()->createFromNotifyId($notification['id']);
|
||||
$entry = DI::mstdnNotification()->createFromNotificationId($notification['id']);
|
||||
if (!empty($entry)) {
|
||||
$notifications[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($request['min_id'])) {
|
||||
|
|
|
@ -35,7 +35,7 @@ class Clear extends BaseApi
|
|||
self::login(self::SCOPE_WRITE);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
||||
DBA::update('notify', ['seen' => true], ['uid' => $uid]);
|
||||
DBA::update('notification', ['seen' => true], ['uid' => $uid]);
|
||||
|
||||
System::jsonExit([]);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class Dismiss extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
DBA::update('notify', ['seen' => true], ['uid' => $uid, 'id' => $parameters['id']]);
|
||||
DBA::update('notification', ['seen' => true], ['uid' => $uid, 'id' => $parameters['id']]);
|
||||
|
||||
System::jsonExit([]);
|
||||
}
|
||||
|
|
|
@ -46,21 +46,22 @@ class Statuses extends BaseApi
|
|||
self::login(self::SCOPE_WRITE);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
||||
$data = self::getJsonPostData();
|
||||
|
||||
$status = $data['status'] ?? '';
|
||||
$media_ids = $data['media_ids'] ?? [];
|
||||
$in_reply_to_id = $data['in_reply_to_id'] ?? 0;
|
||||
$sensitive = $data['sensitive'] ?? false; // @todo Possibly trigger "nsfw" flag?
|
||||
$spoiler_text = $data['spoiler_text'] ?? '';
|
||||
$visibility = $data['visibility'] ?? '';
|
||||
$scheduled_at = $data['scheduled_at'] ?? ''; // Currently unsupported, but maybe in the future
|
||||
$language = $data['language'] ?? '';
|
||||
$request = self::getRequest([
|
||||
'status' => '', // Text content of the status. If media_ids is provided, this becomes optional. Attaching a poll is optional while status is provided.
|
||||
'media_ids' => [], // Array of Attachment ids to be attached as media. If provided, status becomes optional, and poll cannot be used.
|
||||
'poll' => [], // Poll data. If provided, media_ids cannot be used, and poll[expires_in] must be provided.
|
||||
'in_reply_to_id' => 0, // ID of the status being replied to, if status is a reply
|
||||
'sensitive' => false, // Mark status and attached media as sensitive?
|
||||
'spoiler_text' => '', // Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field.
|
||||
'visibility' => '', // Visibility of the posted status. One of: "public", "unlisted", "private" or "direct".
|
||||
'scheduled_at' => '', // ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future.
|
||||
'language' => '', // ISO 639 language code for this status.
|
||||
]);
|
||||
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
|
||||
// The imput is defined as text. So we can use Markdown for some enhancements
|
||||
$body = Markdown::toBBCode($status);
|
||||
$body = Markdown::toBBCode($request['status']);
|
||||
|
||||
$body = BBCode::expandTags($body);
|
||||
|
||||
|
@ -69,7 +70,7 @@ class Statuses extends BaseApi
|
|||
$item['verb'] = Activity::POST;
|
||||
$item['contact-id'] = $owner['id'];
|
||||
$item['author-id'] = $item['owner-id'] = Contact::getPublicIdByUserId($uid);
|
||||
$item['title'] = $spoiler_text;
|
||||
$item['title'] = $request['spoiler_text'];
|
||||
$item['body'] = $body;
|
||||
|
||||
if (!empty(self::getCurrentApplication()['name'])) {
|
||||
|
@ -80,7 +81,7 @@ class Statuses extends BaseApi
|
|||
$item['app'] = 'API';
|
||||
}
|
||||
|
||||
switch ($visibility) {
|
||||
switch ($request['visibility']) {
|
||||
case 'public':
|
||||
$item['allow_cid'] = '';
|
||||
$item['allow_gid'] = '';
|
||||
|
@ -112,7 +113,7 @@ class Statuses extends BaseApi
|
|||
case 'direct':
|
||||
// Direct messages are currently unsupported
|
||||
DI::mstdnError()->InternalError('Direct messages are currently unsupported');
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
$item['allow_cid'] = $owner['allow_cid'];
|
||||
$item['allow_gid'] = $owner['allow_gid'];
|
||||
|
@ -129,12 +130,12 @@ class Statuses extends BaseApi
|
|||
break;
|
||||
}
|
||||
|
||||
if (!empty($language)) {
|
||||
$item['language'] = json_encode([$language => 1]);
|
||||
if (!empty($request['language'])) {
|
||||
$item['language'] = json_encode([$request['language'] => 1]);
|
||||
}
|
||||
|
||||
if ($in_reply_to_id) {
|
||||
$parent = Post::selectFirst(['uri'], ['uri-id' => $in_reply_to_id, 'uid' => [0, $uid]]);
|
||||
if ($request['in_reply_to_id']) {
|
||||
$parent = Post::selectFirst(['uri'], ['uri-id' => $request['in_reply_to_id'], 'uid' => [0, $uid]]);
|
||||
$item['thr-parent'] = $parent['uri'];
|
||||
$item['gravity'] = GRAVITY_COMMENT;
|
||||
$item['object-type'] = Activity\ObjectType::COMMENT;
|
||||
|
@ -143,16 +144,16 @@ class Statuses extends BaseApi
|
|||
$item['object-type'] = Activity\ObjectType::NOTE;
|
||||
}
|
||||
|
||||
if (!empty($media_ids)) {
|
||||
if (!empty($request['media_ids'])) {
|
||||
$item['object-type'] = Activity\ObjectType::IMAGE;
|
||||
$item['post-type'] = Item::PT_IMAGE;
|
||||
$item['attachments'] = [];
|
||||
|
||||
foreach ($media_ids as $id) {
|
||||
foreach ($request['media_ids'] as $id) {
|
||||
$media = DBA::toArray(DBA::p("SELECT `resource-id`, `scale`, `type`, `desc`, `filename`, `datasize`, `width`, `height` FROM `photo`
|
||||
WHERE `resource-id` IN (SELECT `resource-id` FROM `photo` WHERE `id` = ?) AND `photo`.`uid` = ?
|
||||
ORDER BY `photo`.`width` DESC LIMIT 2", $id, $uid));
|
||||
|
||||
|
||||
if (empty($media)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -162,7 +163,7 @@ class Statuses extends BaseApi
|
|||
$ressources[] = $media[0]['resource-id'];
|
||||
$phototypes = Images::supportedTypes();
|
||||
$ext = $phototypes[$media[0]['type']];
|
||||
|
||||
|
||||
$attachment = ['type' => Post\Media::IMAGE, 'mimetype' => $media[0]['type'],
|
||||
'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . '.' . $ext,
|
||||
'size' => $media[0]['datasize'],
|
||||
|
@ -170,7 +171,7 @@ class Statuses extends BaseApi
|
|||
'description' => $media[0]['desc'] ?? '',
|
||||
'width' => $media[0]['width'],
|
||||
'height' => $media[0]['height']];
|
||||
|
||||
|
||||
if (count($media) > 1) {
|
||||
$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . '.' . $ext;
|
||||
$attachment['preview-width'] = $media[1]['width'];
|
||||
|
@ -184,7 +185,7 @@ class Statuses extends BaseApi
|
|||
if (!empty($id)) {
|
||||
$item = Post::selectFirst(['uri-id'], ['id' => $id]);
|
||||
if (!empty($item['uri-id'])) {
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid));
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,10 @@ class Context extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$request = self::getRequest([
|
||||
'limit' => 40, // Maximum number of results to return. Defaults to 40.
|
||||
]);
|
||||
|
||||
$id = $parameters['id'];
|
||||
|
||||
$parent = Post::selectFirst(['parent-uri-id'], ['uri-id' => $id]);
|
||||
|
@ -68,24 +72,20 @@ class Context extends BaseApi
|
|||
|
||||
$statuses = ['ancestors' => [], 'descendants' => []];
|
||||
|
||||
$ancestors = [];
|
||||
foreach (self::getParents($id, $parents) as $ancestor) {
|
||||
$ancestors[$ancestor] = DI::mstdnStatus()->createFromUriId($ancestor, $uid);
|
||||
$ancestors = self::getParents($id, $parents);
|
||||
|
||||
asort($ancestors);
|
||||
|
||||
foreach (array_slice($ancestors, 0, $request['limit']) as $ancestor) {
|
||||
$statuses['ancestors'][] = DI::mstdnStatus()->createFromUriId($ancestor, $uid);;
|
||||
}
|
||||
|
||||
ksort($ancestors);
|
||||
foreach ($ancestors as $ancestor) {
|
||||
$statuses['ancestors'][] = $ancestor;
|
||||
}
|
||||
$descendants = self::getChildren($id, $children);
|
||||
|
||||
$descendants = [];
|
||||
foreach (self::getChildren($id, $children) as $descendant) {
|
||||
$descendants[] = DI::mstdnStatus()->createFromUriId($descendant, $uid);
|
||||
}
|
||||
asort($descendants);
|
||||
|
||||
ksort($descendants);
|
||||
foreach ($descendants as $descendant) {
|
||||
$statuses['descendants'][] = $descendant;
|
||||
foreach (array_slice($descendants, 0, $request['limit']) as $descendant) {
|
||||
$statuses['descendants'][] = DI::mstdnStatus()->createFromUriId($descendant, $uid);
|
||||
}
|
||||
|
||||
System::jsonExit($statuses);
|
||||
|
|
|
@ -41,6 +41,8 @@ class PublicTimeline extends BaseApi
|
|||
*/
|
||||
public static function rawContent(array $parameters = [])
|
||||
{
|
||||
$uid = self::getCurrentUserID();
|
||||
|
||||
$request = self::getRequest([
|
||||
'local' => false, // Show only local statuses? Defaults to false.
|
||||
'remote' => false, // Show only remote statuses? Defaults to false.
|
||||
|
@ -56,7 +58,7 @@ class PublicTimeline extends BaseApi
|
|||
$params = ['order' => ['uri-id' => true], 'limit' => $request['limit']];
|
||||
|
||||
$condition = ['gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'private' => Item::PUBLIC,
|
||||
'uid' => 0, 'network' => Protocol::FEDERATED];
|
||||
'uid' => 0, 'network' => Protocol::FEDERATED, 'parent-author-blocked' => false, 'parent-author-hidden' => false];
|
||||
|
||||
if ($request['local']) {
|
||||
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin`)"]);
|
||||
|
@ -88,7 +90,12 @@ class PublicTimeline extends BaseApi
|
|||
$condition = DBA::mergeConditions($condition, ['gravity' => GRAVITY_PARENT]);
|
||||
}
|
||||
|
||||
$items = Post::selectForUser(0, ['uri-id', 'uid'], $condition, $params);
|
||||
if (!empty($uid)) {
|
||||
$condition = DBA::mergeConditions($condition,
|
||||
["NOT EXISTS (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` = `parent-author-id` AND (`blocked` OR `ignored`))", $uid]);
|
||||
}
|
||||
|
||||
$items = Post::selectForUser($uid, ['uri-id', 'uid'], $condition, $params);
|
||||
|
||||
$statuses = [];
|
||||
while ($item = Post::fetch($items)) {
|
||||
|
|
|
@ -43,7 +43,7 @@ class Attach extends BaseModule
|
|||
|
||||
// @TODO: Replace with parameter from router
|
||||
$item_id = intval($a->argv[1]);
|
||||
|
||||
|
||||
// Check for existence
|
||||
$item = MAttach::exists(['id' => $item_id]);
|
||||
if ($item === false) {
|
||||
|
|
|
@ -29,7 +29,7 @@ use Friendica\Database\DBA;
|
|||
use Friendica\DI;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\HTTPInputData;
|
||||
|
||||
require_once __DIR__ . '/../../include/api.php';
|
||||
|
||||
|
@ -129,7 +129,7 @@ class BaseApi extends BaseModule
|
|||
public static function unsupported(string $method = 'all')
|
||||
{
|
||||
$path = DI::args()->getQueryString();
|
||||
Logger::info('Unimplemented API call', ['method' => $method, 'path' => $path, 'agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'request' => $_REQUEST ?? []]);
|
||||
Logger::info('Unimplemented API call', ['method' => $method, 'path' => $path, 'agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'request' => HTTPInputData::process()]);
|
||||
$error = DI::l10n()->t('API endpoint %s %s is not implemented', strtoupper($method), $path);
|
||||
$error_description = DI::l10n()->t('The API endpoint is currently not implemented but might be in the future.');
|
||||
$errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
|
||||
|
@ -141,26 +141,30 @@ class BaseApi extends BaseModule
|
|||
*
|
||||
* @return array request data
|
||||
*/
|
||||
public static function getRequest(array $defaults) {
|
||||
public static function getRequest(array $defaults)
|
||||
{
|
||||
$httpinput = HTTPInputData::process();
|
||||
$input = array_merge($httpinput['variables'], $httpinput['files'], $_REQUEST);
|
||||
|
||||
$request = [];
|
||||
|
||||
foreach ($defaults as $parameter => $defaultvalue) {
|
||||
if (is_string($defaultvalue)) {
|
||||
$request[$parameter] = $_REQUEST[$parameter] ?? $defaultvalue;
|
||||
$request[$parameter] = $input[$parameter] ?? $defaultvalue;
|
||||
} elseif (is_int($defaultvalue)) {
|
||||
$request[$parameter] = (int)($_REQUEST[$parameter] ?? $defaultvalue);
|
||||
$request[$parameter] = (int)($input[$parameter] ?? $defaultvalue);
|
||||
} elseif (is_float($defaultvalue)) {
|
||||
$request[$parameter] = (float)($_REQUEST[$parameter] ?? $defaultvalue);
|
||||
$request[$parameter] = (float)($input[$parameter] ?? $defaultvalue);
|
||||
} elseif (is_array($defaultvalue)) {
|
||||
$request[$parameter] = $_REQUEST[$parameter] ?? [];
|
||||
$request[$parameter] = $input[$parameter] ?? [];
|
||||
} elseif (is_bool($defaultvalue)) {
|
||||
$request[$parameter] = in_array(strtolower($_REQUEST[$parameter] ?? ''), ['true', '1']);
|
||||
$request[$parameter] = in_array(strtolower($input[$parameter] ?? ''), ['true', '1']);
|
||||
} else {
|
||||
Logger::notice('Unhandled default value type', ['parameter' => $parameter, 'type' => gettype($defaultvalue)]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($_REQUEST ?? [] as $parameter => $value) {
|
||||
foreach ($input ?? [] as $parameter => $value) {
|
||||
if ($parameter == 'pagename') {
|
||||
continue;
|
||||
}
|
||||
|
@ -173,45 +177,6 @@ class BaseApi extends BaseModule
|
|||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post data that is transmitted as JSON
|
||||
*
|
||||
* @return array request data
|
||||
*/
|
||||
public static function getJsonPostData()
|
||||
{
|
||||
$postdata = Network::postdata();
|
||||
if (empty($postdata)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return json_decode($postdata, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request data for put requests
|
||||
*
|
||||
* @return array request data
|
||||
*/
|
||||
public static function getPutData()
|
||||
{
|
||||
$rawdata = Network::postdata();
|
||||
if (empty($rawdata)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$putdata = [];
|
||||
|
||||
foreach (explode('&', $rawdata) as $value) {
|
||||
$data = explode('=', $value);
|
||||
if (count($data) == 2) {
|
||||
$putdata[$data[0]] = urldecode($data[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return $putdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log in user via OAuth1 or Simple HTTP Auth.
|
||||
*
|
||||
|
@ -394,7 +359,7 @@ class BaseApi extends BaseModule
|
|||
Logger::warning('Requested token scope is not allowed for the application', ['token' => $fields, 'application' => $application]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!DBA::insert('application-token', $fields, Database::INSERT_UPDATE)) {
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ use Friendica\Core\Session;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
/**
|
||||
* Performs an activity (like, dislike, announce, attendyes, attendno, attendmaybe)
|
||||
|
@ -90,7 +89,7 @@ class Activity extends BaseModule
|
|||
{
|
||||
$fields = ['uri-id', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
|
||||
$item = Post::selectFirst($fields, ['id' => $itemId, 'private' => [Item::PUBLIC, Item::UNLISTED]]);
|
||||
if (!DBA::isResult($item) || ($item['body'] == '')) {
|
||||
if (!DBA::isResult($item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,20 +50,20 @@ class NoScrape extends BaseModule
|
|||
System::jsonError(403, 'Authentication required');
|
||||
}
|
||||
|
||||
Profile::load($a, $which);
|
||||
$profile = Profile::getByNickname($which, local_user());
|
||||
|
||||
if (empty($a->profile['uid'])) {
|
||||
if (empty($profile['uid'])) {
|
||||
System::jsonError(404, 'Profile not found');
|
||||
}
|
||||
|
||||
$json_info = [
|
||||
'addr' => $a->profile['addr'],
|
||||
'addr' => $profile['addr'],
|
||||
'nick' => $which,
|
||||
'guid' => $a->profile['guid'],
|
||||
'key' => $a->profile['upubkey'],
|
||||
'guid' => $profile['guid'],
|
||||
'key' => $profile['upubkey'],
|
||||
'homepage' => DI::baseUrl() . "/profile/{$which}",
|
||||
'comm' => ($a->profile['account-type'] == User::ACCOUNT_TYPE_COMMUNITY),
|
||||
'account-type' => $a->profile['account-type'],
|
||||
'comm' => ($profile['account-type'] == User::ACCOUNT_TYPE_COMMUNITY),
|
||||
'account-type' => $profile['account-type'],
|
||||
];
|
||||
|
||||
$dfrn_pages = ['request', 'confirm', 'notify', 'poll'];
|
||||
|
@ -71,30 +71,30 @@ class NoScrape extends BaseModule
|
|||
$json_info["dfrn-{$dfrn}"] = DI::baseUrl() . "/dfrn_{$dfrn}/{$which}";
|
||||
}
|
||||
|
||||
if (!$a->profile['net-publish']) {
|
||||
if (!$profile['net-publish']) {
|
||||
$json_info['hide'] = true;
|
||||
System::jsonExit($json_info);
|
||||
}
|
||||
|
||||
$keywords = $a->profile['pub_keywords'] ?? '';
|
||||
$keywords = $profile['pub_keywords'] ?? '';
|
||||
$keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], $keywords);
|
||||
$keywords = explode(',', $keywords);
|
||||
|
||||
$contactPhoto = DBA::selectFirst('contact', ['photo'], ['self' => true, 'uid' => $a->profile['uid']]);
|
||||
$contactPhoto = DBA::selectFirst('contact', ['photo'], ['self' => true, 'uid' => $profile['uid']]);
|
||||
|
||||
$json_info['fn'] = $a->profile['name'];
|
||||
$json_info['fn'] = $profile['name'];
|
||||
$json_info['photo'] = $contactPhoto["photo"];
|
||||
$json_info['tags'] = $keywords;
|
||||
$json_info['language'] = $a->profile['language'];
|
||||
$json_info['language'] = $profile['language'];
|
||||
|
||||
if (!empty($a->profile['last-item'])) {
|
||||
$json_info['updated'] = date("c", strtotime($a->profile['last-item']));
|
||||
if (!empty($profile['last-item'])) {
|
||||
$json_info['updated'] = date("c", strtotime($profile['last-item']));
|
||||
}
|
||||
|
||||
if (!($a->profile['hide-friends'] ?? false)) {
|
||||
if (!($profile['hide-friends'] ?? false)) {
|
||||
$json_info['contacts'] = DBA::count('contact',
|
||||
[
|
||||
'uid' => $a->profile['uid'],
|
||||
'uid' => $profile['uid'],
|
||||
'self' => 0,
|
||||
'blocked' => 0,
|
||||
'pending' => 0,
|
||||
|
@ -106,13 +106,13 @@ class NoScrape extends BaseModule
|
|||
|
||||
// We display the last activity (post or login), reduced to year and week number
|
||||
$last_active = 0;
|
||||
$condition = ['uid' => $a->profile['uid'], 'self' => true];
|
||||
$condition = ['uid' => $profile['uid'], 'self' => true];
|
||||
$contact = DBA::selectFirst('contact', ['last-item'], $condition);
|
||||
if (DBA::isResult($contact)) {
|
||||
$last_active = strtotime($contact['last-item']);
|
||||
}
|
||||
|
||||
$condition = ['uid' => $a->profile['uid']];
|
||||
$condition = ['uid' => $profile['uid']];
|
||||
$user = DBA::selectFirst('user', ['login_date'], $condition);
|
||||
if (DBA::isResult($user)) {
|
||||
if ($last_active < strtotime($user['login_date'])) {
|
||||
|
@ -124,8 +124,8 @@ class NoScrape extends BaseModule
|
|||
//These are optional fields.
|
||||
$profile_fields = ['about', 'locality', 'region', 'postal-code', 'country-name'];
|
||||
foreach ($profile_fields as $field) {
|
||||
if (!empty($a->profile[$field])) {
|
||||
$json_info["$field"] = $a->profile[$field];
|
||||
if (!empty($profile[$field])) {
|
||||
$json_info["$field"] = $profile[$field];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,9 +63,9 @@ class Authorize extends BaseApi
|
|||
|
||||
// @todo Compare the application scope and requested scope
|
||||
|
||||
$request = $_REQUEST;
|
||||
unset($request['pagename']);
|
||||
$redirect = 'oauth/authorize?' . http_build_query($request);
|
||||
$redirect_request = $_REQUEST;
|
||||
unset($redirect_request['pagename']);
|
||||
$redirect = 'oauth/authorize?' . http_build_query($redirect_request);
|
||||
|
||||
$uid = local_user();
|
||||
if (empty($uid)) {
|
||||
|
|
|
@ -170,8 +170,10 @@ class Index extends BaseSearch
|
|||
}
|
||||
|
||||
if (!empty($uriids)) {
|
||||
$params = ['order' => ['id' => true], 'group_by' => ['uri-id']];
|
||||
$items = Post::toArray(Post::selectForUser(local_user(), Item::DISPLAY_FIELDLIST, ['uri-id' => $uriids], $params));
|
||||
$condition = ["(`uid` = ? OR (`uid` = ? AND NOT `global`))", 0, local_user()];
|
||||
$condition = DBA::mergeConditions($condition, ['uri-id' => $uriids]);
|
||||
$params = ['order' => ['id' => true]];
|
||||
$items = Post::toArray(Post::selectForUser(local_user(), Item::DISPLAY_FIELDLIST, $condition, $params));
|
||||
}
|
||||
|
||||
if (empty($items)) {
|
||||
|
|
|
@ -70,7 +70,7 @@ class HTTPRequest implements IHTTPRequest
|
|||
*
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public function get(string $url, array $opts = [], int &$redirects = 0)
|
||||
public function get(string $url, array $opts = [], &$redirects = 0)
|
||||
{
|
||||
$stamp1 = microtime(true);
|
||||
|
||||
|
@ -222,7 +222,7 @@ class HTTPRequest implements IHTTPRequest
|
|||
*
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public function post(string $url, $params, array $headers = [], int $timeout = 0, int &$redirects = 0)
|
||||
public function post(string $url, $params, array $headers = [], int $timeout = 0, &$redirects = 0)
|
||||
{
|
||||
$stamp1 = microtime(true);
|
||||
|
||||
|
@ -447,7 +447,7 @@ class HTTPRequest implements IHTTPRequest
|
|||
*
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public function fetch(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0)
|
||||
public function fetch(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = '', &$redirects = 0)
|
||||
{
|
||||
$ret = $this->fetchFull($url, $timeout, $accept_content, $cookiejar, $redirects);
|
||||
|
||||
|
@ -461,7 +461,7 @@ class HTTPRequest implements IHTTPRequest
|
|||
*
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public function fetchFull(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0)
|
||||
public function fetchFull(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = '', &$redirects = 0)
|
||||
{
|
||||
return $this->get(
|
||||
$url,
|
||||
|
|
|
@ -108,7 +108,7 @@ class Account extends BaseDataTransferObject
|
|||
$userContactCreated = $userContact['created'] ?? DBA::NULL_DATETIME;
|
||||
|
||||
$created = $userContactCreated < $publicContactCreated && ($userContactCreated != DBA::NULL_DATETIME) ? $userContactCreated : $publicContactCreated;
|
||||
$this->created_at = DateTimeFormat::utc($created, DateTimeFormat::ATOM);
|
||||
$this->created_at = DateTimeFormat::utc($created, DateTimeFormat::JSON);
|
||||
|
||||
$this->note = BBCode::convert($publicContact['about'], false);
|
||||
$this->url = $publicContact['url'];
|
||||
|
|
|
@ -52,7 +52,7 @@ class Notification extends BaseDataTransferObject
|
|||
{
|
||||
$this->id = (string)$id;
|
||||
$this->type = $type;
|
||||
$this->created_at = DateTimeFormat::utc($created_at, DateTimeFormat::ATOM);
|
||||
$this->created_at = DateTimeFormat::utc($created_at, DateTimeFormat::JSON);
|
||||
$this->account = $account->toArray();
|
||||
|
||||
if (!empty($status)) {
|
||||
|
|
|
@ -100,7 +100,7 @@ class Status extends BaseDataTransferObject
|
|||
public function __construct(array $item, Account $account, Counts $counts, UserAttributes $userAttributes, bool $sensitive, Application $application, array $mentions, array $tags, Card $card, array $attachments, array $reblog)
|
||||
{
|
||||
$this->id = (string)$item['uri-id'];
|
||||
$this->created_at = DateTimeFormat::utc($item['created'], DateTimeFormat::ATOM);
|
||||
$this->created_at = DateTimeFormat::utc($item['created'], DateTimeFormat::JSON);
|
||||
|
||||
if ($item['gravity'] == GRAVITY_COMMENT) {
|
||||
$this->in_reply_to_id = (string)$item['thr-parent-id'];
|
||||
|
@ -114,7 +114,12 @@ class Status extends BaseDataTransferObject
|
|||
$this->visibility = $visibility[$item['private']];
|
||||
|
||||
$languages = json_decode($item['language'], true);
|
||||
$this->language = is_array($languages) ? array_key_first($languages) : null;
|
||||
if (is_array($languages)) {
|
||||
reset($languages);
|
||||
$this->language = key($languages);
|
||||
} else {
|
||||
$this->language = null;
|
||||
}
|
||||
|
||||
$this->uri = $item['uri'];
|
||||
$this->url = $item['plink'] ?? null;
|
||||
|
|
|
@ -53,6 +53,6 @@ class Token extends BaseDataTransferObject
|
|||
$this->access_token = $access_token;
|
||||
$this->token_type = $token_type;
|
||||
$this->scope = $scope;
|
||||
$this->created_at = DateTimeFormat::utc($created_at, DateTimeFormat::ATOM);
|
||||
$this->created_at = DateTimeFormat::utc($created_at, DateTimeFormat::JSON);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -401,9 +401,13 @@ class Post
|
|||
}
|
||||
|
||||
// Fetching of Diaspora posts doesn't always work. There are issues with reshares and possibly comments
|
||||
if (($item['network'] != Protocol::DIASPORA) && empty($comment) && !empty(Session::get('remote_comment'))) {
|
||||
if (!local_user() && ($item['network'] != Protocol::DIASPORA) && !empty(Session::get('remote_comment'))) {
|
||||
$remote_comment = [DI::l10n()->t('Comment this item on your system'), DI::l10n()->t('Remote comment'),
|
||||
str_replace('{uri}', urlencode($item['uri']), Session::get('remote_comment'))];
|
||||
|
||||
// Ensure to either display the remote comment or the local activities
|
||||
$buttons = [];
|
||||
$comment_html = '';
|
||||
} else {
|
||||
$remote_comment = '';
|
||||
}
|
||||
|
|
|
@ -602,6 +602,12 @@ class Processor
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!($item['isForum'] ?? false) && ($receiver != 0) && ($item['gravity'] == GRAVITY_PARENT) &&
|
||||
($item['post-reason'] == Item::PR_BCC) && !Contact::isSharingByURL($activity['author'], $receiver)) {
|
||||
Logger::info('Top level post via BCC from a non sharer, ignoring', ['uid' => $receiver, 'contact' => $item['contact-id']]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DI::pConfig()->get($receiver, 'system', 'accept_only_sharer', false) && ($receiver != 0) && ($item['gravity'] == GRAVITY_PARENT)) {
|
||||
$skip = !Contact::isSharingByURL($activity['author'], $receiver);
|
||||
|
||||
|
|
|
@ -748,10 +748,6 @@ class Transmitter
|
|||
|
||||
$contacts = DBA::select('contact', ['id', 'url', 'network', 'protocol', 'gsid'], $condition);
|
||||
while ($contact = DBA::fetch($contacts)) {
|
||||
if (Contact::isLocal($contact['url'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!self::isAPContact($contact, $networks)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -766,7 +762,7 @@ class Transmitter
|
|||
|
||||
$profile = APContact::getByURL($contact['url'], false);
|
||||
if (!empty($profile)) {
|
||||
if (empty($profile['sharedinbox']) || $personal) {
|
||||
if (empty($profile['sharedinbox']) || $personal || Contact::isLocal($contact['url'])) {
|
||||
$target = $profile['inbox'];
|
||||
} else {
|
||||
$target = $profile['sharedinbox'];
|
||||
|
@ -829,15 +825,11 @@ class Transmitter
|
|||
if ($item_profile && ($receiver == $item_profile['followers']) && ($uid == $profile_uid)) {
|
||||
$inboxes = array_merge($inboxes, self::fetchTargetInboxesforUser($uid, $personal, self::isAPPost($last_id)));
|
||||
} else {
|
||||
if (Contact::isLocal($receiver)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$profile = APContact::getByURL($receiver, false);
|
||||
if (!empty($profile)) {
|
||||
$contact = Contact::getByURLForUser($receiver, $uid, false, ['id']);
|
||||
|
||||
if (empty($profile['sharedinbox']) || $personal || $blindcopy) {
|
||||
if (empty($profile['sharedinbox']) || $personal || $blindcopy || Contact::isLocal($receiver)) {
|
||||
$target = $profile['inbox'];
|
||||
} else {
|
||||
$target = $profile['sharedinbox'];
|
||||
|
@ -1525,12 +1517,21 @@ class Transmitter
|
|||
|
||||
if ($type == 'Note') {
|
||||
$body = $item['raw-body'] ?? self::removePictures($body);
|
||||
} elseif (($type == 'Article') && empty($data['summary'])) {
|
||||
$regexp = "/[@!]\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
|
||||
$summary = preg_replace_callback($regexp, ['self', 'mentionAddrCallback'], $body);
|
||||
$data['summary'] = BBCode::toPlaintext(Plaintext::shorten(self::removePictures($summary), 1000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Improve the automated summary
|
||||
* This part is currently deactivated. The automated summary seems to be more
|
||||
* confusing than helping. But possibly we will find a better way.
|
||||
* So the code is left here for now as a reminder
|
||||
*
|
||||
* } elseif (($type == 'Article') && empty($data['summary'])) {
|
||||
* $regexp = "/[@!]\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
|
||||
* $summary = preg_replace_callback($regexp, ['self', 'mentionAddrCallback'], $body);
|
||||
* $data['summary'] = BBCode::toPlaintext(Plaintext::shorten(self::removePictures($summary), 1000));
|
||||
* }
|
||||
*/
|
||||
|
||||
if (empty($item['uid']) || !Feature::isEnabled($item['uid'], 'explicit_mentions')) {
|
||||
$body = self::prependMentions($body, $item['uri-id'], $item['author-link']);
|
||||
}
|
||||
|
|
|
@ -4049,13 +4049,11 @@ class Diaspora
|
|||
return false;
|
||||
}
|
||||
|
||||
$parent = Post::selectFirst(['parent-uri'], ['uri' => $item['thr-parent']]);
|
||||
if (!DBA::isResult($parent)) {
|
||||
return;
|
||||
// This is only needed for the automated tests
|
||||
if (empty($owner['uprvkey'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$item['parent-uri'] = $parent['parent-uri'];
|
||||
|
||||
$message = self::constructComment($item, $owner);
|
||||
if ($message === false) {
|
||||
return false;
|
||||
|
|
|
@ -87,8 +87,13 @@ class Notification extends BaseRepository
|
|||
public function setSeen(bool $seen = true, Model\Notification $notify = null)
|
||||
{
|
||||
if (empty($notify)) {
|
||||
$this->dba->update('notification', ['seen' => $seen], ['uid' => local_user()]);
|
||||
$conditions = ['uid' => local_user()];
|
||||
} else {
|
||||
if (!empty($notify->{'uri-id'})) {
|
||||
$this->dba->update('notification', ['seen' => $seen], ['uid' => local_user(), 'target-uri-id' => $notify->{'uri-id'}]);
|
||||
}
|
||||
|
||||
$conditions = ['(`link` = ? OR (`parent` != 0 AND `parent` = ? AND `otype` = ?)) AND `uid` = ?',
|
||||
$notify->link,
|
||||
$notify->parent,
|
||||
|
|
|
@ -31,9 +31,10 @@ use Exception;
|
|||
*/
|
||||
class DateTimeFormat
|
||||
{
|
||||
const ATOM = 'Y-m-d\TH:i:s\Z';
|
||||
const ATOM = 'Y-m-d\TH:i:s\Z';
|
||||
const MYSQL = 'Y-m-d H:i:s';
|
||||
const HTTP = 'D, d M Y H:i:s \G\M\T';
|
||||
const HTTP = 'D, d M Y H:i:s \G\M\T';
|
||||
const JSON = 'Y-m-d\TH:i:s.v\Z';
|
||||
|
||||
/**
|
||||
* convert() shorthand for UTC.
|
||||
|
|
295
src/Util/HTTPInputData.php
Normal file
295
src/Util/HTTPInputData.php
Normal file
|
@ -0,0 +1,295 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Util;
|
||||
|
||||
/**
|
||||
* Derived from the work of Reid Johnson <https://codereview.stackexchange.com/users/4020/reid-johnson>
|
||||
* @see https://codereview.stackexchange.com/questions/69882/parsing-multipart-form-data-in-php-for-put-requests
|
||||
*/
|
||||
class HTTPInputData
|
||||
{
|
||||
public static function process()
|
||||
{
|
||||
$content_parts = explode(';', static::getContentType());
|
||||
|
||||
$boundary = '';
|
||||
$encoding = '';
|
||||
|
||||
$content_type = array_shift($content_parts);
|
||||
|
||||
foreach ($content_parts as $part) {
|
||||
if (strpos($part, 'boundary') !== false) {
|
||||
$part = explode('=', $part, 2);
|
||||
if (!empty($part[1])) {
|
||||
$boundary = '--' . $part[1];
|
||||
}
|
||||
} elseif (strpos($part, 'charset') !== false) {
|
||||
$part = explode('=', $part, 2);
|
||||
if (!empty($part[1])) {
|
||||
$encoding = $part[1];
|
||||
}
|
||||
}
|
||||
if ($boundary !== '' && $encoding !== '') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($content_type == 'multipart/form-data') {
|
||||
return self::fetchFromMultipart($boundary);
|
||||
}
|
||||
|
||||
// can be handled by built in PHP functionality
|
||||
$content = static::getPhpInputContent();
|
||||
|
||||
$variables = json_decode($content, true);
|
||||
|
||||
if (empty($variables)) {
|
||||
parse_str($content, $variables);
|
||||
}
|
||||
|
||||
return ['variables' => $variables, 'files' => []];
|
||||
}
|
||||
|
||||
private static function fetchFromMultipart(string $boundary)
|
||||
{
|
||||
$result = ['variables' => [], 'files' => []];
|
||||
|
||||
$stream = static::getPhpInputStream();
|
||||
|
||||
$sanity = fgets($stream, strlen($boundary) + 5);
|
||||
|
||||
// malformed file, boundary should be first item
|
||||
if (rtrim($sanity) !== $boundary) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$raw_headers = '';
|
||||
|
||||
while (($chunk = fgets($stream)) !== false) {
|
||||
if ($chunk === $boundary) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty(trim($chunk))) {
|
||||
$raw_headers .= $chunk;
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = self::parseRawHeader($stream, $raw_headers, $boundary, $result);
|
||||
|
||||
$raw_headers = '';
|
||||
}
|
||||
|
||||
fclose($stream);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function parseRawHeader($stream, string $raw_headers, string $boundary, array $result)
|
||||
{
|
||||
$variables = $result['variables'];
|
||||
$files = $result['files'];
|
||||
|
||||
$headers = [];
|
||||
|
||||
foreach (explode("\r\n", $raw_headers) as $header) {
|
||||
if (strpos($header, ':') === false) {
|
||||
continue;
|
||||
}
|
||||
list($name, $value) = explode(':', $header, 2);
|
||||
|
||||
$headers[strtolower($name)] = ltrim($value, ' ');
|
||||
}
|
||||
|
||||
if (!isset($headers['content-disposition'])) {
|
||||
return ['variables' => $variables, 'files' => $files];
|
||||
}
|
||||
|
||||
if (!preg_match('/^(.+); *name="([^"]+)"(; *filename="([^"]+)")?/', $headers['content-disposition'], $matches)) {
|
||||
return ['variables' => $variables, 'files' => $files];
|
||||
}
|
||||
|
||||
$name = $matches[2];
|
||||
$filename = $matches[4] ?? '';
|
||||
|
||||
if (!empty($filename)) {
|
||||
$files[$name] = static::fetchFileData($stream, $boundary, $headers, $filename);
|
||||
return ['variables' => $variables, 'files' => $files];
|
||||
} else {
|
||||
$variables = self::fetchVariables($stream, $boundary, $headers, $name, $variables);
|
||||
}
|
||||
|
||||
return ['variables' => $variables, 'files' => $files];
|
||||
}
|
||||
|
||||
protected static function fetchFileData($stream, string $boundary, array $headers, string $filename)
|
||||
{
|
||||
$error = UPLOAD_ERR_OK;
|
||||
|
||||
if (isset($headers['content-type'])) {
|
||||
$tmp = explode(';', $headers['content-type']);
|
||||
|
||||
$contentType = $tmp[0];
|
||||
} else {
|
||||
$contentType = 'unknown';
|
||||
}
|
||||
|
||||
$tmpnam = tempnam(ini_get('upload_tmp_dir'), 'php');
|
||||
$fileHandle = fopen($tmpnam, 'wb');
|
||||
|
||||
if ($fileHandle === false) {
|
||||
$error = UPLOAD_ERR_CANT_WRITE;
|
||||
} else {
|
||||
$lastLine = null;
|
||||
while (($chunk = fgets($stream, 8096)) !== false && strpos($chunk, $boundary) !== 0) {
|
||||
if ($lastLine !== null) {
|
||||
if (!fwrite($fileHandle, $lastLine)) {
|
||||
$error = UPLOAD_ERR_CANT_WRITE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$lastLine = $chunk;
|
||||
}
|
||||
|
||||
if ($lastLine !== null && $error !== UPLOAD_ERR_CANT_WRITE) {
|
||||
if (!fwrite($fileHandle, rtrim($lastLine, "\r\n"))) {
|
||||
$error = UPLOAD_ERR_CANT_WRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $filename,
|
||||
'type' => $contentType,
|
||||
'tmp_name' => $tmpnam,
|
||||
'error' => $error,
|
||||
'size' => filesize($tmpnam)
|
||||
];
|
||||
}
|
||||
|
||||
private static function fetchVariables($stream, string $boundary, array $headers, string $name, array $variables)
|
||||
{
|
||||
$fullValue = '';
|
||||
$lastLine = null;
|
||||
|
||||
while (($chunk = fgets($stream)) !== false && strpos($chunk, $boundary) !== 0) {
|
||||
if ($lastLine !== null) {
|
||||
$fullValue .= $lastLine;
|
||||
}
|
||||
|
||||
$lastLine = $chunk;
|
||||
}
|
||||
|
||||
if ($lastLine !== null) {
|
||||
$fullValue .= rtrim($lastLine, "\r\n");
|
||||
}
|
||||
|
||||
if (isset($headers['content-type'])) {
|
||||
$encoding = '';
|
||||
|
||||
foreach (explode(';', $headers['content-type']) as $part) {
|
||||
if (strpos($part, 'charset') !== false) {
|
||||
$part = explode($part, '=', 2);
|
||||
if (isset($part[1])) {
|
||||
$encoding = $part[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($encoding !== '' && strtoupper($encoding) !== 'UTF-8' && strtoupper($encoding) !== 'UTF8') {
|
||||
$tmp = mb_convert_encoding($fullValue, 'UTF-8', $encoding);
|
||||
if ($tmp !== false) {
|
||||
$fullValue = $tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$fullValue = $name . '=' . $fullValue;
|
||||
|
||||
$tmp = [];
|
||||
parse_str($fullValue, $tmp);
|
||||
|
||||
return self::expandVariables(explode('[', $name), $variables, $tmp);
|
||||
}
|
||||
|
||||
private static function expandVariables(array $names, $variables, array $values)
|
||||
{
|
||||
if (!is_array($variables)) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
$name = rtrim(array_shift($names), ']');
|
||||
if ($name !== '') {
|
||||
$name = $name . '=p';
|
||||
|
||||
$tmp = [];
|
||||
parse_str($name, $tmp);
|
||||
|
||||
$tmp = array_keys($tmp);
|
||||
$name = reset($tmp);
|
||||
}
|
||||
|
||||
if ($name === '') {
|
||||
$variables[] = reset($values);
|
||||
} elseif (isset($variables[$name]) && isset($values[$name])) {
|
||||
$variables[$name] = self::expandVariables($names, $variables[$name], $values[$name]);
|
||||
} elseif (isset($values[$name])) {
|
||||
$variables[$name] = $values[$name];
|
||||
}
|
||||
|
||||
return $variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current PHP input stream
|
||||
* Mainly used for test doubling
|
||||
*
|
||||
* @return false|resource
|
||||
*/
|
||||
protected static function getPhpInputStream()
|
||||
{
|
||||
return fopen('php://input', 'rb');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of the current PHP input
|
||||
* Mainly used for test doubling
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
protected static function getPhpInputContent()
|
||||
{
|
||||
return file_get_contents('php://input');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content type string of the current call
|
||||
* Mainly used for test doubling
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
protected static function getContentType()
|
||||
{
|
||||
return $_SERVER['CONTENT_TYPE'] ?? 'application/x-www-form-urlencoded';
|
||||
}
|
||||
}
|
|
@ -640,16 +640,17 @@ class HTTPSignature
|
|||
|
||||
$profile = APContact::getByURL($url);
|
||||
if (!empty($profile)) {
|
||||
Logger::log('Taking key from id ' . $id, Logger::DEBUG);
|
||||
Logger::info('Taking key from id', ['id' => $id]);
|
||||
return ['url' => $url, 'pubkey' => $profile['pubkey'], 'type' => $profile['type']];
|
||||
} elseif ($url != $actor) {
|
||||
$profile = APContact::getByURL($actor);
|
||||
if (!empty($profile)) {
|
||||
Logger::log('Taking key from actor ' . $actor, Logger::DEBUG);
|
||||
Logger::info('Taking key from actor', ['actor' => $actor]);
|
||||
return ['url' => $actor, 'pubkey' => $profile['pubkey'], 'type' => $profile['type']];
|
||||
}
|
||||
}
|
||||
|
||||
Logger::notice('Key could not be fetched', ['url' => $url, 'actor' => $actor]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -548,4 +548,15 @@ class Network
|
|||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given URL is a local link
|
||||
*
|
||||
* @param string $url
|
||||
* @return bool
|
||||
*/
|
||||
public static function isLocalLink(string $url)
|
||||
{
|
||||
return (strpos(Strings::normaliseLink($url), Strings::normaliseLink(DI::baseUrl())) !== false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,13 +30,11 @@ use Friendica\Protocol\DFRN;
|
|||
use Friendica\Protocol\Diaspora;
|
||||
use Friendica\Protocol\Email;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Model\Conversation;
|
||||
use Friendica\Model\FContact;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Protocol\Relay;
|
||||
|
||||
class Delivery
|
||||
|
@ -216,11 +214,6 @@ class Delivery
|
|||
$contact['network'] = Protocol::DIASPORA;
|
||||
}
|
||||
|
||||
// Ensure that local contacts are delivered locally
|
||||
if (Model\Contact::isLocal($contact['url'])) {
|
||||
$contact['network'] = Protocol::DFRN;
|
||||
}
|
||||
|
||||
Logger::notice('Delivering', ['cmd' => $cmd, 'uri-id' => $post_uriid, 'followup' => $followup, 'network' => $contact['network']]);
|
||||
|
||||
switch ($contact['network']) {
|
||||
|
@ -316,40 +309,6 @@ class Delivery
|
|||
|
||||
Logger::debug('Notifier entry: ' . $contact["url"] . ' ' . (($target_item['guid'] ?? '') ?: $target_item['id']) . ' entry: ' . $atom);
|
||||
|
||||
// perform local delivery if we are on the same site
|
||||
if (Model\Contact::isLocal($contact['url'])) {
|
||||
$condition = ['nurl' => Strings::normaliseLink($contact['url']), 'self' => true];
|
||||
$target_self = DBA::selectFirst('contact', ['uid'], $condition);
|
||||
if (!DBA::isResult($target_self)) {
|
||||
return;
|
||||
}
|
||||
$target_uid = $target_self['uid'];
|
||||
|
||||
// Check if the user has got this contact
|
||||
$cid = Model\Contact::getIdForURL($owner['url'], $target_uid);
|
||||
if (!$cid) {
|
||||
// Otherwise there should be a public contact
|
||||
$cid = Model\Contact::getIdForURL($owner['url']);
|
||||
if (!$cid) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$target_importer = DFRN::getImporter($cid, $target_uid);
|
||||
if (empty($target_importer)) {
|
||||
// This should never happen
|
||||
return;
|
||||
}
|
||||
|
||||
DFRN::import($atom, $target_importer, Conversation::PARCEL_LOCAL_DFRN, Conversation::PUSH);
|
||||
|
||||
if (in_array($cmd, [Delivery::POST, Delivery::POKE])) {
|
||||
Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::DFRN);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$protocol = Model\Post\DeliveryData::DFRN;
|
||||
|
||||
// We don't have a relationship with contacts on a public post.
|
||||
|
|
|
@ -119,7 +119,7 @@ class ExpirePosts
|
|||
Logger::notice('Adding missing entries');
|
||||
|
||||
$rows = 0;
|
||||
$userposts = DBA::select('post-user', [], ["`uri-id` not in (select `uri-id` from `post`)"], ['group_by' => ['uri-id']]);
|
||||
$userposts = DBA::select('post-user', [], ["`uri-id` not in (select `uri-id` from `post`)"]);
|
||||
while ($fields = DBA::fetch($userposts)) {
|
||||
$post_fields = DBStructure::getFieldsForTable('post', $fields);
|
||||
DBA::insert('post', $post_fields, Database::INSERT_IGNORE);
|
||||
|
@ -133,7 +133,7 @@ class ExpirePosts
|
|||
}
|
||||
|
||||
$rows = 0;
|
||||
$userposts = DBA::select('post-user', [], ["`gravity` = ? AND `uri-id` not in (select `uri-id` from `post-thread`)", GRAVITY_PARENT], ['group_by' => ['uri-id']]);
|
||||
$userposts = DBA::select('post-user', [], ["`gravity` = ? AND `uri-id` not in (select `uri-id` from `post-thread`)", GRAVITY_PARENT]);
|
||||
while ($fields = DBA::fetch($userposts)) {
|
||||
$post_fields = DBStructure::getFieldsForTable('post-thread', $fields);
|
||||
$post_fields['commented'] = $post_fields['changed'] = $post_fields['created'];
|
||||
|
|
|
@ -41,6 +41,7 @@ use Friendica\Protocol\Diaspora;
|
|||
use Friendica\Protocol\OStatus;
|
||||
use Friendica\Protocol\Relay;
|
||||
use Friendica\Protocol\Salmon;
|
||||
use Friendica\Util\Network;
|
||||
|
||||
/*
|
||||
* The notifier is typically called with:
|
||||
|
@ -313,7 +314,7 @@ class Notifier
|
|||
/// @todo Possibly we should not uplink when the author is the forum itself?
|
||||
|
||||
if ((intval($parent['forum_mode']) == 1) && !$top_level && ($cmd !== Delivery::UPLINK)
|
||||
&& ($target_item['verb'] != Activity::ANNOUNCE)) {
|
||||
&& ($target_item['verb'] != Activity::ANNOUNCE)) {
|
||||
Worker::add($a->queue['priority'], 'Notifier', Delivery::UPLINK, $post_uriid, $sender_uid);
|
||||
}
|
||||
|
||||
|
@ -494,29 +495,32 @@ class Notifier
|
|||
/**
|
||||
* Deliver the message to the contacts
|
||||
*
|
||||
* @param string $cmd
|
||||
* @param int $post_uriid
|
||||
* @param string $cmd
|
||||
* @param int $post_uriid
|
||||
* @param int $sender_uid
|
||||
* @param array $target_item
|
||||
* @param array $thr_parent
|
||||
* @param array $owner
|
||||
* @param bool $batch_delivery
|
||||
* @param array $contacts
|
||||
* @param array $ap_contacts
|
||||
* @param array $conversants
|
||||
* @return int
|
||||
* @throws InternalServerErrorException
|
||||
* @throws Exception
|
||||
* @param array $target_item
|
||||
* @param array $thr_parent
|
||||
* @param array $owner
|
||||
* @param bool $batch_delivery
|
||||
* @param array $contacts
|
||||
* @param array $ap_contacts
|
||||
* @param array $conversants
|
||||
* @return int
|
||||
* @throws InternalServerErrorException
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function delivery(string $cmd, int $post_uriid, int $sender_uid, array $target_item, array $thr_parent, array $owner, bool $batch_delivery, bool $in_batch, array $contacts, array $ap_contacts, array $conversants = [])
|
||||
{
|
||||
$a = DI::app();
|
||||
$a = DI::app();
|
||||
$delivery_queue_count = 0;
|
||||
|
||||
foreach ($contacts as $contact) {
|
||||
// Ensure that local contacts are delivered via DFRN
|
||||
if (Contact::isLocal($contact['url'])) {
|
||||
$contact['network'] = Protocol::DFRN;
|
||||
// Direct delivery of local contacts
|
||||
if ($target_uid = User::getIdForURL($contact['url'])) {
|
||||
Logger::info('Direct delivery', ['uri-id' => $target_item['uri-id'], 'target' => $target_uid]);
|
||||
$fields = ['protocol' => Conversation::PARCEL_LOCAL_DFRN, 'direction' => Conversation::PUSH];
|
||||
Item::storeForUserByUriId($target_item['uri-id'], $target_uid, $fields, $target_item['uid']);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Deletions are always sent via DFRN as well.
|
||||
|
@ -575,19 +579,19 @@ class Notifier
|
|||
/**
|
||||
* Deliver the message via OStatus
|
||||
*
|
||||
* @param int $target_id
|
||||
* @param array $target_item
|
||||
* @param array $owner
|
||||
* @param array $url_recipients
|
||||
* @param bool $public_message
|
||||
* @param bool $push_notify
|
||||
* @return int
|
||||
* @throws InternalServerErrorException
|
||||
* @throws Exception
|
||||
* @param int $target_id
|
||||
* @param array $target_item
|
||||
* @param array $owner
|
||||
* @param array $url_recipients
|
||||
* @param bool $public_message
|
||||
* @param bool $push_notify
|
||||
* @return int
|
||||
* @throws InternalServerErrorException
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function deliverOStatus(int $target_id, array $target_item, array $owner, array $url_recipients, bool $public_message, bool $push_notify)
|
||||
{
|
||||
$a = DI::app();
|
||||
$a = DI::app();
|
||||
$delivery_queue_count = 0;
|
||||
|
||||
$url_recipients = array_filter($url_recipients);
|
||||
|
@ -775,6 +779,16 @@ class Notifier
|
|||
foreach ($inboxes as $inbox => $receivers) {
|
||||
$contacts = array_merge($contacts, $receivers);
|
||||
|
||||
if ((count($receivers) == 1) && Network::isLocalLink($inbox)) {
|
||||
$contact = Contact::getById($receivers[0], ['url']);
|
||||
if ($target_uid = User::getIdForURL($contact['url'])) {
|
||||
$fields = ['protocol' => Conversation::PARCEL_LOCAL_DFRN, 'direction' => Conversation::PUSH, 'post-reason' => Item::PR_BCC];
|
||||
Item::storeForUserByUriId($target_item['uri-id'], $target_uid, $fields, $target_item['uid']);
|
||||
Logger::info('Delivered locally', ['cmd' => $cmd, 'id' => $target_item['id'], 'inbox' => $inbox]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Logger::info('Delivery via ActivityPub', ['cmd' => $cmd, 'id' => $target_item['id'], 'inbox' => $inbox]);
|
||||
|
||||
if (Worker::add(['priority' => $priority, 'created' => $created, 'dont_fork' => true],
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
use Friendica\Database\DBA;
|
||||
|
||||
if (!defined('DB_UPDATE_VERSION')) {
|
||||
define('DB_UPDATE_VERSION', 1419);
|
||||
define('DB_UPDATE_VERSION', 1421);
|
||||
}
|
||||
|
||||
return [
|
||||
|
@ -882,6 +882,29 @@ return [
|
|||
"mid" => ["mid"],
|
||||
]
|
||||
],
|
||||
"notification" => [
|
||||
"comment" => "notifications",
|
||||
"fields" => [
|
||||
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"],
|
||||
"uid" => ["type" => "mediumint unsigned", "foreign" => ["user" => "uid"], "comment" => "Owner User id"],
|
||||
"vid" => ["type" => "smallint unsigned", "foreign" => ["verb" => "id", "on delete" => "restrict"], "comment" => "Id of the verb table entry that contains the activity verbs"],
|
||||
"type" => ["type" => "tinyint unsigned", "comment" => ""],
|
||||
"actor-id" => ["type" => "int unsigned", "foreign" => ["contact" => "id"], "comment" => "Link to the contact table with uid=0 of the actor that caused the notification"],
|
||||
"target-uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Item-uri id of the related post"],
|
||||
"parent-uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Item-uri id of the parent of the related post"],
|
||||
"created" => ["type" => "datetime", "comment" => ""],
|
||||
"seen" => ["type" => "boolean", "default" => "0", "comment" => ""],
|
||||
],
|
||||
"indexes" => [
|
||||
"PRIMARY" => ["id"],
|
||||
"uid_vid_type_actor-id_target-uri-id" => ["UNIQUE", "uid", "vid", "type", "actor-id", "target-uri-id"],
|
||||
"vid" => ["vid"],
|
||||
"actor-id" => ["actor-id"],
|
||||
"target-uri-id" => ["target-uri-id"],
|
||||
"parent-uri-id" => ["parent-uri-id"],
|
||||
"seen_uid" => ["seen", "uid"],
|
||||
]
|
||||
],
|
||||
"notify" => [
|
||||
"comment" => "notifications",
|
||||
"fields" => [
|
||||
|
|
|
@ -196,6 +196,8 @@
|
|||
"parent-author-link" => ["parent-post-author", "url"],
|
||||
"parent-author-name" => ["parent-post-author", "name"],
|
||||
"parent-author-network" => ["parent-post-author", "network"],
|
||||
"parent-author-blocked" => ["parent-post-author", "blocked"],
|
||||
"parent-author-hidden" => ["parent-post-author", "hidden"],
|
||||
],
|
||||
"query" => "FROM `post-user`
|
||||
STRAIGHT_JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-user`.`parent-uri-id` AND `post-thread-user`.`uid` = `post-user`.`uid`
|
||||
|
|
|
@ -34,6 +34,8 @@ trait DatabaseTestTrait
|
|||
StaticDatabase::statConnect($_SERVER);
|
||||
// Rollbacks every DB usage (in case the test couldn't call tearDown)
|
||||
StaticDatabase::statRollback();
|
||||
// Rollback the first, outer transaction just 2 be sure
|
||||
StaticDatabase::getGlobConnection()->rollBack();
|
||||
// Start the first, outer transaction
|
||||
StaticDatabase::getGlobConnection()->beginTransaction();
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ class ExtendedPDO extends PDO
|
|||
{
|
||||
if($this->_transactionDepth <= 0 || !$this->hasSavepoint()) {
|
||||
parent::beginTransaction();
|
||||
$this->_transactionDepth = $this->_transactionDepth < 0 ? 0 : $this->_transactionDepth;
|
||||
$this->_transactionDepth = 0;
|
||||
} else {
|
||||
$this->exec("SAVEPOINT LEVEL{$this->_transactionDepth}");
|
||||
}
|
||||
|
@ -84,14 +84,15 @@ class ExtendedPDO extends PDO
|
|||
*/
|
||||
public function commit()
|
||||
{
|
||||
// We don't want to "really" commit something, so skip the most outer hierarchy
|
||||
if ($this->_transactionDepth <= 1 && $this->hasSavepoint()) {
|
||||
$this->_transactionDepth = $this->_transactionDepth <= 0 ? 0 : 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->_transactionDepth--;
|
||||
|
||||
if($this->_transactionDepth <= 0 || !$this->hasSavepoint()) {
|
||||
parent::commit();
|
||||
$this->_transactionDepth = $this->_transactionDepth < 0 ? 0 : $this->_transactionDepth;
|
||||
} else {
|
||||
$this->exec("RELEASE SAVEPOINT LEVEL{$this->_transactionDepth}");
|
||||
}
|
||||
$this->exec("RELEASE SAVEPOINT LEVEL{$this->_transactionDepth}");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,14 +103,15 @@ class ExtendedPDO extends PDO
|
|||
*/
|
||||
public function rollBack()
|
||||
{
|
||||
if ($this->_transactionDepth <= 0) {
|
||||
throw new PDOException('Rollback error : There is no transaction started');
|
||||
}
|
||||
|
||||
$this->_transactionDepth--;
|
||||
|
||||
if($this->_transactionDepth == 0 || !$this->hasSavepoint()) {
|
||||
parent::rollBack();
|
||||
if($this->_transactionDepth <= 0 || !$this->hasSavepoint()) {
|
||||
$this->_transactionDepth = 0;
|
||||
try {
|
||||
parent::rollBack();
|
||||
} catch (PDOException $e) {
|
||||
// this shouldn't happen, but it does ...
|
||||
}
|
||||
} else {
|
||||
$this->exec("ROLLBACK TO SAVEPOINT LEVEL{$this->_transactionDepth}");
|
||||
}
|
||||
|
|
|
@ -39,6 +39,9 @@ class StaticDatabase extends Database
|
|||
*/
|
||||
private static $staticConnection;
|
||||
|
||||
/** @var bool */
|
||||
private $_locked = false;
|
||||
|
||||
/**
|
||||
* Override the behaviour of connect, due there is just one, static connection at all
|
||||
*
|
||||
|
@ -77,6 +80,31 @@ class StaticDatabase extends Database
|
|||
return true;
|
||||
}
|
||||
|
||||
/** Mock for locking tables */
|
||||
public function lock($table)
|
||||
{
|
||||
if ($this->_locked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->in_transaction = true;
|
||||
$this->_locked = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Mock for unlocking tables */
|
||||
public function unlock()
|
||||
{
|
||||
// See here: https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html
|
||||
$this->performCommit();
|
||||
|
||||
$this->in_transaction = false;
|
||||
$this->_locked = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a commit
|
||||
*
|
||||
|
@ -163,18 +191,6 @@ class StaticDatabase extends Database
|
|||
return self::$staticConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a global commit for every nested transaction of the static connection
|
||||
*/
|
||||
public static function statCommit()
|
||||
{
|
||||
if (isset(self::$staticConnection)) {
|
||||
while (self::$staticConnection->getTransactionDepth() > 0) {
|
||||
self::$staticConnection->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a global rollback for every nested transaction of the static connection
|
||||
*/
|
||||
|
|
97
tests/Util/HTTPInputDataDouble.php
Normal file
97
tests/Util/HTTPInputDataDouble.php
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Test\Util;
|
||||
|
||||
use Friendica\Util\HTTPInputData;
|
||||
|
||||
/**
|
||||
* This class is used to enable testability for HTTPInputData
|
||||
* It overrides the two PHP input functionality with custom content
|
||||
*/
|
||||
class HTTPInputDataDouble extends HTTPInputData
|
||||
{
|
||||
/** @var false|resource */
|
||||
protected static $injectedStream = false;
|
||||
/** @var false|string */
|
||||
protected static $injectedContent = false;
|
||||
/** @var false|string */
|
||||
protected static $injectedContentType = false;
|
||||
|
||||
/**
|
||||
* injects the PHP input stream for a test
|
||||
*
|
||||
* @param false|resource $stream
|
||||
*/
|
||||
public static function setPhpInputStream($stream)
|
||||
{
|
||||
self::$injectedStream = $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* injects the PHP input content for a test
|
||||
*
|
||||
* @param false|string $content
|
||||
*/
|
||||
public static function setPhpInputContent($content)
|
||||
{
|
||||
self::$injectedContent = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* injects the PHP input content type for a test
|
||||
*
|
||||
* @param false|string $contentType
|
||||
*/
|
||||
public static function setPhpInputContentType($contentType)
|
||||
{
|
||||
self::$injectedContentType = $contentType;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
protected static function getPhpInputStream()
|
||||
{
|
||||
return static::$injectedStream;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
protected static function getPhpInputContent()
|
||||
{
|
||||
return static::$injectedContent;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
protected static function getContentType()
|
||||
{
|
||||
return static::$injectedContentType;
|
||||
}
|
||||
|
||||
protected static function fetchFileData($stream, string $boundary, array $headers, string $filename)
|
||||
{
|
||||
$data = parent::fetchFileData($stream, $boundary, $headers, $filename);
|
||||
if (!empty($data['tmp_name'])) {
|
||||
unlink($data['tmp_name']);
|
||||
$data['tmp_name'] = $data['name'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
1
tests/datasets/http/form-urlencoded-json.httpinput
Normal file
1
tests/datasets/http/form-urlencoded-json.httpinput
Normal file
|
@ -0,0 +1 @@
|
|||
{"media_ids":[],"sensitive":false,"status":"Test Status","visibility":"private","spoiler_text":"Title"}
|
1
tests/datasets/http/form-urlencoded.httpinput
Normal file
1
tests/datasets/http/form-urlencoded.httpinput
Normal file
|
@ -0,0 +1 @@
|
|||
title=Test2
|
BIN
tests/datasets/http/multipart-file.httpinput
Normal file
BIN
tests/datasets/http/multipart-file.httpinput
Normal file
Binary file not shown.
50
tests/datasets/http/multipart.httpinput
Normal file
50
tests/datasets/http/multipart.httpinput
Normal file
|
@ -0,0 +1,50 @@
|
|||
--43395968-f65c-437e-b536-5b33e3e3c7e5
|
||||
Content-Disposition: form-data; name="display_name"
|
||||
Content-Transfer-Encoding: binary
|
||||
Content-Type: multipart/form-data; charset=utf-8
|
||||
Content-Length: 9
|
||||
|
||||
User Name
|
||||
--43395968-f65c-437e-b536-5b33e3e3c7e5
|
||||
Content-Disposition: form-data; name="note"
|
||||
Content-Transfer-Encoding: binary
|
||||
Content-Type: multipart/form-data; charset=utf-8
|
||||
Content-Length: 8
|
||||
|
||||
About me
|
||||
--43395968-f65c-437e-b536-5b33e3e3c7e5
|
||||
Content-Disposition: form-data; name="locked"
|
||||
Content-Transfer-Encoding: binary
|
||||
Content-Type: multipart/form-data; charset=utf-8
|
||||
Content-Length: 5
|
||||
|
||||
false
|
||||
--43395968-f65c-437e-b536-5b33e3e3c7e5
|
||||
Content-Disposition: form-data; name="fields_attributes[0][name]"
|
||||
Content-Transfer-Encoding: binary
|
||||
Content-Type: multipart/form-data; charset=utf-8
|
||||
Content-Length: 10
|
||||
|
||||
variable 1
|
||||
--43395968-f65c-437e-b536-5b33e3e3c7e5
|
||||
Content-Disposition: form-data; name="fields_attributes[0][value]"
|
||||
Content-Transfer-Encoding: binary
|
||||
Content-Type: multipart/form-data; charset=utf-8
|
||||
Content-Length: 7
|
||||
|
||||
value 1
|
||||
--43395968-f65c-437e-b536-5b33e3e3c7e5
|
||||
Content-Disposition: form-data; name="fields_attributes[1][name]"
|
||||
Content-Transfer-Encoding: binary
|
||||
Content-Type: multipart/form-data; charset=utf-8
|
||||
Content-Length: 10
|
||||
|
||||
variable 2
|
||||
--43395968-f65c-437e-b536-5b33e3e3c7e5
|
||||
Content-Disposition: form-data; name="fields_attributes[1][value]"
|
||||
Content-Transfer-Encoding: binary
|
||||
Content-Type: multipart/form-data; charset=utf-8
|
||||
Content-Length: 7
|
||||
|
||||
value 2
|
||||
--43395968-f65c-437e-b536-5b33e3e3c7e5--
|
|
@ -189,7 +189,7 @@ class ApiTest extends FixtureTest
|
|||
private function assertXml($result = '', $root_element = '')
|
||||
{
|
||||
self::assertStringStartsWith('<?xml version="1.0"?>', $result);
|
||||
self::assertContains('<' . $root_element, $result);
|
||||
self::assertStringContainsString('<' . $root_element, $result);
|
||||
// We could probably do more checks here.
|
||||
}
|
||||
|
||||
|
@ -1505,7 +1505,7 @@ class ApiTest extends FixtureTest
|
|||
$result = api_search('json');
|
||||
foreach ($result['status'] as $status) {
|
||||
self::assertStatus($status);
|
||||
self::assertContains('reply', $status['text'], '', true);
|
||||
self::assertStringContainsStringIgnoringCase('reply', $status['text'], '', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1521,7 +1521,7 @@ class ApiTest extends FixtureTest
|
|||
$result = api_search('json');
|
||||
foreach ($result['status'] as $status) {
|
||||
self::assertStatus($status);
|
||||
self::assertContains('reply', $status['text'], '', true);
|
||||
self::assertStringContainsStringIgnoringCase('reply', $status['text'], '', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1537,7 +1537,7 @@ class ApiTest extends FixtureTest
|
|||
$result = api_search('json');
|
||||
foreach ($result['status'] as $status) {
|
||||
self::assertStatus($status);
|
||||
self::assertContains('reply', $status['text'], '', true);
|
||||
self::assertStringContainsStringIgnoringCase('reply', $status['text'], '', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1551,7 +1551,7 @@ class ApiTest extends FixtureTest
|
|||
$result = api_search('json');
|
||||
foreach ($result['status'] as $status) {
|
||||
self::assertStatus($status);
|
||||
self::assertContains('#friendica', $status['text'], '', true);
|
||||
self::assertStringContainsStringIgnoringCase('#friendica', $status['text'], '', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2266,6 +2266,7 @@ class ApiTest extends FixtureTest
|
|||
[
|
||||
'network' => 'feed',
|
||||
'title' => 'item_title',
|
||||
'uri-id' => 1,
|
||||
// We need a long string to test that it is correctly cut
|
||||
'body' => 'perspiciatis impedit voluptatem quis molestiae ea qui ' .
|
||||
'reiciendis dolorum aut ducimus sunt consequatur inventore dolor ' .
|
||||
|
@ -2308,11 +2309,12 @@ class ApiTest extends FixtureTest
|
|||
[
|
||||
'network' => 'feed',
|
||||
'title' => 'item_title',
|
||||
'uri-id' => -1,
|
||||
'body' => '',
|
||||
'plink' => 'item_plink'
|
||||
]
|
||||
);
|
||||
self::assertEquals('item_title', $result['text']);
|
||||
self::assertEquals("item_title", $result['text']);
|
||||
self::assertEquals('<h4>item_title</h4><br>item_plink', $result['html']);
|
||||
}
|
||||
|
||||
|
@ -2325,8 +2327,9 @@ class ApiTest extends FixtureTest
|
|||
{
|
||||
$result = api_convert_item(
|
||||
[
|
||||
'title' => 'item_title',
|
||||
'body' => 'item_title item_body'
|
||||
'title' => 'item_title',
|
||||
'body' => 'item_title item_body',
|
||||
'uri-id' => 1,
|
||||
]
|
||||
);
|
||||
self::assertEquals('item_title item_body', $result['text']);
|
||||
|
@ -2874,7 +2877,7 @@ class ApiTest extends FixtureTest
|
|||
$_POST['text'] = 'message_text';
|
||||
$_POST['screen_name'] = $this->friendUser['nick'];
|
||||
$result = api_direct_messages_new('json');
|
||||
self::assertContains('message_text', $result['direct_message']['text']);
|
||||
self::assertStringContainsString('message_text', $result['direct_message']['text']);
|
||||
self::assertEquals('selfcontact', $result['direct_message']['sender_screen_name']);
|
||||
self::assertEquals(1, $result['direct_message']['friendica_seen']);
|
||||
}
|
||||
|
@ -2891,8 +2894,8 @@ class ApiTest extends FixtureTest
|
|||
$_POST['screen_name'] = $this->friendUser['nick'];
|
||||
$_REQUEST['title'] = 'message_title';
|
||||
$result = api_direct_messages_new('json');
|
||||
self::assertContains('message_text', $result['direct_message']['text']);
|
||||
self::assertContains('message_title', $result['direct_message']['text']);
|
||||
self::assertStringContainsString('message_text', $result['direct_message']['text']);
|
||||
self::assertStringContainsString('message_title', $result['direct_message']['text']);
|
||||
self::assertEquals('selfcontact', $result['direct_message']['sender_screen_name']);
|
||||
self::assertEquals(1, $result['direct_message']['friendica_seen']);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
timeoutForLargeTests="900">
|
||||
<testsuite name='friendica'>
|
||||
<directory suffix='.php'>functional/</directory>
|
||||
<directory suffix='.php'>include/</directory>
|
||||
<directory suffix='.php'>legacy/</directory>
|
||||
<directory suffix='.php'>src/</directory>
|
||||
</testsuite>
|
||||
<!-- Filters for Code Coverage -->
|
||||
|
@ -16,14 +16,14 @@
|
|||
<whitelist>
|
||||
<directory suffix=".php">..</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">config/</directory>
|
||||
<directory suffix=".php">doc/</directory>
|
||||
<directory suffix=".php">images/</directory>
|
||||
<directory suffix=".php">library/</directory>
|
||||
<directory suffix=".php">spec/</directory>
|
||||
<directory suffix=".php">tests/</directory>
|
||||
<directory suffix=".php">view/</directory>
|
||||
<directory suffix=".php">vendor/</directory>
|
||||
<directory suffix=".php">../config/</directory>
|
||||
<directory suffix=".php">../doc/</directory>
|
||||
<directory suffix=".php">../images/</directory>
|
||||
<directory suffix=".php">../library/</directory>
|
||||
<directory suffix=".php">../spec/</directory>
|
||||
<directory suffix=".php">../tests/</directory>
|
||||
<directory suffix=".php">../view/</directory>
|
||||
<directory suffix=".php">../vendor/</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
|
|
@ -103,7 +103,7 @@ class InstallerTest extends MockedTest
|
|||
$this->mockL10nT('File Information PHP module', 1);
|
||||
$this->mockL10nT('Error: File Information PHP module required but not installed.', 1);
|
||||
$this->mockL10nT('Program execution functions', 1);
|
||||
$this->mockL10nT('Error: Program execution functions required but not enabled.', 1);
|
||||
$this->mockL10nT('Error: Program execution functions (proc_open) required but not enabled.', 1);
|
||||
}
|
||||
|
||||
private function assertCheckExist($position, $title, $help, $status, $required, $assertionArray)
|
||||
|
@ -248,7 +248,7 @@ class InstallerTest extends MockedTest
|
|||
self::assertFalse($install->checkFunctions());
|
||||
self::assertCheckExist(9,
|
||||
'Program execution functions',
|
||||
'Error: Program execution functions required but not enabled.',
|
||||
'Error: Program execution functions (proc_open) required but not enabled.',
|
||||
false,
|
||||
true,
|
||||
$install->getChecks());
|
||||
|
|
|
@ -81,7 +81,7 @@ class FilesystemStorageTest extends StorageTest
|
|||
public function testMissingDirPermissions()
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionMessageRegExp("/Filesystem storage failed to create \".*\". Check you write permissions./");
|
||||
$this->expectExceptionMessageMatches("/Filesystem storage failed to create \".*\". Check you write permissions./");
|
||||
$this->root->getChild('storage')->chmod(000);
|
||||
|
||||
$instance = $this->getInstance();
|
||||
|
@ -97,7 +97,7 @@ class FilesystemStorageTest extends StorageTest
|
|||
static::markTestIncomplete("Cannot catch file_put_content() error due vfsStream failure");
|
||||
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionMessageRegExp("/Filesystem storage failed to save data to \".*\". Check your write permissions/");
|
||||
$this->expectExceptionMessageMatches("/Filesystem storage failed to save data to \".*\". Check your write permissions/");
|
||||
|
||||
vfsStream::create(['storage' => ['f0' => ['c0' => ['k0i0' => '']]]], $this->root);
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ class BasePathTest extends MockedTest
|
|||
public function testFailedBasePath()
|
||||
{
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessageRegExp("/(.*) is not a valid basepath/");
|
||||
$this->expectExceptionMessageMatches("/(.*) is not a valid basepath/");
|
||||
|
||||
$basepath = new BasePath('/now23452sgfgas', []);
|
||||
$basepath->getPath();
|
||||
|
|
|
@ -59,7 +59,7 @@ class ConfigFileLoaderTest extends MockedTest
|
|||
*/
|
||||
public function testLoadConfigWrong()
|
||||
{
|
||||
$this->expectExceptionMessageRegExp("/Error loading config file \w+/");
|
||||
$this->expectExceptionMessageMatches("/Error loading config file \w+/");
|
||||
$this->expectException(\Exception::class);
|
||||
$this->delConfigFile('local.config.php');
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue