Merge remote-tracking branch 'upstream/develop' into item-view
60
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: Bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
<!--
|
||||
Please fill out this template with all the information you have. The more info you can provide the easier it will be to help you out or fix the problem you are seeing. For trouble with the UI don’t forget to add a screenshot or two. Please do your best!
|
||||
|
||||
Please note that this template is only for bugs. Please use other templates in case of feature requests or support requests
|
||||
|
||||
Lastly, be sure to preview your issue before saving. Thanks!
|
||||
-->
|
||||
|
||||
- [ ] I have searched open and closed issues for duplicates
|
||||
|
||||
<!--
|
||||
You can search all issues here
|
||||
https://github.com/friendica/friendica/issues?utf8=%E2%9C%93&q=is%3Aissue
|
||||
Replace [ ] with [X] once you've searched
|
||||
-->
|
||||
|
||||
### Bug Description
|
||||
|
||||
<!-- Give an overall summary of the issue. -->
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
<!-- Using bullet points, list the steps that reproduce the bug. -->
|
||||
|
||||
1. step one
|
||||
2. step two
|
||||
3. step three
|
||||
|
||||
Actual Result:
|
||||
|
||||
<!-- Describe the details of the buggy behaviour. -->
|
||||
|
||||
Expected Result:
|
||||
|
||||
<!-- Describe in detail what the correct behavior should be. -->
|
||||
|
||||
### Screenshots
|
||||
|
||||
<!-- How to take screenshots on all OSes: https://www.take-a-screenshot.org/
|
||||
You can drag and drop images into this text box. -->
|
||||
|
||||
### Platform Info
|
||||
|
||||
Friendica Version:
|
||||
|
||||
<!-- You can see Friendica’s version number at Help -> About this site -> Site/Friendica Version -->
|
||||
|
||||
Friendica Source:
|
||||
|
||||
PHP version:
|
||||
|
||||
SQL version:
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Friendica Community Support
|
||||
url: https://forum.friendi.ca/
|
||||
about: Please ask and answer questions here.
|
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: New Feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Is the feature request related to a problem? Please describe.
|
||||
|
||||
<!-- A clear and concise description of what the problem is. Like `I'm always frustrated when [...]`
|
||||
|
||||
### Describe the feature you'd like
|
||||
|
||||
<!-- A clear and concise description of waht you want to happen. -->
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
<!-- A clear and concise description of any alternative solutions or feature you've considered. -->
|
||||
|
||||
### Additional context
|
10
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
name: Question / Support
|
||||
about: Select this if you have a question
|
||||
title: ''
|
||||
labels: Support Request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# For general question about Friendica, please try to find a solution at https://wiki.friendi.ca first.
|
15
.github/issue_template.md
vendored
|
@ -1,15 +0,0 @@
|
|||
### Expected behavior
|
||||
|
||||
### Actual behavior
|
||||
|
||||
### Steps to reproduce the problem
|
||||
|
||||
### Friendica version you encountered the problem
|
||||
|
||||
see `example.com/friendica` on your Friendica node for the version information.
|
||||
|
||||
### Friendica source (git, zip)
|
||||
|
||||
### PHP version
|
||||
|
||||
### SQL version
|
101
.github/workflows/php.yml
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
name: Testing Friendica
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
friendica:
|
||||
name: Friendica (PHP ${{ matrix.php-versions }})
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:latest
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: test
|
||||
MYSQL_PASSWORD: test
|
||||
MYSQL_USER: test
|
||||
ports:
|
||||
- 3306/tcp
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379/tcp
|
||||
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
memcached:
|
||||
image: memcached
|
||||
ports:
|
||||
- 11211/tcp
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['7.2', '7.3', '7.4']
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP, with composer and extensions
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: pecl, composer:v1
|
||||
extensions: pdo_mysql, gd, zip, opcache, ctype, pcntl, ldap, apcu, memcached, redis, imagick, memcache
|
||||
coverage: xdebug
|
||||
ini-values: apc.enabled=1, apc.enable_cli=1
|
||||
|
||||
- name: Start mysql service
|
||||
run: sudo /etc/init.d/mysql start
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate
|
||||
|
||||
- name: Get composer cache directory
|
||||
id: composercache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composercache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist
|
||||
|
||||
- name: Copy default Friendica config
|
||||
run: cp config/local-sample.config.php config/local.config.php
|
||||
|
||||
- name: Verify MariaDB connection
|
||||
env:
|
||||
PORT: ${{ job.services.mariadb.ports[3306] }}
|
||||
run: |
|
||||
while ! mysqladmin ping -h"127.0.0.1" -P"$PORT" --silent; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
- name: Setup MYSQL database
|
||||
env:
|
||||
PORT: ${{ job.services.mariadb.ports[3306] }}
|
||||
run: |
|
||||
mysql -h"127.0.0.1" -P"$PORT" -utest -ptest test < database.sql
|
||||
|
||||
- name: Test with Parallel-lint
|
||||
run: vendor/bin/parallel-lint --exclude vendor/ --exclude view/asset/ .
|
||||
|
||||
- name: Test with phpunit
|
||||
run: vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml
|
||||
env:
|
||||
MYSQL_HOST: 127.0.0.1
|
||||
MYSQL_PORT: ${{ job.services.mariadb.ports[3306] }}
|
||||
MYSQL_DATABASE: test
|
||||
MYSQL_PASSWORD: test
|
||||
MYSQL_USER: test
|
||||
REDIS_PORT: ${{ job.services.redis.ports[6379] }}
|
||||
REDIS_HOST: 127.0.0.1
|
||||
MEMCACHED_PORT: ${{ job.services.memcached.ports[11211] }}
|
||||
MEMCACHE_PORT: ${{ job.services.memcached.ports[11211] }}
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
file: clover.xml
|
4
.gitignore
vendored
|
@ -71,8 +71,8 @@ venv/
|
|||
/addons
|
||||
/addon
|
||||
|
||||
#ignore .htaccess
|
||||
.htaccess
|
||||
#ignore base .htaccess
|
||||
/.htaccess
|
||||
|
||||
#ignore filesystem storage default path
|
||||
/storage
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# This file is meant to be copied to ".htaccess" on Apache-powered web servers.
|
||||
# The created .htaccess file can be edited manually and will not be overwritten by Friendica updates.
|
||||
|
||||
Options -Indexes
|
||||
AddType application/x-java-archive .jar
|
||||
AddType audio/ogg .oga
|
||||
|
|
31
.travis.yml
|
@ -1,31 +0,0 @@
|
|||
---
|
||||
language: php
|
||||
## Friendica officially supports PHP version >= 7.1
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- redis
|
||||
- memcached
|
||||
env:
|
||||
- MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USERNAME=travis MYSQL_PASSWORD="" MYSQL_DATABASE=test
|
||||
|
||||
install:
|
||||
- composer install
|
||||
before_script:
|
||||
- cp config/local-sample.config.php config/local.config.php
|
||||
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
|
||||
- mysql -utravis test < database.sql
|
||||
- pecl channel-update pecl.php.net
|
||||
- pecl config-set preferred_state beta
|
||||
- phpenv config-add .travis/redis.ini
|
||||
- phpenv config-add .travis/memcached.ini
|
||||
|
||||
script:
|
||||
- vendor/bin/parallel-lint --exclude vendor/ --exclude view/asset/ .
|
||||
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml
|
||||
|
||||
after_success: bash <(curl -s https://codecov.io/bash)
|
|
@ -1,4 +0,0 @@
|
|||
extension="apcu.so"
|
||||
|
||||
apc.enabled = 1
|
||||
apc.enable_cli = 1
|
|
@ -1 +0,0 @@
|
|||
extension="memcached.so"
|
|
@ -1 +0,0 @@
|
|||
extension="redis.so"
|
272
CHANGELOG
|
@ -1,11 +1,269 @@
|
|||
Version 2020.06 (unreleased)
|
||||
Version 2021.03 (unreleased)
|
||||
Friendica Core
|
||||
Removed the frontend worker [annando]
|
||||
|
||||
Friendica Addons
|
||||
|
||||
Closed Issues
|
||||
|
||||
Version 2021.01 (2021-01-04)
|
||||
Friendica Core
|
||||
Added HU translation
|
||||
Updates to the translations: DE, IT, RU [translation teams]
|
||||
Updates to the themes (duepuntozero, frio, vier) [annando, MrPetovan, tobiasd, vinzv]
|
||||
General Code cleanup [annando, MrPetovan, nupplaphil]
|
||||
Enhanced the handling of permission sets [annando]
|
||||
Enhanced the usage of system resources when displaying photos and updating contacts [annando]
|
||||
Enhanced the database structure [annando, Quix0r]
|
||||
Enhanced the detection of PeerTube servers [annando]
|
||||
Enhanced the photo cache [annando]
|
||||
Enhanced the import of old postings which would otherwise not be imported due their age [annando]
|
||||
Enhanced the delivery process of ActivityPub content [annando]
|
||||
Enhanced the performance profiler [annando]
|
||||
Enhanced the background worker [annando]
|
||||
Enhanced the handling of blocked authors [MrPetovan]
|
||||
Enhanced the user management in the admin panel [MrPetovan]
|
||||
Enhanced the process of expiring postings [annando]
|
||||
Enhanced the un/follow process of contacts [annando]
|
||||
Enhanced the handling of HTTP requests [nupplaphil]
|
||||
Enhanced filter possibilities of contacts [annando]
|
||||
Enhanced language detection of postings [annando]
|
||||
Enhanced the admin panel [MrPetovan, tobiasd]
|
||||
Enhanced the contact suggestions [annando]
|
||||
Enhanced the community page (filter, tags) [annando]
|
||||
Enhanced the display of the reason why a posting is displayed in a stream [annando]
|
||||
Enhanced the forum delivery of postings [redmatrix]
|
||||
Enhanced PHP8 compatibility [annando]
|
||||
Enhanced the worker_cooldown mechanism [annando]
|
||||
Added new options to the remote_self feature [annando]
|
||||
Added API endpoints for accounts and trends [annando]
|
||||
Added API endpoints for re-sharing of postings [annando]
|
||||
Added provider fields to the API [annando]
|
||||
Added the possibility to map $_SERVER variables during installation [nupplaphil]
|
||||
Added the possibility to filter account types on the network page [annando]
|
||||
Added missing Mastodon API endpoints as "unsupported" [annando]
|
||||
Added a watchdog mode to check if the daemon is running [annando]
|
||||
Added number of group members to the contact widget [annando]
|
||||
Added endless scrolling in several places [annando]
|
||||
Added an option to stay local when clicking on a contact profile [annando]
|
||||
Added support of ActivityPub relays [annando]
|
||||
Model\User::getAuthenticationInfo is now available for addons [MrPetovan]
|
||||
Contact details can only edited for mail and feed contacts [annando]
|
||||
Fixed some problems during the export of user data [annando]
|
||||
Fixed various problems with the notification system [MrPetovan]
|
||||
Fixed a problem with emoticon alt-text interpretation [MrPetovan]
|
||||
Fixed a problem that caused comments on Tweets being distributed via ActivityPub [annando]
|
||||
Fixed a problem with the auto-completion when composing comments [MrPetovan]
|
||||
Fixed an ACL problem while poking contacts [MrPetovan]
|
||||
Fixed a problem with Mastodon emoticons [MrPetovan]
|
||||
Fixed a parser problem that caused additional <br> tags [annando]
|
||||
Fixed escaping of several HTML snippets [MrPetovan]
|
||||
Fixed a problem with fetching objects by URL [annando]
|
||||
|
||||
Friendica Addons
|
||||
Updated to the translations IT, HU [translation teams]
|
||||
advancedcontentfilter:
|
||||
Added examples [hoergen]
|
||||
blackout:
|
||||
Improved the wording in the admin interface [urbalazs]
|
||||
catavatar:
|
||||
Improved the generation of avatars [annando]
|
||||
ifttt:
|
||||
Added support for delayed postings [annando]
|
||||
mailstream:
|
||||
Improved code structure [nupplaphil]
|
||||
Fix case-sensitive check by [nupplaphil]
|
||||
markdown:
|
||||
Improved parsing [MrPetovan]
|
||||
newmemberwidget:
|
||||
Improved addon description [SpencerDub]
|
||||
langfilter:
|
||||
Changed the input to use a slider [MrPetovan]
|
||||
ldapauth:
|
||||
Reworked the authentication code [MrPetovan]
|
||||
libravatar:
|
||||
Fixed a problem with DNS requests [annando]
|
||||
Improved the list of available avatars [annando]
|
||||
phpmailer:
|
||||
Fixed UTF8 encoding problems [MrPetovan]
|
||||
rendertime:
|
||||
Added more information about the "other" things that cost time [annando]
|
||||
showmore:
|
||||
Improved handling of the HTML structure of postings [MrPetovan]
|
||||
showmore_dyn:
|
||||
Improved user settings, language [MrPetovan]
|
||||
twitter:
|
||||
Improved logging [annando]
|
||||
Improved the twitter_post_hook [MrPetovan]
|
||||
Improved the posts send to twitter [annando]
|
||||
Improved the remote_self functionality [annando]
|
||||
Added support for delayed postings [annando]
|
||||
Fixed a bug with direct re-shares [MrPetovan]
|
||||
|
||||
Closed Issues
|
||||
2803, 4230, 4486, 4494, 5616, 7393, 7697, 8485, 8533, 8605, 8689,
|
||||
8796, 8896, 8943, 8950, 9042, 9089, 9127, 9142, 9165, 9235, 9236,
|
||||
9238, 9249, 9264, 9268, 9276, 9281, 9291, 9296, 9305, 9306, 9315,
|
||||
9326, 9328, 9329, 9337, 9344, 9348, 9363, 9383, 9385, 9407, 9427,
|
||||
9430, 9432, 9457, 9461, 9464, 9465, 9480, 9486, 9496, 9508, 9518,
|
||||
9525, 9538, 9549, 9564, 9568, 9573, 9598, 9611, 9622, 9629, 9630,
|
||||
9633, 9636, 9639, 9641, 9642, 9662, 9672, 9673, 9678, 9682, 9692,
|
||||
9712
|
||||
|
||||
Version 2020.09-1 (2020-09-24)
|
||||
Friendica Core:
|
||||
Updates to the translations: RU [translation teams]
|
||||
Enhanced forum delivery using attached mention tags [redmatrix]
|
||||
Enhanced code test-ability [nupplaphil]
|
||||
Enhanced character set detection when parsing URLs [MrPetovan]
|
||||
Enhanced the Activity Pub relay functionality [annando]
|
||||
Added phpseclib dependency to replace standalone ASN1 library [nupplaphil]
|
||||
Fixed a bug generating Message-IDs for notification mails [nupplaphil]
|
||||
Fixed missing uri-ids in the database [annando]
|
||||
Fixed a display problem with the new re-shares [annando]
|
||||
|
||||
Friendica Addons:
|
||||
showmore_dyn:
|
||||
New addon to collapse long post depending on their actual height [wiwie]
|
||||
nominatim:
|
||||
Added addon to resolve coordinates with OpenStreetmap [annando]
|
||||
phpmailer:
|
||||
Fixed a bug that prevented notification mails being send [nupplaphil]
|
||||
|
||||
Closed Issues:
|
||||
9142, 9264
|
||||
|
||||
Version 2020.09 (2020-09-20)
|
||||
Friendica Core:
|
||||
Updates to the translations: DE, EN GB, EN US, ES, FR, IT, NL, PL, RU, ZH_CN [translation teams]
|
||||
Updates to the themes (all) [MrPetovan, tobiasd]
|
||||
Updates to the documentation [annando, mpanhans, realkinetix, tobiasd]
|
||||
General code cleanup and refactoring [annando, MrPetovan, nupplaphil]
|
||||
Enhanced the API [annando]
|
||||
Enhanced the processing of background jobs [annando]
|
||||
Enhanced federation of activities [annando, vpzomtrrfrt]
|
||||
Enhanced the user notifications[annando]
|
||||
Enhanced database usage [annando, MrPetovan]
|
||||
Enhanced ActivityPub support for forums [annando]
|
||||
Enhanced the utilization of the cache [annando, MrPetovan]
|
||||
Enhanced the performance of the daemon [annando]
|
||||
Enhanced the communication with the directory servers [annando]
|
||||
Enhanced the re-sharing of items [annando]
|
||||
Enhanced sample lighttpd and nginx configs [MrPetovan, tobiasd]
|
||||
Enhanced the checks for incoming postings using ActivityPub [annando, Roger Meyer]
|
||||
Enhanced the import of RSS feeds by removing tracking pixels [annando]
|
||||
Enhanced the speed of the full text search [annando]
|
||||
Replaced library used for text completion [MrPetovan]
|
||||
Fixed a problem that prevented recipients of direct messages to be selected [MrPetovan]
|
||||
Fixed a problem that prevented new email contacts from being added [annando]
|
||||
Fixed a problem with the console command search [tobiasd]
|
||||
Fixed a problem during the search for contacts [annando]
|
||||
Fixed a problem with the JOT of private notes [MrPetovan]
|
||||
Fixed missing HTML encoding [MrPetovan]
|
||||
Fixed a layout problem with the frio composer for new postings [MrPetovan]
|
||||
Fixed some composer notices [nupplaphil]
|
||||
Fixed a problem for empty preview data when importing feed posts [annando]
|
||||
Fixed a problem with the pager on search result pages [annando]
|
||||
Fixed some templates to show the correct un-/follow button for contacts [annando]
|
||||
Fixed a problem with the generation of the Message-ID of notification emails [nupplaphil]
|
||||
Added nodeinfo2 support [annando]
|
||||
Added CSV export and import of blocked servers to the console [tobiasd]
|
||||
Added new admin debug module for ActivityPub [MrPetovan]
|
||||
Added the automatic determination of frequency to pull feeds [annando]
|
||||
Added signed fetching from system users for ActivityPub [annando]
|
||||
Added the discovery of new peers from contacts [annando]
|
||||
Added the directory API endpoint [annando]
|
||||
Added support for signed outbox requests [annando]
|
||||
Added direction functionality for clarification of posting flow [annando]
|
||||
Added the ability to set the database version [annando]
|
||||
Added support for ActivityPub relay server [annando]
|
||||
By default display of re-sharer information is now flattened [annando]
|
||||
Removed some unused POCO functionality [annando]
|
||||
Removed the unused rating functionality [annando]
|
||||
Removed unneeded network request for local stuff [annando]
|
||||
Removed some useless info messages [annando]
|
||||
Reworked some additional features according to a user voting [MrPetovan]
|
||||
|
||||
Friendica Addons:
|
||||
Updates to the translations: DE, EN GB, EN US, IT, NL, RU, ZH_CN [translation teams]
|
||||
Updates to the docs [SpencerDub]
|
||||
General code cleanup and maintenance [annando, MrPetovan]
|
||||
blockbot:
|
||||
added some "good" bots [annando]
|
||||
forumdirectory:
|
||||
fixed some SQL queries [MrPetovan]
|
||||
phpmailer:
|
||||
fixed a problem leading to double message ID headers [nupplaphil]
|
||||
qcomment:
|
||||
restructured the addon and fixed a bug preventing the addon from working [MrPetovan]
|
||||
|
||||
Closed Issues:
|
||||
2811, 4606, 5742, 5782, 7660, 8676, 8788, 8797, 8798, 8847, 8860,
|
||||
8874, 8882, 8885, 8906, 8914, 8922, 8928, 8929, 8935, 8940, 8941,
|
||||
8956, 8958, 8961, 8967, 8989, 8993, 8994, 8995, 8997, 8999, 9000,
|
||||
9004, 9013, 9015, 9051, 9064, 9065, 9072, 9081, 9090, 9091, 9099,
|
||||
9107, 9135, 9136, 9137, 9138, 9140, 9142, 9150, 9153, 9154, 9163,
|
||||
9164, 9172, 9182, 9192, 9193, 9204, 9210, 9229, 9231, 9246
|
||||
|
||||
Version 2020.07-1 (2020-09-08)
|
||||
Friendica Core
|
||||
Fixed a problem that leaked sensitive information [Roger Meyer, MrPetovan]
|
||||
|
||||
Version 2020.07 (2020-07-12)
|
||||
Friendica Core:
|
||||
Update to the translations: DE, EN GB, EN US, FR, ET, NL, PL, RU, ZH-CN [translation teams]
|
||||
Updates to the themes (frio, vier) [MrPetovan]
|
||||
Updated the shipped composer version, and the dependency list [annando, MrPetovan, tobiasd]
|
||||
Updates to the documentation [MrPetovan]
|
||||
General code refactoring and enhancements [AlfredSK, annando, MrPetovan]
|
||||
Replace charged terms with "allowlist", "denylist" and "blocklist" [MrPetovan]
|
||||
Enhanced the comment distribution in threads that involve diaspora*, AP and DFRN actors [annando]
|
||||
Enhanced the profile probing mechanism [annando, MrPetovan]
|
||||
Enhanced the post update process of the database [annando]
|
||||
Enhanced the database performance [annando]
|
||||
Enhanced ActivityPub attachment handling [MrPetovan]
|
||||
Enhanced security of redirections [annando]
|
||||
Enhanced database performance [annando]
|
||||
Enhanced the handling of BBCode [pre] tags [MrPetovan]
|
||||
Enhanced Markdown to BBCode conversion [MrPetovan]
|
||||
Enhanced the speed of the network page [annando]
|
||||
Fixed a problem recognising logins via the API [MrPetovan]
|
||||
Fixed a problem with handling local diaspora* URLs [MrPetovan]
|
||||
Fixed a problem with implicit mentions [annando]
|
||||
Fixed a problem with the password reset token security [lynn-stephenson]
|
||||
Fixed a problem with receiving non-public posts via ActivityPub [annando]
|
||||
Fixed a problem with the photo endpoint of the API [MrPetovan]
|
||||
Fixed a problem with pressing the ESC key in the frio-theme [MrPetovan]
|
||||
Fixed a problem with the display if post categories [annando]
|
||||
Fixed a problem with validation of feeds [annando]
|
||||
Fixed a problem that prevented AP activities being fetched sometimes [annando]
|
||||
Renamed the -q option of the console user delete command to -y [MrPetovan]
|
||||
Added notification count to page title [MrPetovan]
|
||||
Added handling of relative URLs during feed detection [MrPetovan]
|
||||
Added entities [nupplaphil]
|
||||
|
||||
Friendica Addons:
|
||||
Update to the translations (EN GB, NB NO, NL, PL, RU, ZH CN) [translation teams]
|
||||
blockbot:
|
||||
The list of accepted user agents was enhanced [annando]
|
||||
Diaspora*:
|
||||
Enhanced conntector settings [MrPetovan]
|
||||
PHP Mailer SMTP:
|
||||
Updated phpmailer version [dependabot]
|
||||
showmore_dyn:
|
||||
New addon to collapse long post depending on their actual height [wiwie]
|
||||
twitter:
|
||||
Enhaceed the handling of mobile twitter URLs [annando]
|
||||
Enhanced the handling of quoted tweets [MrPetovan]
|
||||
added HTML error code handling [MrPetovan]
|
||||
various:
|
||||
enhancements to the probe mechanism [MrPetovan]
|
||||
|
||||
Closed Issues:
|
||||
3084, 3884, 8287, 8314, 8374, 8400, 8425, 8432, 8458, 8470, 8477,
|
||||
8482, 8488, 8489, 8490, 8493, 8495, 8498, 8511, 8517, 8523, 8527,
|
||||
8551, 8553, 8560, 8564, 8565, 8568, 8578, 8586, 8593, 8606, 8610,
|
||||
8612, 8626, 8664, 8672, 8683, 8685, 8691, 8694, 8702, 8709, 8714,
|
||||
8717, 8722, 8726, 8732, 8736, 8743, 8744, 8746, 8756, 8766, 8769,
|
||||
8781, 8800, 8807, 8808, 8827, 8829, 8836, 8844, 8846, 8857, 8866
|
||||
|
||||
Version 2020.03 "Red Hot Poker" (2020-03-30)
|
||||
Friendica Core:
|
||||
|
@ -61,7 +319,7 @@ Version 2020.03 "Red Hot Poker" (2020-03-30)
|
|||
Update to the translations (CS, DE, FR, PL, RU, ZH-CN) [translation teams]
|
||||
General code refactoring and enhancements [AndyHee, annando, MrPetovan, nupplaphil]
|
||||
blockbot:
|
||||
Ensure that good agents are whitelisted [valvin1]
|
||||
Ensure that good agents are allowlisted [valvin1]
|
||||
markdown:
|
||||
Addon to use Markdown while composing a posting was added [annando]
|
||||
showmore:
|
||||
|
@ -617,7 +875,7 @@ Version 2018.09 (2018-09-23)
|
|||
Version 2018.05 (2018-06-01)
|
||||
Friendica Core:
|
||||
Update to the translations (DE, EN-GB, EN-US, FI, IS, IT, NL, PL, RU, ZN CH) [translation teams]
|
||||
Update to the documentation [andyhee, annando, fabrixxm, M-arcus, MrPedovan, rudloff, tobiasd]
|
||||
Update to the documentation [andyhee, annando, fabrixxm, M-arcus, MrPetovan, rudloff, tobiasd]
|
||||
Enhancements to the DB handling [annando]
|
||||
Enhancements to the relay system [annando]
|
||||
Enhancements to the handling of URL that contain unicode characters [annando]
|
||||
|
@ -911,7 +1169,7 @@ Version 3.5.3 (2017-10-05)
|
|||
Updates to the documentation [tobiasd]
|
||||
Code revision and refactoring [Hypolite]
|
||||
pumpio, twitter bridges adopted to new background mechanism [annando]
|
||||
Leistungsschutzrecht has a new source list, and a whitelist [annando]
|
||||
Leistungsschutzrecht has a new source list, and an allowlist [annando]
|
||||
retriever marked unsupported due to unwanted side-effects [annando]
|
||||
Unicode emoji added [annando]
|
||||
Enhancement to the general content filter [annando]
|
||||
|
@ -1373,7 +1631,7 @@ Version 3.3.1 (2014-11-06)
|
|||
Set default location to empty for new users. Suppress warning on user creation (issue #1193) (fabrixxm)
|
||||
Correctly build urls with queries (issue #1190) (fabrixxm)
|
||||
Optionally use keywords in feed as post tags with "remote self" (annando)
|
||||
A blacklist of keywords to not use can be defined (annando)
|
||||
A denylist of keywords to not use can be defined (annando)
|
||||
"remote self" works also with Friendica and Diaspora contacts (annando)
|
||||
Show exact post time after 12 hours (FX7)
|
||||
Optionally redirect from non-SSL to SSL (annando)
|
||||
|
|
20
CREDITS.txt
|
@ -9,6 +9,7 @@ Aditoo
|
|||
AgnesElisa
|
||||
Albert
|
||||
Alberto Díaz Tormo
|
||||
Aleksandr "M.O.Z.G" Dikov
|
||||
Alex
|
||||
Alexander An
|
||||
Alexander Fortin
|
||||
|
@ -34,6 +35,7 @@ Athalbert
|
|||
aweiher
|
||||
axelt
|
||||
balderino
|
||||
Balázs Úr
|
||||
Beanow
|
||||
beardyunixer
|
||||
Beatriz Vital
|
||||
|
@ -54,6 +56,7 @@ Chris Case
|
|||
Christian González
|
||||
Christian M. Grube
|
||||
Christian Vogeley
|
||||
Christian Wiwie
|
||||
Cohan Robinson
|
||||
Copiis Praeesse
|
||||
CrystalStiletto
|
||||
|
@ -113,7 +116,6 @@ Hypolite Petovan
|
|||
Ilmari
|
||||
ImgBotApp
|
||||
irhen
|
||||
Jak
|
||||
Jakob
|
||||
Jens Tautenhahn
|
||||
jensp
|
||||
|
@ -121,6 +123,7 @@ Jeroen De Meerleer
|
|||
jeroenpraat
|
||||
Joan Bar
|
||||
JOduMonT
|
||||
joe slam
|
||||
Johannes Schwab
|
||||
John Brazil
|
||||
Jonatan Nyberg
|
||||
|
@ -131,6 +134,8 @@ julia.domagalska
|
|||
Julio Cova
|
||||
Karel
|
||||
Karolina
|
||||
Kastal András
|
||||
Keenan Pepper
|
||||
Keith Fernie
|
||||
Klaus Weidenbach
|
||||
Koyu Berteon
|
||||
|
@ -141,10 +146,11 @@ Leberwurscht
|
|||
Leonard Lausen
|
||||
Lionel Triay
|
||||
loma-one
|
||||
loma1
|
||||
Lorem Ipsum
|
||||
Ludovic Grossard
|
||||
Lynn Stephenson
|
||||
maase2
|
||||
Magdalena Gazda
|
||||
Mai Anh Nguyen
|
||||
Manuel Pérez Monís
|
||||
Marcin Klessa
|
||||
|
@ -153,7 +159,6 @@ Marcus Müller
|
|||
Marie Olive
|
||||
Mariusz Pisz
|
||||
marmor
|
||||
Marquis_de_Carabas
|
||||
Martin Schmitt
|
||||
Mateusz Mikos
|
||||
Mats Sjöberg
|
||||
|
@ -169,9 +174,11 @@ Michal Šupler
|
|||
Michalina
|
||||
Mike Macgirvin
|
||||
miqrogroove
|
||||
mpanhans
|
||||
mytbk
|
||||
nathilia-peirce
|
||||
Nicola Spanti
|
||||
nobody
|
||||
Olaf Conradi
|
||||
Oliver
|
||||
Olivier
|
||||
|
@ -208,6 +215,7 @@ repat
|
|||
Ricardo Pereira
|
||||
Rik 4
|
||||
RJ Madsen
|
||||
Roger Meyer
|
||||
Roland Häder
|
||||
Rui Andrada
|
||||
rwa
|
||||
|
@ -219,23 +227,22 @@ Samuli Valavuo
|
|||
Sandro Santilli
|
||||
Sebastian Egbers
|
||||
sella
|
||||
Sennewood
|
||||
Seth
|
||||
Silke Meyer
|
||||
Simon L'nu
|
||||
Simó Albert i Beltran
|
||||
softmetz
|
||||
soko1
|
||||
SpencerDub
|
||||
Spencer Dub
|
||||
St John Karp
|
||||
Stanislav N.
|
||||
Steffen K9
|
||||
StefOfficiel
|
||||
steve jobs
|
||||
Sveinn í Felli
|
||||
Sven Anders
|
||||
Sylke Vicious
|
||||
Sylvain Lagacé
|
||||
szymon.filip
|
||||
Sérgio Lima
|
||||
Taekus
|
||||
Tazman DeVille
|
||||
|
@ -263,7 +270,6 @@ U-SOUND\mike
|
|||
ufic
|
||||
Ulf Rompe
|
||||
Unknown
|
||||
Valvin
|
||||
Valvin A
|
||||
Vasudev Kamath
|
||||
Vasya Novikov
|
||||
|
|
|
@ -40,3 +40,7 @@ Have a look at the [installation documentation](doc/Install.md) for further info
|
|||
|*Vier theme, desktop browser. Public timeline view.*|
|
||||
|![Vier theme in desktop browser](images/screenshots/friendica-vier-community.png?raw=true "Vier theme in desktop browser")
|
||||
|*Vier theme, desktop browser. Community post displayed.*|
|
||||
|
||||
## Endorsements
|
||||
|
||||
- [![Awesome Humane Tech](images/humane-tech-badge.svg)](https://github.com/humanetech-community/awesome-humane-tech) On August 12th 2020, Friendica was added to [the curated Awesome Humane Tech directory](https://github.com/humanetech-community/awesome-humane-tech) in [the "Fediverse" category](https://github.com/humanetech-community/awesome-humane-tech#fediverse).
|
||||
|
|
10
bin/.htaccess
Normal file
|
@ -0,0 +1,10 @@
|
|||
# This file prevents browser access to Friendica command-line scripts on Apache-powered web servers.
|
||||
# It isn't meant to be edited manually, please check the base Friendica folder for the .htaccess-dist file instead.
|
||||
|
||||
<IfModule authz_host_module>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !authz_host_module>
|
||||
Order Allow,Deny
|
||||
Deny from all
|
||||
</IfModule>
|
|
@ -51,9 +51,14 @@
|
|||
*
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
||||
exit();
|
||||
}
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Util\ExAuth;
|
||||
use Friendica\Security\ExAuth;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
if (sizeof($_SERVER["argv"]) == 0) {
|
||||
|
@ -80,6 +85,7 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['auth_ejabb
|
|||
$appMode = $dice->create(Mode::class);
|
||||
|
||||
if ($appMode->isNormal()) {
|
||||
$oAuth = new ExAuth();
|
||||
/** @var ExAuth $oAuth */
|
||||
$oAuth = $dice->create(ExAuth::class);
|
||||
$oAuth->readStdin();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
*
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
||||
exit();
|
||||
}
|
||||
|
||||
use Dice\Dice;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
|
|
|
@ -23,11 +23,18 @@
|
|||
* This script was taken from http://php.net/manual/en/function.pcntl-fork.php
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
||||
exit();
|
||||
}
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
// Get options
|
||||
|
@ -59,6 +66,8 @@ if (DI::mode()->isInstall()) {
|
|||
die("Friendica isn't properly installed yet.\n");
|
||||
}
|
||||
|
||||
DI::mode()->setExecutor(Mode::DAEMON);
|
||||
|
||||
DI::config()->load();
|
||||
|
||||
if (empty(DI::config()->get('system', 'pidfile'))) {
|
||||
|
@ -138,34 +147,35 @@ Logger::notice('Starting worker daemon.', ["pid" => $pid]);
|
|||
if (!$foreground) {
|
||||
echo "Starting worker daemon.\n";
|
||||
|
||||
// Switch over to daemon mode.
|
||||
if ($pid = pcntl_fork()) {
|
||||
return; // Parent
|
||||
}
|
||||
|
||||
fclose(STDIN); // Close all of the standard
|
||||
|
||||
// Enabling this seem to block a running php process with 100% CPU usage when there is an outpout
|
||||
// fclose(STDOUT); // file descriptors as we
|
||||
// fclose(STDERR); // are running as a daemon.
|
||||
|
||||
DBA::disconnect();
|
||||
|
||||
// Fork a daemon process
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
echo "Daemon couldn't be forked.\n";
|
||||
Logger::warning('Could not fork daemon');
|
||||
exit(1);
|
||||
} elseif ($pid) {
|
||||
// The parent process continues here
|
||||
echo 'Child process started with pid ' . $pid . ".\n";
|
||||
Logger::notice('Child process started', ['pid' => $pid]);
|
||||
file_put_contents($pidfile, $pid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// We now are in the child process
|
||||
register_shutdown_function('shutdown');
|
||||
|
||||
// Make the child the main process, detach it from the terminal
|
||||
if (posix_setsid() < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($pid = pcntl_fork()) {
|
||||
return; // Parent
|
||||
}
|
||||
// Closing all existing connections with the outside
|
||||
fclose(STDIN);
|
||||
|
||||
$pid = getmypid();
|
||||
file_put_contents($pidfile, $pid);
|
||||
|
||||
// We lose the database connection upon forking
|
||||
DBA::reconnect();
|
||||
// And now connect the database again
|
||||
DBA::connect();
|
||||
}
|
||||
|
||||
DI::config()->set('system', 'worker_daemon_mode', true);
|
||||
|
@ -185,7 +195,12 @@ while (true) {
|
|||
$do_cron = true;
|
||||
}
|
||||
|
||||
if ($do_cron || (!DI::process()->isMaxLoadReached() && Worker::entriesExists() && Worker::isReady())) {
|
||||
Worker::spawnWorker($do_cron);
|
||||
} else {
|
||||
Logger::info('Cool down for 5 seconds', ['pid' => $pid]);
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
if ($do_cron) {
|
||||
// We force a reconnect of the database connection.
|
||||
|
@ -195,8 +210,9 @@ while (true) {
|
|||
$last_cron = time();
|
||||
}
|
||||
|
||||
Logger::info("Sleeping", ["pid" => $pid]);
|
||||
$start = time();
|
||||
Logger::info("Sleeping", ["pid" => $pid, 'until' => gmdate(DateTimeFormat::MYSQL, $start + $wait_interval)]);
|
||||
|
||||
do {
|
||||
$seconds = (time() - $start);
|
||||
|
||||
|
@ -204,9 +220,14 @@ while (true) {
|
|||
// Background: After jobs had been started, they often fork many workers.
|
||||
// To not waste too much time, the sleep period increases.
|
||||
$arg = (($seconds + 1) / ($wait_interval / 9)) + 1;
|
||||
$sleep = round(log10($arg) * 1000000, 0);
|
||||
$sleep = min(1000000, round(log10($arg) * 1000000, 0));
|
||||
usleep($sleep);
|
||||
|
||||
$pid = pcntl_waitpid(-1, $status, WNOHANG);
|
||||
if ($pid > 0) {
|
||||
Logger::info('Children quit via pcntl_waitpid', ['pid' => $pid, 'status' => $status]);
|
||||
}
|
||||
|
||||
$timeout = ($seconds >= $wait_interval);
|
||||
} while (!$timeout && !Worker::IPCJobsExists());
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ dontinclude = ['root', 'friendica', 'bavatar', 'tony baldwin', 'Taek', 'silke m'
|
|||
path = os.path.abspath(argv[0].split('bin/dev/make_credits.py')[0])
|
||||
print('> base directory is assumed to be: '+path)
|
||||
# a place to store contributors
|
||||
contributors = ["Andi Stadler", "Ratten", "Vít Šesták 'v6ak'"]
|
||||
contributors = ["Andi Stadler", "Ratten", "Roger Meyer", "Vít Šesták 'v6ak'"]
|
||||
# get the contributors
|
||||
print('> getting contributors to the friendica core repository')
|
||||
p = subprocess.Popen(['git', 'shortlog', '--no-merges', '-s'],
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
*
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
||||
exit();
|
||||
}
|
||||
|
||||
if (($_SERVER["argc"] > 1) && isset($_SERVER["argv"][1])) {
|
||||
echo $_SERVER["argv"][1];
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
* Usage: php bin/wait-for-connection {HOST} {PORT} [{TIMEOUT}]
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
||||
exit();
|
||||
}
|
||||
|
||||
$timeout = 60;
|
||||
switch ($argc) {
|
||||
case 4:
|
||||
|
|
|
@ -21,8 +21,14 @@
|
|||
* Starts the background processing
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
||||
exit();
|
||||
}
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\App;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Update;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\DI;
|
||||
|
@ -53,6 +59,8 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['worker']])
|
|||
DI::init($dice);
|
||||
$a = DI::app();
|
||||
|
||||
DI::mode()->setExecutor(Mode::WORKER);
|
||||
|
||||
// Check the database structure and possibly fixes it
|
||||
Update::check($a->getBasePath(), true, DI::mode());
|
||||
|
||||
|
@ -76,4 +84,4 @@ Worker::processQueue($run_cron);
|
|||
|
||||
Worker::unclaimProcess();
|
||||
|
||||
Worker::endProcess();
|
||||
DI::process()->end();
|
||||
|
|
41
boot.php
|
@ -38,7 +38,7 @@ use Friendica\Util\DateTimeFormat;
|
|||
|
||||
define('FRIENDICA_PLATFORM', 'Friendica');
|
||||
define('FRIENDICA_CODENAME', 'Red Hot Poker');
|
||||
define('FRIENDICA_VERSION', '2020.06-dev');
|
||||
define('FRIENDICA_VERSION', '2021.03-dev');
|
||||
define('DFRN_PROTOCOL_VERSION', '2.23');
|
||||
define('NEW_UPDATE_ROUTINE_VERSION', 1170);
|
||||
|
||||
|
@ -201,6 +201,7 @@ define('PRIORITY_HIGH', 20);
|
|||
define('PRIORITY_MEDIUM', 30);
|
||||
define('PRIORITY_LOW', 40);
|
||||
define('PRIORITY_NEGLIGIBLE', 50);
|
||||
define('PRIORITIES', [PRIORITY_CRITICAL, PRIORITY_HIGH, PRIORITY_MEDIUM, PRIORITY_LOW, PRIORITY_NEGLIGIBLE]);
|
||||
/* @}*/
|
||||
|
||||
/**
|
||||
|
@ -253,10 +254,10 @@ function public_contact()
|
|||
if (!$public_contact_id && !empty($_SESSION['authenticated'])) {
|
||||
if (!empty($_SESSION['my_address'])) {
|
||||
// Local user
|
||||
$public_contact_id = intval(Contact::getIdForURL($_SESSION['my_address'], 0, true));
|
||||
$public_contact_id = intval(Contact::getIdForURL($_SESSION['my_address'], 0, false));
|
||||
} elseif (!empty($_SESSION['visitor_home'])) {
|
||||
// Remote user
|
||||
$public_contact_id = intval(Contact::getIdForURL($_SESSION['visitor_home'], 0, true));
|
||||
$public_contact_id = intval(Contact::getIdForURL($_SESSION['visitor_home'], 0, false));
|
||||
}
|
||||
} elseif (empty($_SESSION['authenticated'])) {
|
||||
$public_contact_id = false;
|
||||
|
@ -266,7 +267,7 @@ function public_contact()
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns contact id of authenticated site visitor or false
|
||||
* Returns public contact id of authenticated site visitor or false
|
||||
*
|
||||
* @return int|bool visitor_id or false
|
||||
*/
|
||||
|
@ -382,38 +383,6 @@ function is_site_admin()
|
|||
return local_user() && $admin_email && in_array($a->user['email'] ?? '', $adminlist);
|
||||
}
|
||||
|
||||
function explode_querystring($query)
|
||||
{
|
||||
$arg_st = strpos($query, '?');
|
||||
if ($arg_st !== false) {
|
||||
$base = substr($query, 0, $arg_st);
|
||||
$arg_st += 1;
|
||||
} else {
|
||||
$base = '';
|
||||
$arg_st = 0;
|
||||
}
|
||||
|
||||
$args = explode('&', substr($query, $arg_st));
|
||||
foreach ($args as $k => $arg) {
|
||||
/// @TODO really compare type-safe here?
|
||||
if ($arg === '') {
|
||||
unset($args[$k]);
|
||||
}
|
||||
}
|
||||
$args = array_values($args);
|
||||
|
||||
if (!$base) {
|
||||
$base = $args[0];
|
||||
unset($args[0]);
|
||||
$args = array_values($args);
|
||||
}
|
||||
|
||||
return [
|
||||
'base' => $base,
|
||||
'args' => $args,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete URL of the current page, e.g.: http(s)://something.com/network
|
||||
*
|
||||
|
|
|
@ -34,34 +34,39 @@
|
|||
"league/html-to-markdown": "^4.8",
|
||||
"level-2/dice": "^4",
|
||||
"lightopenid/lightopenid": "dev-master",
|
||||
"matriphe/iso-639": "^1.2",
|
||||
"michelf/php-markdown": "^1.7",
|
||||
"mobiledetect/mobiledetectlib": "^2.8",
|
||||
"monolog/monolog": "^1.25",
|
||||
"nikic/fast-route": "^1.3",
|
||||
"paragonie/hidden-string": "^1.0",
|
||||
"patrickschur/language-detection": "^3.4",
|
||||
"pear/console_table": "^1.3",
|
||||
"pear/text_languagedetect": "1.*",
|
||||
"phpseclib/phpseclib": "^2.0",
|
||||
"pragmarx/google2fa": "^5.0",
|
||||
"pragmarx/recovery": "^0.1.0",
|
||||
"psr/container": "^1.0",
|
||||
"seld/cli-prompt": "^1.0",
|
||||
"smarty/smarty": "^3.1",
|
||||
"xemlock/htmlpurifier-html5": "^0.1.11",
|
||||
"fxp/composer-asset-plugin": "^1.4",
|
||||
"bower-asset/base64": "^1.0",
|
||||
"bower-asset/chart-js": "^2.8",
|
||||
"bower-asset/dompurify": "^1.0",
|
||||
"bower-asset/fork-awesome": "^1.1",
|
||||
"bower-asset/vue": "^2.6",
|
||||
"npm-asset/cropperjs": "1.2.2",
|
||||
"npm-asset/es-jquery-sortable": "^0.9.13",
|
||||
"npm-asset/fullcalendar": "^3.10",
|
||||
"npm-asset/imagesloaded": "4.1.4",
|
||||
"npm-asset/jquery": "^2.0",
|
||||
"npm-asset/jquery-colorbox": "^1.6",
|
||||
"npm-asset/jquery-datetimepicker": "^2.5",
|
||||
"npm-asset/jgrowl": "^1.4",
|
||||
"npm-asset/moment": "^2.24",
|
||||
"npm-asset/fullcalendar": "^3.10",
|
||||
"npm-asset/cropperjs": "1.2.2",
|
||||
"npm-asset/imagesloaded": "4.1.4",
|
||||
"npm-asset/typeahead.js": "^0.11.1",
|
||||
"bower-asset/fork-awesome": "^1.1"
|
||||
"npm-asset/perfect-scrollbar": "0.6.16",
|
||||
"npm-asset/textcomplete": "^0.18.2",
|
||||
"npm-asset/typeahead.js": "^0.11.1"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
|
@ -74,14 +79,10 @@
|
|||
"Friendica\\": "src/",
|
||||
"Friendica\\Addon\\": "addon/"
|
||||
},
|
||||
"psr-0": {
|
||||
"": "library/"
|
||||
},
|
||||
"files": [
|
||||
"include/conversation.php",
|
||||
"include/dba.php",
|
||||
"include/enotify.php",
|
||||
"include/items.php",
|
||||
"boot.php"
|
||||
]
|
||||
},
|
||||
|
@ -91,6 +92,9 @@
|
|||
}
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.0"
|
||||
},
|
||||
"autoloader-suffix": "Friendica",
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
|
@ -124,7 +128,7 @@
|
|||
"mikey179/vfsstream": "^1.6",
|
||||
"mockery/mockery": "^1.2",
|
||||
"johnkary/phpunit-speedtrap": "1.1",
|
||||
"jakub-onderka/php-parallel-lint": "^1.0"
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit"
|
||||
|
|
1136
composer.lock
generated
1159
database.sql
|
@ -15,10 +15,13 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en
|
|||
|
||||
## Implemented endpoints
|
||||
|
||||
- [`GET /api/v1//accounts/:id`](https://docs.joinmastodon.org/methods/accounts/#retrieve-information)
|
||||
- [`GET /api/v1//accounts/:id/statuses`](https://docs.joinmastodon.org/methods/accounts/#retrieve-information)
|
||||
- [`GET /api/v1/custom_emojis`](https://docs.joinmastodon.org/methods/instance/custom_emojis/)
|
||||
- Doesn't return unicode emojis since they aren't using an image URL
|
||||
|
||||
|
||||
- [`GET /api/v1/directory`](https://docs.joinmastodon.org/methods/instance/directory/)
|
||||
- [`GET /api/v1/follow_requests`](https://docs.joinmastodon.org/methods/accounts/follow_requests#pending-follows)
|
||||
- Returned IDs are specific to follow requests
|
||||
- [`POST /api/v1/follow_requests/:id/authorize`](https://docs.joinmastodon.org/methods/accounts/follow_requests#accept-follow)
|
||||
|
@ -33,6 +36,8 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en
|
|||
|
||||
- [`GET /api/v1/instance`](https://docs.joinmastodon.org/methods/instance#fetch-instance)
|
||||
- [`GET /api/v1/instance/peers`](https://docs.joinmastodon.org/methods/instance#list-of-connected-domains)
|
||||
- [`GET /api/v1/timelines/public`](https://docs.joinmastodon.org/methods/timelines/)
|
||||
- [`GET /api/v1/trends`](https://docs.joinmastodon.org/methods/instance/trends/)
|
||||
|
||||
## Non-implemented endpoints
|
||||
|
||||
|
|
|
@ -152,19 +152,29 @@ These endpoints use the [Friendica API entities](help/API-Entities).
|
|||
- [GET api/friendships/incoming](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friendships-incoming)
|
||||
- Unsupported parameters
|
||||
- `stringify_ids`
|
||||
- [GET api/followers/ids](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-followers-ids)
|
||||
- Unsupported parameters:
|
||||
- `user_id`: Relationships aren't returned for other users than self
|
||||
- `screen_name`: Relationships aren't returned for other users than self
|
||||
- [GET api/friends/ids](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friends-ids)
|
||||
- Unsupported parameters:
|
||||
- `user_id`: Relationships aren't returned for other users than self
|
||||
- `screen_name`: Relationships aren't returned for other users than self
|
||||
|
||||
- - [GET api/followers/ids](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-followers-ids)
|
||||
- [GET api/followers/list](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-followers-list)
|
||||
- [GET api/friends/ids](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friends-ids)
|
||||
- [GET api/friends/list](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friends-list)
|
||||
- Additional parameters:
|
||||
- `since_id`: You can use the `next_cursor` value to load the next page.
|
||||
- `max_id`: You can use the inverse of the `previous_cursor` value to load the previous page.
|
||||
- Unsupported parameter:
|
||||
- `skip_status`: No status is returned even if it isn't set to true.
|
||||
- Caveats:
|
||||
- `cursor` trumps `since_id` trumps `max_id` if any combination is provided.
|
||||
- `user_id` must be the ID of a contact associated with a local user account.
|
||||
- `screen_name` must be associated with a local user account.
|
||||
- `screen_name` trumps `user_id` if both are provided (undocumented Twitter behavior).
|
||||
- Will succeed but return an empty array for users hiding their contact lists.
|
||||
|
||||
|
||||
- [POST api/friendships/destroy](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/post-friendships-destroy)
|
||||
|
||||
|
||||
|
||||
|
||||
## Non-implemented endpoints
|
||||
|
||||
- [GET oauth/authenticate](https://developer.twitter.com/en/docs/basics/authentication/api-reference/authenticate)
|
||||
|
@ -188,8 +198,6 @@ These endpoints use the [Friendica API entities](help/API-Entities).
|
|||
- [POST lists/subscribers/destroy](https://developer.twitter.com/en/docs/accounts-and-users/create-manage-lists/api-reference/post-lists-subscribers-destroy)
|
||||
|
||||
|
||||
- [GET followers/list](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-followers-list)
|
||||
- [GET friends/list](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friends-list)
|
||||
- [GET friendships/lookup](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friendships-lookup)
|
||||
- [GET friendships/no_retweets/ids](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friendships-no_retweets-ids)
|
||||
- [GET friendships/outgoing](https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friendships-outgoing)
|
||||
|
|
|
@ -466,6 +466,19 @@ Hook data is a `\FastRoute\RouterCollector` object that should be used to add ad
|
|||
|
||||
**Notice**: The class whose name is provided in the route handler must be reachable via auto-loader.
|
||||
|
||||
### probe_detect
|
||||
|
||||
Called before trying to detect the target network of a URL.
|
||||
If any registered hook function sets the `result` key of the hook data array, it will be returned immediately.
|
||||
Hook functions should also return immediately if the hook data contains an existing result.
|
||||
|
||||
Hook data:
|
||||
|
||||
- **uri** (input): the profile URI.
|
||||
- **network** (input): the target network (can be empty for auto-detection).
|
||||
- **uid** (input): the user to return the contact data for (can be empty for public contacts).
|
||||
- **result** (output): Set by the hook function to indicate a successful detection.
|
||||
|
||||
## Complete list of hook callbacks
|
||||
|
||||
Here is a complete list of all hook callbacks with file locations (as of 24-Sep-2018). Please see the source for details of any hooks not documented above.
|
||||
|
@ -505,10 +518,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
|
|||
Hook::callAll('item_photo_menu', $args);
|
||||
Hook::callAll('jot_tool', $jotplugins);
|
||||
|
||||
### include/items.php
|
||||
|
||||
Hook::callAll('page_info_data', $data);
|
||||
|
||||
### mod/directory.php
|
||||
|
||||
Hook::callAll('directory_item', $arr);
|
||||
|
@ -595,10 +604,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
|
|||
|
||||
Hook::callAll('post_local_end', $arr);
|
||||
|
||||
### mod/lockview.php
|
||||
|
||||
Hook::callAll('lockview_content', $item);
|
||||
|
||||
### mod/uexport.php
|
||||
|
||||
Hook::callAll('uexport_options', $options);
|
||||
|
@ -670,6 +675,10 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
|
|||
Hook::callAll('register_account', $uid);
|
||||
Hook::callAll('remove_user', $user);
|
||||
|
||||
### src/Module/PermissionTooltip.php
|
||||
|
||||
Hook::callAll('lockview_content', $item);
|
||||
|
||||
### src/Content/ContactBlock.php
|
||||
|
||||
Hook::callAll('contact_block_end', $arr);
|
||||
|
|
|
@ -65,17 +65,17 @@ table.bbcodes > * > tr > th {
|
|||
<td><a href="http://friendi.ca" target="external-link">Friendica</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[img]https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg[/img]</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg" alt="Immagine/foto"></td>
|
||||
<td>[img]https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg[/img]</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg" alt="Immagine/foto"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[img=https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg]The Friendica Logo[/img]</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg" alt="The Friendica Logo"></td>
|
||||
<td>[img=https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg]The Friendica Logo[/img]</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg" alt="The Friendica Logo"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[img=64x32]https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg[/img]<br>
|
||||
<td>[img=64x32]https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg[/img]<br>
|
||||
<br>Note: provided height is simply discarded.</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg" style="width: 64px;"></td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg" style="width: 64px;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[size=xx-small]small text[/size]</td>
|
||||
|
@ -502,10 +502,6 @@ You can embed video, audio and more in a message.
|
|||
<td>[embed]URL[/embed]</td>
|
||||
<td>Embed OEmbed rich content.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[iframe]URL[/iframe]</td>
|
||||
<td>General embed, iframe size is limited by the theme size for video players.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[url]*url*[/url]</td>
|
||||
<td>If *url* supports oembed or opengraph specifications the embedded object will be shown (eg, documents from scribd).
|
||||
|
@ -613,15 +609,34 @@ On Mastodon this field is used for the content warning.
|
|||
<th>Result</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>If you need to put literal bbcode in a message, [noparse], [nobb] or [pre] are used to escape bbcode:
|
||||
<td>If you need to put literal BBCode in a message, [noparse], [nobb] or [pre] blocks prevent BBCode conversion:
|
||||
<ul>
|
||||
<li>[noparse][b]bold[/b][/noparse]</li>
|
||||
<li>[nobb][b]bold[/b][/nobb]</li>
|
||||
<li>[pre][b]bold[/b][/pre]</li>
|
||||
</ul>
|
||||
Note: [code] has priority over [noparse], [nobb] and [pre] which makes them display as BBCode tags in code blocks instead of being removed.
|
||||
[code] blocks inside [noparse] will still be converted to a code block.
|
||||
</td>
|
||||
<td>[b]bold[/b]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Additionally, [noparse] and [pre] blocks prevent mention and hashtag conversion to links:
|
||||
<ul>
|
||||
<li>[noparse]@user@domain.tld #hashtag[/noparse]</li>
|
||||
<li>[pre]@user@domain.tld #hashtag[/pre]</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>@user@domain.tld #hashtag</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Additionally, [pre] blocks preserve spaces:
|
||||
<ul>
|
||||
<li>[pre] Spaces[/pre]</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td> Spaces</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[nosmile] is used to disable smilies on a post by post basis<br>
|
||||
<br>
|
||||
|
|
|
@ -43,7 +43,7 @@ At first you have to get the current version. You can either pull it from [Githu
|
|||
|
||||
$> cd /var/www/virtual/YOURSPACE/html/addon; git pull
|
||||
|
||||
Or you can download a tar archive here: [jappixmini.tgz](https://github.com/friendica/friendica-addons/blob/master/jappixmini.tgz) (click at „view raw“).
|
||||
Or you can download a tar archive here: [jappixmini.tgz](https://github.com/friendica/friendica-addons/blob/stable/jappixmini.tgz) (click at „view raw“).
|
||||
|
||||
Just unpack the file and rename the directory to „jappixmini“.
|
||||
Next, upload this directory and the .tgz-file into your addon directory of your friendica installation.
|
||||
|
|
|
@ -4,49 +4,46 @@ Forums
|
|||
* [Home](help)
|
||||
|
||||
|
||||
Friendica also lets you create forums and/or celebrity accounts.
|
||||
Friendica also lets you create community forums and other types of accounts that can function as discussion forums, celebrity accounts, announcement channels, news reflectors, or organization pages, depending on how you want to interact with others. Management of these pages can be delegated to other accounts, or a parent account can be designated to easily toggle multiple identities.
|
||||
|
||||
Every page in Friendica has a nickname and these must all be unique.
|
||||
This applies to all forums, whether they are normal profiles or forum profiles.
|
||||
Every page in Friendica has a nickname and these must all be unique. This applies to all forums, whether they are normal profiles or forum profiles.
|
||||
|
||||
Therefore the first thing you need to do to create a new forum is to register a new account for the forum.
|
||||
Please note that the site administrator can restrict and/or regulate the registration of new accounts.
|
||||
|
||||
If you create a second account on a system and use the same email address or OpenID account as an existing account, you will no longer be able to use the email address (or OpenID) to log in to the account.
|
||||
You should log in using the account nickname instead.
|
||||
|
||||
On the new account, visit the 'Settings' page.
|
||||
Towards the end of the page are "Advanced Account/Page Type Settings".
|
||||
Typically you would use "Normal Account" for a normal personal account.
|
||||
This is the default selection.
|
||||
Community Forum/Celebrity Accounts provide the ability for people to become friends/fans of the forum without requiring approval.
|
||||
|
||||
The exact setting you would use depends on how you wish to interact with people who join the page.
|
||||
The "Soapbox" setting lets the page owner control all communications.
|
||||
Everything you post will go out to the forum members, but there will be no opportunity for interaction.
|
||||
This setting would typically be used for announcements or corporate communications.
|
||||
|
||||
The most common setting is the "Community Forum".
|
||||
This creates a forum page where all members can freely interact.
|
||||
|
||||
The "Automatic Friend Account" is typically used for personal profile forums where you wish to automatically approve any friendship/connection requests.
|
||||
|
||||
Managing Multiple forums
|
||||
Managing Accounts
|
||||
---
|
||||
|
||||
We recommend that you create group forums with the same email address and password as your normal account.
|
||||
If you do this, you will find a new "Manage" tab on the menu bar which lets you toggle identities easily and manage your forums.
|
||||
You are not required to do this, but the alternative is to log out and log back into the other account to manage alternate forums.
|
||||
This could get cumbersome if you manage several different forums/identities.
|
||||
To create a new linked account that can be used as a forum, log in to your normal account and go to Settings > Manage Accounts.
|
||||
Here you can register additional accounts with new nicknames that will be linked to your primary account.
|
||||
|
||||
You may also appoint a delegate to manage your forum.
|
||||
Do this by visiting the [Delegation Setup Page](settings/delegation).
|
||||
This will provide you with a list of contacts on this system under "Potential Delegates".
|
||||
You may appoint a delegate to manage your new account (e.g. forum page).
|
||||
The Delegates section of Manage Accounts page will provide you with a list of contacts on this instance under "Potential Delegates".
|
||||
Selecting one or more persons will give them access to manage your forum.
|
||||
They will be able to edit contacts, profiles, and all content for this account/page.
|
||||
Please use this facility wisely.
|
||||
Delegated managers will not be able to alter basic account settings such as passwords or page types and/or remove the account.
|
||||
Delegated managers will not be able to alter basic account settings, such as passwords or page types, or remove the account.
|
||||
|
||||
Additionally, this page is also where you can choose to designate an account as a parent user.
|
||||
If your primary account is designated as the parent user, you will be able to easily toggle identities and manage your forums or other types of accounts.
|
||||
|
||||
Types of Accounts
|
||||
---
|
||||
|
||||
On the new account, visit the Settings > Account page.
|
||||
Towards the end of the page is a section for "Advanced account types".
|
||||
Typically you would use "Personal Page - Standard" for a normal personal account with manual approval of “friends” and “followers.”
|
||||
This is the default selection.
|
||||
On this page you can change the type of account if desired.
|
||||
|
||||
The other subtypes of a Personal Page are “Soapbox” and “Love-all.”
|
||||
A Soapbox account is an announcement channel that automatically approvals follower requests.
|
||||
Everything posted by the account will go out to the followers, but there will be no opportunity for interaction.
|
||||
This setting would typically be used for announcements or corporate communications.
|
||||
“Love-all” automatically approves contacts as friends.
|
||||
|
||||
In addition to Personal Page, there are options for Organization Page, News Page, and Community Forum.
|
||||
Organization and New Pages automatically approve contact requests as followers.
|
||||
|
||||
Community Forum provide the ability for people to become friends/fans of the forum without requiring approval.
|
||||
This creates a forum page where all members can freely interact.
|
||||
|
||||
Posting to Community forums
|
||||
---
|
||||
|
|
|
@ -22,7 +22,7 @@ Our Git Branches
|
|||
|
||||
There are two relevant branches in the main repo on GitHub:
|
||||
|
||||
1. master: This branch contains stable releases only.
|
||||
1. stable: This branch contains stable releases only.
|
||||
2. develop: This branch contains the latest code.
|
||||
This is what you want to work with.
|
||||
|
||||
|
@ -43,7 +43,7 @@ Release branches
|
|||
A release branch is created when the develop branch contains all features it should have.
|
||||
A release branch is used for a few things.
|
||||
|
||||
1. It allows last-minute bug fixing before the release goes to master branch.
|
||||
1. It allows last-minute bug fixing before the release goes to stable branch.
|
||||
2. It allows meta-data changes (README, CHANGELOG, etc.) for version bumps and documentation changes.
|
||||
3. It makes sure the develop branch can receive new features that are **not** part of this release.
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ Friendica Documentation and Resources
|
|||
* [Using SSL with Friendica](help/SSL)
|
||||
* [Config values that can only be set in config/local.config.php](help/Config)
|
||||
* [Improve Performance](help/Improve-Performance)
|
||||
* [Migrate](help/Migrate)
|
||||
* [Administration Tools](help/tools)
|
||||
|
||||
**Developer Manual**
|
||||
|
|
|
@ -33,7 +33,7 @@ The account will expire after 7 days, but you can ask the server admin to keep y
|
|||
* Apache with mod-rewrite enabled and "Options All" so you can use a local `.htaccess` file
|
||||
* PHP 7+ (PHP 7.1+ is recommended for performance and official support)
|
||||
* PHP *command line* access with register_argc_argv set to true in the php.ini file
|
||||
* Curl, GD, PDO, MySQLi, hash, xml, zip and OpenSSL extensions
|
||||
* Curl, GD, PDO, mbstrings, MySQLi, hash, xml, zip and OpenSSL extensions
|
||||
* The POSIX module of PHP needs to be activated (e.g. [RHEL, CentOS](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) have disabled it)
|
||||
* some form of email server or email gateway such that PHP mail() works
|
||||
* MySQL 5.6+ or an equivalent alternative for MySQL (MariaDB, Percona Server etc.)
|
||||
|
@ -47,7 +47,6 @@ For alternative server configurations (such as Nginx server and MariaDB database
|
|||
### Optional
|
||||
|
||||
* PHP ImageMagick extension (php-imagick) for animated GIF support.
|
||||
* [Composer](https://getcomposer.org/) for a git install
|
||||
|
||||
## Installation procedure
|
||||
|
||||
|
@ -61,6 +60,8 @@ If this is nothing for you, you might be interested in
|
|||
|
||||
### Get Friendica
|
||||
|
||||
Download the full archive of the stable release of Friendica core and the addons from [the project homepage](https://friendi.ca/resources/download-files/).
|
||||
Make sure that the version of the Friendica archive and the addons match.
|
||||
Unpack the Friendica files into the root of your web server document area.
|
||||
|
||||
If you copy the directory tree to your webserver, make sure that you also copy `.htaccess-dist` - as "dot" files are often hidden and aren't normally copied.
|
||||
|
@ -72,7 +73,7 @@ This makes the software much easier to update.
|
|||
|
||||
The Linux commands to clone the repository into a directory "mywebsite" would be
|
||||
|
||||
git clone https://github.com/friendica/friendica.git -b master mywebsite
|
||||
git clone https://github.com/friendica/friendica.git -b stable mywebsite
|
||||
cd mywebsite
|
||||
bin/composer.phar install --no-dev
|
||||
|
||||
|
@ -88,7 +89,7 @@ Get the addons by going into your website folder.
|
|||
|
||||
Clone the addon repository (separately):
|
||||
|
||||
git clone https://github.com/friendica/friendica-addons.git -b master addon
|
||||
git clone https://github.com/friendica/friendica-addons.git -b stable addon
|
||||
|
||||
If you want to use the development version of Friendica you can switch to the develop branch in the repository by running
|
||||
|
||||
|
@ -435,7 +436,7 @@ provided by one of our members.
|
|||
>
|
||||
> This is obvious as soon as you notice that the friendica-cron uses `proc_open`
|
||||
> to execute PHP scripts that also use `proc_open`, but it took me quite some time to find that out.
|
||||
> I hope this saves some time for other people using suhosin with function blacklists.
|
||||
> I hope this saves some time for other people using suhosin with function blocklists.
|
||||
|
||||
### Unable to create all mysql tables on MySQL 5.7.17 or newer
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@ Friendica Message Flow
|
|||
This page documents some of the details of how messages get from one person to another in the Friendica network.
|
||||
There are multiple paths, using multiple protocols and message formats.
|
||||
|
||||
Those attempting to understand these message flows should become familiar with (at the minimum) the [DFRN protocol document](https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf) and the message passing elements of the OStatus stack (salmon and Pubsubhubbub).
|
||||
|
||||
Most message passing involves the file include/items.php, which has functions for several feed-related import/export activities.
|
||||
Those attempting to understand these message flows should become familiar with (at the minimum) the [DFRN protocol document](https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf) and the message passing elements of the OStatus stack (salmon and Pubsubhubbub).
|
||||
|
||||
When a message is posted, all immediate deliveries to all networks are made using include/notifier.php, which chooses how (and to whom) to deliver the message.
|
||||
This file also invokes the local side of all deliveries including DFRN-notify.
|
||||
|
|
92
doc/Migrate.md
Normal file
|
@ -0,0 +1,92 @@
|
|||
Migrating to a new server installation
|
||||
===============
|
||||
|
||||
* [Home](help)
|
||||
|
||||
## Preparation
|
||||
|
||||
### New server
|
||||
Set up your new server as described [here](Install); follow the installation procedure until you have created a database.
|
||||
|
||||
### Heads up to users
|
||||
Inform your users of an upcoming interruption to your service.
|
||||
To ensure data consistency, your server needs to be offline during some steps of the migration processes.
|
||||
|
||||
You may also find these addons useful for communicating with your users prior to the migration process:
|
||||
* blackout
|
||||
* notifyall
|
||||
|
||||
### Storage
|
||||
Check your storage backend with ``bin/console storage list`` in the root folder.
|
||||
The output should look like this:
|
||||
````
|
||||
Sel | Name
|
||||
-----------------------
|
||||
| Filesystem
|
||||
* | Database
|
||||
````
|
||||
|
||||
If you are *not* using ``Database`` run the following commands:
|
||||
1. ``bin/console storage set Database`` to activate the database backend.
|
||||
2. ``bin/console storage move`` to initiate moving the stored image files.
|
||||
|
||||
This process may take a long time depending on the size of your storage and your server's capacity.
|
||||
Prior to initiating this process, you may want to check the number of files in the storage with the following command: ``tree -if -I index.html /path/to/storage/``.
|
||||
|
||||
### Cleaning up
|
||||
Before transferring your database, you may want to clean it up; ensure the expiration of database items is set to a reasonable value and activated via the administrator panel.
|
||||
*Admin* > *Site* > *Performance* > Enable "Clean up database"
|
||||
After adjusting these settings, the database cleaning up processes will be initiated according to your configured daily cron job.
|
||||
|
||||
To review the size of your database, log into MySQL with ``mysql -p`` run the following query:
|
||||
````
|
||||
SELECT table_schema AS "Database", SUM(data_length + index_length) / 1024 / 1024 / 1024 AS "Size (GB)" FROM information_schema.TABLES GROUP BY table_schema;
|
||||
````
|
||||
|
||||
You should see an output like this:
|
||||
````
|
||||
+--------------------+----------------+
|
||||
| Database | Size (GB) |
|
||||
+--------------------+----------------+
|
||||
| friendica_db | 8.054092407227 |
|
||||
| [..........] | [...........] |
|
||||
+--------------------+----------------+
|
||||
````
|
||||
|
||||
Finally, you may also want to optimise your database with the following command: ``mysqloptimize -p friendica-db``
|
||||
|
||||
### Going offline
|
||||
Stop background tasks and put your server in maintenance mode.
|
||||
1. If you had set up a worker cron job like this ``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/worker.php`` run ``crontab -e`` and comment out this line. Alternatively if you deploy a worker daemon, disable this instead.
|
||||
2. Put your server into maintenance mode: ``bin/console maintenance 1 "We are currently upgrading our system and will be back soon."``
|
||||
|
||||
## Dumping DB
|
||||
Export your database: ``mysqldump -p friendica_db > friendica_db-$(date +%Y%m%d).sql`` and possibly compress it.
|
||||
|
||||
## Transferring to new server
|
||||
Transfer your database and a copy of your configuration file ``config/local.config.php.copy`` to your new server installation.
|
||||
|
||||
## Restoring your DB
|
||||
Import your database on your new server: ``mysql -p friendica_db < your-friendica_db-file.sql``
|
||||
|
||||
## Completing migration
|
||||
|
||||
### Configuration file
|
||||
Copy your old server's configuration file to ``config/local.config.php``.
|
||||
Ensure the newly created database credentials are identical to the setting in the configuration file; otherwise update them accordingly.
|
||||
|
||||
### Cron job for worker
|
||||
Set up the required daily cron job.
|
||||
Run ``crontab -e`` and add the following line according to your system specification
|
||||
``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/worker.php``
|
||||
|
||||
### DNS settings
|
||||
Adjust your DNS records by pointing them to your new server.
|
||||
|
||||
## Troubleshooting
|
||||
If you are unable to login to your newly migrated Friendica installation, check your web server's error and access logs and mysql logs for obvious issues.
|
||||
|
||||
If still unable to resolve the problem, it's likely an issue with your [installation](Install).
|
||||
In this case, you may try to an entirely new Friendica installation on your new server, but use a different FQDN and DNS name.
|
||||
Once you have this up and running, take it offline and purge the database and configuration file and try migrating to this installation.
|
||||
|
21
doc/README.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
# About the docs of the Friendica Project
|
||||
|
||||
**Note**: It is expected that some of the links in these files wont work in the Friendica repository as they are supposed to work on an installed Friendica node.
|
||||
|
||||
## User and Admin documentation
|
||||
|
||||
Every Friendica node has the _current_ version of the user and admin documentation available in the `/help` location.
|
||||
The documentation is mainly done in English, but the pages can be translated and some are already to German.
|
||||
If you want to help expanding the documentation or the translation, please register an account at the [Friendica wiki](https://wiki.friendi.ca) where the [texts are maintained](https://wiki.friendi.ca/docs).
|
||||
The documentation is periodically merged back from there to the _development_ branch of Friendica.
|
||||
|
||||
Images that you use in the documentation should be located in the `img` sub-directory of this directory.
|
||||
Translations are located in sub-directories named after the language codes, e.g. `de`.
|
||||
Depending on the selected interface language the different translations will be applied, or the `en` original will be used as a fall-back.
|
||||
|
||||
## Developers Documentation
|
||||
|
||||
We provide a configuration file for [Doxygen](https://www.doxygen.nl/index.html) in the root of the Friendica repository.
|
||||
With that you should be able to extract some documentation from the source code.
|
||||
|
||||
In addition there are some documentation files about the database structure in `doc`db`.
|
|
@ -74,7 +74,7 @@ You can chose between the following modes:
|
|||
##### Invitation based registry
|
||||
|
||||
Additionally to the setting in the admin panel, you can decide if registrations are only possible using an invitation code or not.
|
||||
To enable invitation based registration, you have to set the `invitation_only` setting in the [config/local.config.php](/help/Config) file.
|
||||
To enable invitation based registration, you have to set the `invitation_only` setting to `true` in the `system` section of the [config/local.config.php](/help/Config) file.
|
||||
If you want to use this method, the registration policy has to be set to either *open* or *requires approval*.
|
||||
|
||||
#### Check Full Names
|
||||
|
@ -182,7 +182,7 @@ By default, any (valid) email address is allowed in registrations.
|
|||
|
||||
#### Allow Users to set remote_self
|
||||
|
||||
If you enable the `Allow Users to set remote_self` users can select Atom feeds from their contact list being their *remote self* in the advanced contact settings.
|
||||
If you enable the `Allow Users to set remote_self` users can select Atom feeds from their contact list being their *remote self* in the contact settings.
|
||||
Which means that postings by the remote self are automatically reposted by Friendica in their names.
|
||||
|
||||
This feature can be used to let the user mirror e.g. blog postings into their Friendica postings.
|
||||
|
@ -240,15 +240,9 @@ This section allows you to configure the background process that is triggered by
|
|||
The process does check the available system resources before creating a new worker for a task.
|
||||
Because of this, it may happen that the maximum number of worker processes you allow will not be reached.
|
||||
|
||||
If your server setup does not allow you to use the `proc_open` function of PHP, please disable it in this section.
|
||||
|
||||
The tasks for the background process have priorities.
|
||||
To guarantee that important tasks are executed even though the system has a lot of work to do, it is useful to enable the *fastlane*.
|
||||
|
||||
Should you not be able to run a cron job on your server, you can also activate the *frontend* worker.
|
||||
If you have done so, you can call `example.com/worker` (replace example.com with your actual domain name) on a regular basis from an external service.
|
||||
This will then trigger the execution of the background process.
|
||||
|
||||
### Relocate
|
||||
|
||||
## Users
|
||||
|
|
|
@ -36,11 +36,11 @@ The addon tree has to be updated separately like so:
|
|||
git pull
|
||||
|
||||
For both repositories:
|
||||
The default branch to use is the ``master`` branch, which is the stable version of Friendica.
|
||||
The default branch to use is the ``stable`` branch, which is the stable version of Friendica.
|
||||
It is updated about four times a year on a fixed schedule.
|
||||
|
||||
If you want to use and test bleeding edge code please checkout the ``develop`` branch.
|
||||
The new features and fixes will be merged from ``develop`` into ``master`` after a release candidate period before each release.
|
||||
The new features and fixes will be merged from ``develop`` into ``stable`` after a release candidate period before each release.
|
||||
|
||||
Warning: The ``develop`` branch is unstable, and breaks on average once a month for at most 24 hours until a patch is submitted and merged.
|
||||
Be sure to pull frequently if you choose the ``develop`` branch.
|
||||
|
|
|
@ -18,9 +18,6 @@ Database Tables
|
|||
| [event](help/database/db_event) | Events |
|
||||
| [fcontact](help/database/db_fcontact) | friend suggestion stuff |
|
||||
| [fsuggest](help/database/db_fsuggest) | friend suggestion stuff |
|
||||
| [gcign](help/database/db_gcign) | contacts ignored by friend suggestions |
|
||||
| [gcontact](help/database/db_gcontact) | global contacts |
|
||||
| [glink](help/database/db_glink) | "friends of friends" linkages derived from poco |
|
||||
| [group](help/database/db_group) | privacy groups, group info |
|
||||
| [group_member](help/database/db_group_member) | privacy groups, member info |
|
||||
| [gserver](help/database/db_gserver) | |
|
||||
|
|
|
@ -67,6 +67,6 @@ Table contact
|
|||
| bd | | date | NO | | 0001-01-01 | |
|
||||
| notify_new_posts | | tinyint(1) | NO | | 0 | |
|
||||
| fetch_further_information | | tinyint(1) | NO | | 0 | |
|
||||
| ffi_keyword_blacklist | | mediumtext | NO | | NULL | |
|
||||
| ffi_keyword_denylist | | mediumtext | NO | | NULL | |
|
||||
|
||||
Return to [database documentation](help/database)
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
Table gcign
|
||||
===========
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ----- | ------------------------------ | ------- | ---- | --- | ------- | --------------- |
|
||||
| id | sequential ID | int(11) | NO | PRI | NULL | auto_increment |
|
||||
| uid | local user.id | int(11) | NO | MUL | 0 | |
|
||||
| gcid | gcontact.id of ignored contact | int(11) | NO | MUL | 0 | |
|
||||
|
||||
Return to [database documentation](help/database)
|
|
@ -1,32 +0,0 @@
|
|||
Table gcontact
|
||||
==============
|
||||
|
||||
| Field |Description | Type | Null | Key | Default | Extra |
|
||||
|--------------|------------------------------------|------------------|------|-----|---------------------|----------------|
|
||||
| id | sequential ID | int(10) unsigned | NO | PRI | NULL | auto_increment |
|
||||
| name | Name that this contact is known by | varchar(255) | NO | | | |
|
||||
| nick | Nick- and user name of the contact | varchar(255) | NO | | | |
|
||||
| url | Link to the contacts profile page | varchar(255) | NO | | | |
|
||||
| nurl | | varchar(255) | NO | MUL | | |
|
||||
| photo | Link to the profile photo | varchar(255) | NO | | | |
|
||||
| connect | | varchar(255) | NO | | | |
|
||||
| created | | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||
| updated | | datetime | YES | MUL | 0001-01-01 00:00:00 | |
|
||||
| last_contact | | datetime | YES | | 0001-01-01 00:00:00 | |
|
||||
| last_failure | | datetime | YES | | 0001-01-01 00:00:00 | |
|
||||
| location | | varchar(255) | NO | | | |
|
||||
| about | | text | NO | | NULL | |
|
||||
| keywords | puplic keywords (interests) | text | NO | | NULL | |
|
||||
| gender | | varchar(32) | NO | | | |
|
||||
| birthday | | varchar(32) | NO | | 0001-01-01 | |
|
||||
| community | 1 if contact is forum account | tinyint(1) | NO | | 0 | |
|
||||
| hide | 1 = should be hidden from search | tinyint(1) | NO | | 0 | |
|
||||
| nsfw | 1 = contact posts nsfw content | tinyint(1) | NO | | 0 | |
|
||||
| network | social network protocol | varchar(255) | NO | | | |
|
||||
| addr | | varchar(255) | NO | | | |
|
||||
| notify | | text | NO | | | |
|
||||
| alias | | varchar(255) | NO | | | |
|
||||
| generation | | tinyint(3) | NO | | 0 | |
|
||||
| server_url | baseurl of the contacts server | varchar(255) | NO | | | |
|
||||
|
||||
Return to [database documentation](help/database)
|
|
@ -1,13 +0,0 @@
|
|||
Table glink
|
||||
===========
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
|---------|------------------|------------------|------|-----|---------------------|----------------|
|
||||
| id | sequential ID | int(10) unsigned | NO | PRI | NULL | auto_increment |
|
||||
| cid | | int(11) | NO | MUL | 0 | |
|
||||
| uid | | int(11) | NO | | 0 | |
|
||||
| gcid | | int(11) | NO | MUL | 0 | |
|
||||
| zcid | | int(11) | NO | MUL | 0 | |
|
||||
| updated | | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||
|
||||
Return to [database documentation](help/database)
|
|
@ -226,10 +226,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
|
|||
Hook::callAll('item_photo_menu', $args);
|
||||
Hook::callAll('jot_tool', $jotplugins);
|
||||
|
||||
### include/items.php
|
||||
|
||||
Hook::callAll('page_info_data', $data);
|
||||
|
||||
### mod/directory.php
|
||||
|
||||
Hook::callAll('directory_item', $arr);
|
||||
|
@ -316,10 +312,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
|
|||
|
||||
Hook::callAll('post_local_end', $arr);
|
||||
|
||||
### mod/lockview.php
|
||||
|
||||
Hook::callAll('lockview_content', $item);
|
||||
|
||||
### mod/uexport.php
|
||||
|
||||
Hook::callAll('uexport_options', $options);
|
||||
|
@ -426,6 +418,10 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
|
|||
|
||||
Hook::callAll('storage_instance', $data);
|
||||
|
||||
### src/Module/PermissionTooltip.php
|
||||
|
||||
Hook::callAll('lockview_content', $item);
|
||||
|
||||
### src/Worker/Directory.php
|
||||
|
||||
Hook::callAll('globaldir_update', $arr);
|
||||
|
|
|
@ -65,17 +65,17 @@ table.bbcodes > * > tr > th {
|
|||
<td><a href="http://friendi.ca" target="external-link">Friendica</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[img]https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg[/img]</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg" alt="Immagine/foto"></td>
|
||||
<td>[img]https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg[/img]</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg" alt="Immagine/foto"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[img=https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg]Das Friendica Logo[/img]</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg" alt="Das Friendica Logo"></td>
|
||||
<td>[img=https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg]Das Friendica Logo[/img]</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg" alt="Das Friendica Logo"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[img=64x32]https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg[/img]<br>
|
||||
<td>[img=64x32]https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg[/img]<br>
|
||||
<br>Note: provided height is simply discarded.</td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/master/images/friendica-32.jpg" style="width: 64px;"></td>
|
||||
<td><img src="https://raw.githubusercontent.com/friendica/friendica/stable/images/friendica-32.jpg" style="width: 64px;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[size=xx-small]kleiner Text[/size]</td>
|
||||
|
@ -482,10 +482,6 @@ Du kannst Videos, Musikdateien und weitere Dinge in Beiträgen einbinden.
|
|||
<td>[embed]URL[/embed]</td>
|
||||
<td>OEmbed rich content einbetten.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[iframe]URL[/iframe]</td>
|
||||
<td>General embed, iframe size is limited by the theme size for video players.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[url]*url*[/url]</td>
|
||||
<td>Wenn *url* die OEmbed- oder Opengraph-Spezifikationen unterstützt, wird das Objekt eingebettet (z.B. Dokumente von scribd).
|
||||
|
|
|
@ -49,7 +49,7 @@ Per Git:
|
|||
cd /var/www/<Pfad zu Deiner friendica-Installation>/addon; git pull
|
||||
</p>
|
||||
|
||||
oder als normaler Download von hier: https://github.com/friendica/friendica-addons/blob/master/jappixmini.tgz (auf „view raw“ klicken)
|
||||
oder als normaler Download von hier: https://github.com/friendica/friendica-addons/blob/stable/jappixmini.tgz (auf „view raw“ klicken)
|
||||
|
||||
Entpacke diese Datei (ggf. den entpackten Ordner in „jappixmini“ umbenennen) und lade sowohl den entpackten Ordner komplett als auch die .tgz Datei in den Addon Ordner Deiner Friendica Installation hoch.
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ Wenn du die Möglichkeit hierzu hast, empfehlen wir dir "git" zu nutzen, um die
|
|||
Das macht die Aktualisierung wesentlich einfacher.
|
||||
Der Linux-Code, mit dem man die Dateien direkt in ein Verzeichnis wie "meinewebseite" kopiert, ist
|
||||
|
||||
git clone https://github.com/friendica/friendica.git -b master mywebsite
|
||||
git clone https://github.com/friendica/friendica.git -b stable mywebsite
|
||||
cd mywebsite
|
||||
bin/composer.phar install
|
||||
|
||||
|
@ -70,7 +70,7 @@ Falls Addons installiert werden sollen: Gehe in den Friendica-Ordner
|
|||
|
||||
Und die Addon Repository klonst:
|
||||
|
||||
git clone https://github.com/friendica/friendica-addons.git -b master addon
|
||||
git clone https://github.com/friendica/friendica-addons.git -b stable addon
|
||||
|
||||
Um das Addon-Verzeichnis aktuell zu halten, solltest du in diesem Pfad ein "git pull"-Befehl eintragen
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@ Friendica Nachrichtenfluss
|
|||
Diese Seite soll einige Infos darüber dokumentieren, wie Nachrichten innerhalb von Friendica von einer Person zur anderen übertragen werden.
|
||||
Es gibt verschiedene Pfade, die verschiedene Protokolle und Nachrichtenformate nutzen.
|
||||
|
||||
Diejenigen, die den Nachrichtenfluss genauer verstehen wollen, sollten sich mindestens mit dem DFRN-Protokoll ([Dokument mit den DFRN Spezifikationen](https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf)) und den Elementen zur Nachrichtenverarbeitung des OStatus Stack informieren (salmon und Pubsubhubbub).
|
||||
|
||||
Der Großteil der Nachrichtenverarbeitung nutzt die Datei include/items.php, welche Funktionen für verschiedene Feed-bezogene Import-/Exportaktivitäten liefert.
|
||||
Diejenigen, die den Nachrichtenfluss genauer verstehen wollen, sollten sich mindestens mit dem DFRN-Protokoll ([Dokument mit den DFRN Spezifikationen](https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf)) und den Elementen zur Nachrichtenverarbeitung des OStatus Stack informieren (salmon und Pubsubhubbub).
|
||||
|
||||
Wenn eine Nachricht veröffentlicht wird, werden alle Übermittlungen an alle Netzwerke mit include/notifier.php durchgeführt, welche entscheidet, wie und an wen die Nachricht geliefert wird.
|
||||
Diese Datei bindet dabei die lokale Bearbeitung aller Übertragungen ein inkl. dfrn-notify.
|
||||
|
|
|
@ -172,7 +172,7 @@ Wildcards werden akzeptiert Standardmäßig sind alle gültigen Email-Adressen e
|
|||
|
||||
#### Nutzern erlauben das remote_self Flag zu setzen
|
||||
|
||||
Webb du die Option `Nutzern erlauben das remote_self Flag zu setzen` aktivierst, können alle Nutzer Atom Feeds in den erweiterten Einstellungen des Kontakts als "Entferntes Konto" markieren.
|
||||
Webb du die Option `Nutzern erlauben das remote_self Flag zu setzen` aktivierst, können alle Nutzer Atom Feeds in den Kontakteinstellungen als "Entferntes Konto" markieren.
|
||||
Dadurch werden automatisch alle Beiträge dieser Feeds für diesen Nutzer gespiegelt und an die Kontakte bei Friendica verteilt.
|
||||
|
||||
Dieses Feature kann z.B. dafür genutzt werden Blogbeiträge zu spiegeln.
|
||||
|
@ -227,15 +227,9 @@ In diesem Abschnitt kann der Hintergrund-Prozess konfiguriert werden.
|
|||
Bevor ein neuer *Worker* Prozess gestartet wird, überprüft das System, dass die vorhandenen Resourchen ausrechend sind,
|
||||
Aus diesem Grund kann es sein, dass die maximale Zahl der Hintergrungprozesse nicht erreicht wird.
|
||||
|
||||
Sollte die PHP Funktion `proc_open` auf dem Server nicht verfügbar sein, kann die Verwendung durch Friendica hier unterbunden werden.
|
||||
|
||||
Die Aufgaben die im Hintergrund erledigt werden, haben Prioritäten zugeteilt.
|
||||
Um garantieren zu können, das wichtige Prozesse schnellst möglich abgearbeitet werden können, selbst wenn das System gerade stark belastet ist, sollte die *fastlane* aktiviert sein.
|
||||
|
||||
Wenn es auf deinem Server nicht möglich ist, einen cron Job zu starten, kannst du den *frontend* Worker einschalten.
|
||||
Nachdem dies geschehen ist, kannst du `example.com/worker` (tausche example.com mit dem echten Domainnamen aus) aufrufen werden.
|
||||
Dadurch werden dann die Aufgaben aktiviert, die der cron Job sonst aktivieren würde.
|
||||
|
||||
### Umsiedeln
|
||||
|
||||
## Nutzer
|
||||
|
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -87,7 +87,7 @@ Field parameter:
|
|||
1. Label for the input box,
|
||||
2. Current value of the variable,
|
||||
3. Help text for the input box,
|
||||
4. if set to "required" modern browser will check that this input box is filled when submitting the form,
|
||||
4. Should be set to the translation of "Required" to mark this field as required,
|
||||
5. if set to "autofocus" modern browser will put the cursur into this box once the page is loaded,
|
||||
6. if set, it will be used for the input type, default is `text` (possible types: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#%3Cinput%3E_types).
|
||||
|
||||
|
@ -122,7 +122,7 @@ Field parameter:
|
|||
1. Label for the field,
|
||||
2. Value for the field, e.g. the old password,
|
||||
3. Help text for the input field,
|
||||
4. if set to "required" modern browser will check that this field is filled out,
|
||||
4. Should be set to the translation of "Required" to mark this field as required,
|
||||
5. if set to "autofocus" modern browser will put the cursor automatically into this input field.
|
||||
|
||||
### field_radio.tpl
|
||||
|
@ -176,5 +176,5 @@ Field parameter:
|
|||
0. Name of the input field,
|
||||
1. Label for the input box,
|
||||
2. Current text for the box,
|
||||
3. Help text for the input box.
|
||||
4. if set to "required" modern browser will check that this input box is filled when submitting the form,
|
||||
3. Help text for the input box,
|
||||
4. Should be set to the translation of "Required" to mark this field as required.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* [Home](help)
|
||||
|
||||
To change the look of friendica you have to touch the themes.
|
||||
The current default theme is [Vier](https://github.com/friendica/friendica/tree/master/view/theme/vier) but there are numerous others.
|
||||
The current default theme is [Vier](https://github.com/friendica/friendica/tree/stable/view/theme/vier) but there are numerous others.
|
||||
Have a look at [friendica-themes.com](http://friendica-themes.com) for an overview of the existing themes.
|
||||
In case none of them suits your needs, there are several ways to change a theme.
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ The console provides the following commands:
|
|||
* typo: Checks for parse errors in Friendica files
|
||||
* postupdate: Execute pending post update scripts (can last days)
|
||||
* storage: Manage storage backend
|
||||
* relay: Manage ActivityPub relay servers
|
||||
|
||||
Please consult *bin/console help* on the command line interface of your server for details about the commands.
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ Friendica translations
|
|||
The Friendica translation process is based on `gettext` PO files.
|
||||
|
||||
Basic worflow:
|
||||
1. `xgettext` is used to collect translation strings across the project in the master PO file located in `view/lang/C/messages.po`.
|
||||
1. `xgettext` is used to collect translation strings across the project in the authoritative PO file located in `view/lang/C/messages.po`.
|
||||
2. This file makes translations strings available at [the Transifex Friendica page](https://www.transifex.com/Friendica/friendica/dashboard/).
|
||||
3. The translation itself is done at Transifex by volunteers.
|
||||
4. The resulting PO files by languages are manually updated in `view/lang/<language>/messages.po`.
|
||||
|
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 111 B |
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 111 B |
Before Width: | Height: | Size: 1,021 B After Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 1,015 B After Width: | Height: | Size: 407 B |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 10 KiB |
BIN
images/friendica-192.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
images/friendica-192.png
Normal file
After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 4.5 KiB |
BIN
images/friendica-512.jpg
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
images/friendica-512.png
Normal file
After Width: | Height: | Size: 34 KiB |
|
@ -1,240 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="friendica.svg"
|
||||
inkscape:export-filename="/home/meta/Documents/My random images/friendica.png"
|
||||
inkscape:export-xdpi="80.552788"
|
||||
inkscape:export-ydpi="80.552788">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="highlightgradient">
|
||||
<stop
|
||||
id="stop3833"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:0.74374998;" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3829" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="shadowgradient">
|
||||
<stop
|
||||
id="stop3833-5"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:0.5;" />
|
||||
<stop
|
||||
style="stop-color:#818080;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3829-9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#highlightgradient"
|
||||
id="linearGradient4011"
|
||||
x1="44.948269"
|
||||
y1="0"
|
||||
x2="54.103466"
|
||||
y2="46.797421"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(1,0.54545455)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#shadowgradient"
|
||||
id="linearGradient4021"
|
||||
x1="52.016712"
|
||||
y1="96"
|
||||
x2="42.867535"
|
||||
y2="41.837971"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.5,0,48)" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter4055"
|
||||
x="-0.03"
|
||||
width="1.06"
|
||||
y="-0.12"
|
||||
height="1.24">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.2"
|
||||
id="feGaussianBlur4057" />
|
||||
</filter>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter4059"
|
||||
x="-0.029877551"
|
||||
width="1.0597551"
|
||||
y="-0.122"
|
||||
height="1.244">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.22"
|
||||
id="feGaussianBlur4061" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.9132799"
|
||||
inkscape:cx="53.033009"
|
||||
inkscape:cy="2.8284271"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
width="256px"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="1010"
|
||||
inkscape:window-x="194"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2985"
|
||||
empspacing="3"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
spacingx="2px"
|
||||
spacingy="2px" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Colors"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-956.3622)"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="fill:#ffc019;fill-opacity:1;stroke:none"
|
||||
d="M 16,0 C 7.0091019,0.04308252 0,7.0521845 0,16 0,16 0,57.499123 0,80 0,89.120146 7.0091019,96 16,96 L 32,96 32,70 64,70 63.916016,46.068359 32,46.236328 32,26 64,26 64,0 C 64,0 24,0 16,0 z"
|
||||
transform="translate(0,956.3622)"
|
||||
id="rect2993"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccsccccccccc" />
|
||||
<path
|
||||
style="fill:#1872a2;fill-opacity:1;stroke:none"
|
||||
d="m 80,1052.3622 c 8.990898,0 16.086165,-6.966 16,-16 0,0 0,-41.4991 0,-64 0.07767,-9.01639 -7.067354,-16 -16,-16 l -16,0 0,26 -32,0 0,22 32,0 0,22 -32,0 0,26 c 0,0 32,0 48,0 z"
|
||||
id="rect2993-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccsccccccccc" />
|
||||
</g>
|
||||
<g
|
||||
style="display:inline"
|
||||
inkscape:label="Lines as original logo"
|
||||
id="g3997"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3999"
|
||||
d="m 64,0 0,26 -32,0 0,22 m 32,0 0,22 -32,0 0,26"
|
||||
style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<rect
|
||||
ry="16"
|
||||
rx="16"
|
||||
y="0"
|
||||
x="0"
|
||||
height="96"
|
||||
width="96"
|
||||
id="rect4001"
|
||||
style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Lines with center break"
|
||||
style="display:none">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 64,0 0,26 -32,0 0,22 32,0 0,22 -32,0 0,26"
|
||||
id="path3926"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="rect3928"
|
||||
width="96"
|
||||
height="96"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="16"
|
||||
ry="16" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Effects"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="fill:url(#linearGradient3930);fill-opacity:1;stroke:none"
|
||||
id="rect3823"
|
||||
width="96"
|
||||
height="48.04369"
|
||||
x="-3.1086245e-15"
|
||||
y="1.8024861e-14"
|
||||
ry="15.215644"
|
||||
rx="15.214664" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient3904);fill-opacity:1;stroke:none"
|
||||
id="rect3823-8"
|
||||
width="96"
|
||||
height="47.86721"
|
||||
x="1.5376101e-14"
|
||||
y="-96"
|
||||
ry="15.159752"
|
||||
rx="15.214664"
|
||||
transform="scale(1,-1)" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient4011);fill-opacity:1;stroke:none;filter:url(#filter4059)"
|
||||
id="rect4003"
|
||||
width="98"
|
||||
height="24"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="15.214664"
|
||||
ry="8.2994423"
|
||||
transform="matrix(1.0296115,0,0,1.1963836,-2.901924,-4.7132067)" />
|
||||
<rect
|
||||
style="opacity:0.56746030000000003;fill:url(#linearGradient4021);fill-opacity:1;stroke:none;filter:url(#filter4055)"
|
||||
id="rect4013"
|
||||
width="96"
|
||||
height="24"
|
||||
x="0"
|
||||
y="72"
|
||||
rx="14.008356"
|
||||
ry="12"
|
||||
transform="matrix(0.9768331,0,0,0.91974646,1.1649641,8.098115)" />
|
||||
</g>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg2" width="96" height="96" version="1.1"><defs id="defs4"><linearGradient id="highlightgradient"><stop id="stop3833" offset="0" style="stop-color:#fff;stop-opacity:.74374998"/><stop style="stop-color:#fff;stop-opacity:0" id="stop3829" offset="1"/></linearGradient><linearGradient id="shadowgradient"><stop id="stop3833-5" offset="0" style="stop-color:#000;stop-opacity:.5"/><stop style="stop-color:#818080;stop-opacity:0" id="stop3829-9" offset="1"/></linearGradient><linearGradient id="linearGradient4011" x1="44.948" x2="54.103" y1="0" y2="46.797" gradientTransform="scale(1,0.54545455)" gradientUnits="userSpaceOnUse" xlink:href="#highlightgradient"/><linearGradient id="linearGradient4021" x1="52.017" x2="42.868" y1="96" y2="41.838" gradientTransform="matrix(1,0,0,0.5,0,48)" gradientUnits="userSpaceOnUse" xlink:href="#shadowgradient"/><filter id="filter4055" width="1.06" height="1.24" x="-.03" y="-.12"><feGaussianBlur id="feGaussianBlur4057" stdDeviation="1.2"/></filter><filter id="filter4059" width="1.06" height="1.244" x="-.03" y="-.122"><feGaussianBlur id="feGaussianBlur4061" stdDeviation="1.22"/></filter></defs><metadata id="metadata7"/><g id="layer1" transform="translate(0,-956.3622)" style="display:inline"><path style="fill:#ffc019;fill-opacity:1;stroke:none" id="rect2993" d="M 16,0 C 7.0091019,0.04308252 0,7.0521845 0,16 0,16 0,57.499123 0,80 0,89.120146 7.0091019,96 16,96 L 32,96 32,70 64,70 63.916016,46.068359 32,46.236328 32,26 64,26 64,0 C 64,0 24,0 16,0 z" transform="translate(0,956.3622)"/><path style="fill:#1872a2;fill-opacity:1;stroke:none" id="rect2993-6" d="m 80,1052.3622 c 8.990898,0 16.086165,-6.966 16,-16 0,0 0,-41.4991 0,-64 0.07767,-9.01639 -7.067354,-16 -16,-16 l -16,0 0,26 -32,0 0,22 32,0 0,22 -32,0 0,26 c 0,0 32,0 48,0 z"/></g><g style="display:inline" id="g3997"><path id="path3999" d="m 64,0 0,26 -32,0 0,22 m 32,0 0,22 -32,0 0,26" style="fill:none;stroke:#000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/><rect id="rect4001" width="96" height="96" x="0" y="0" rx="16" ry="16" style="fill:none;stroke:#000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/></g><g id="layer3" style="display:none"><path style="fill:none;stroke:#000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" id="path3926" d="m 64,0 0,26 -32,0 0,22 32,0 0,22 -32,0 0,26"/><rect style="fill:none;stroke:#000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" id="rect3928" width="96" height="96" x="0" y="0" rx="16" ry="16"/></g><g id="layer2" style="display:inline"><rect style="fill:url(#linearGradient3930);fill-opacity:1;stroke:none" id="rect3823" width="96" height="48.044" x="0" y="0" rx="15.215" ry="15.216"/><rect style="fill:url(#linearGradient3904);fill-opacity:1;stroke:none" id="rect3823-8" width="96" height="47.867" x="0" y="-96" rx="15.215" ry="15.16" transform="scale(1,-1)"/><rect style="fill:url(#linearGradient4011);fill-opacity:1;stroke:none;filter:url(#filter4059)" id="rect4003" width="98" height="24" x="0" y="0" rx="15.215" ry="8.299" transform="matrix(1.0296115,0,0,1.1963836,-2.901924,-4.7132067)"/><rect style="opacity:.56746030000000003;fill:url(#linearGradient4021);fill-opacity:1;stroke:none;filter:url(#filter4055)" id="rect4013" width="96" height="24" x="0" y="72" rx="14.008" ry="12" transform="matrix(0.9768331,0,0,0.91974646,1.1649641,8.098115)"/></g></svg>
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 3.6 KiB |
BIN
images/globe.gif
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 625 B |
1
images/humane-tech-badge.svg
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
images/larrw.gif
Before Width: | Height: | Size: 1,004 B After Width: | Height: | Size: 211 B |
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 121 B |
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 229 B |
BIN
images/pause.gif
Before Width: | Height: | Size: 866 B After Width: | Height: | Size: 123 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
BIN
images/play.gif
Before Width: | Height: | Size: 912 B After Width: | Height: | Size: 315 B |
BIN
images/rarrw.gif
Before Width: | Height: | Size: 999 B After Width: | Height: | Size: 207 B |
Before Width: | Height: | Size: 826 B After Width: | Height: | Size: 822 B |
Before Width: | Height: | Size: 1,003 B After Width: | Height: | Size: 916 B |
Before Width: | Height: | Size: 885 B After Width: | Height: | Size: 868 B |
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 392 B |
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 373 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 497 B After Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 938 B After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 924 B After Width: | Height: | Size: 221 B |
250
include/api.php
|
@ -43,7 +43,8 @@ use Friendica\Model\Notify;
|
|||
use Friendica\Model\Photo;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Model\UserItem;
|
||||
use Friendica\Network\FKOAuth1;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Security\FKOAuth1;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
use Friendica\Network\HTTPException\ExpectationFailedException;
|
||||
|
@ -57,6 +58,8 @@ use Friendica\Network\HTTPException\UnauthorizedException;
|
|||
use Friendica\Object\Image;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Protocol\Diaspora;
|
||||
use Friendica\Security\OAuth1\OAuthRequest;
|
||||
use Friendica\Security\OAuth1\OAuthUtil;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Images;
|
||||
use Friendica\Util\Network;
|
||||
|
@ -64,7 +67,6 @@ use Friendica\Util\Proxy as ProxyUtils;
|
|||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
require_once __DIR__ . '/../mod/share.php';
|
||||
require_once __DIR__ . '/../mod/item.php';
|
||||
require_once __DIR__ . '/../mod/wall_upload.php';
|
||||
|
||||
|
@ -263,7 +265,10 @@ function api_login(App $a)
|
|||
throw new UnauthorizedException("This API requires login");
|
||||
}
|
||||
|
||||
DI::auth()->setForUser($a, $record);
|
||||
// Don't refresh the login date more often than twice a day to spare database writes
|
||||
$login_refresh = strcmp(DateTimeFormat::utc('now - 12 hours'), $record['login_date']) > 0;
|
||||
|
||||
DI::auth()->setForUser($a, $record, false, false, $login_refresh);
|
||||
|
||||
$_SESSION["allow_api"] = true;
|
||||
|
||||
|
@ -307,22 +312,22 @@ function api_call(App $a, App\Arguments $args = null)
|
|||
}
|
||||
|
||||
$type = "json";
|
||||
if (strpos($args->getQueryString(), ".xml") > 0) {
|
||||
if (strpos($args->getCommand(), ".xml") > 0) {
|
||||
$type = "xml";
|
||||
}
|
||||
if (strpos($args->getQueryString(), ".json") > 0) {
|
||||
if (strpos($args->getCommand(), ".json") > 0) {
|
||||
$type = "json";
|
||||
}
|
||||
if (strpos($args->getQueryString(), ".rss") > 0) {
|
||||
if (strpos($args->getCommand(), ".rss") > 0) {
|
||||
$type = "rss";
|
||||
}
|
||||
if (strpos($args->getQueryString(), ".atom") > 0) {
|
||||
if (strpos($args->getCommand(), ".atom") > 0) {
|
||||
$type = "atom";
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($API as $p => $info) {
|
||||
if (strpos($args->getQueryString(), $p) === 0) {
|
||||
if (strpos($args->getCommand(), $p) === 0) {
|
||||
if (!api_check_method($info['method'])) {
|
||||
throw new MethodNotAllowedException();
|
||||
}
|
||||
|
@ -331,16 +336,16 @@ function api_call(App $a, App\Arguments $args = null)
|
|||
|
||||
if (!empty($info['auth']) && api_user() === false) {
|
||||
api_login($a);
|
||||
Logger::info(API_LOG_PREFIX . 'username {username}', ['module' => 'api', 'action' => 'call', 'username' => $a->user['username']]);
|
||||
}
|
||||
|
||||
Logger::info(API_LOG_PREFIX . 'username {username}', ['module' => 'api', 'action' => 'call', 'username' => $a->user['username']]);
|
||||
Logger::debug(API_LOG_PREFIX . 'parameters', ['module' => 'api', 'action' => 'call', 'parameters' => $_REQUEST]);
|
||||
|
||||
$stamp = microtime(true);
|
||||
$return = call_user_func($info['func'], $type);
|
||||
$duration = floatval(microtime(true) - $stamp);
|
||||
|
||||
Logger::info(API_LOG_PREFIX . 'username {username}', ['module' => 'api', 'action' => 'call', 'username' => $a->user['username'], 'duration' => round($duration, 2)]);
|
||||
Logger::info(API_LOG_PREFIX . 'duration {duration}', ['module' => 'api', 'action' => 'call', 'duration' => round($duration, 2)]);
|
||||
|
||||
DI::profiler()->saveLog(DI::logger(), API_LOG_PREFIX . 'performance');
|
||||
|
||||
|
@ -380,7 +385,7 @@ function api_call(App $a, App\Arguments $args = null)
|
|||
}
|
||||
|
||||
Logger::warning(API_LOG_PREFIX . 'not implemented', ['module' => 'api', 'action' => 'call', 'query' => DI::args()->getQueryString()]);
|
||||
throw new NotImplementedException();
|
||||
throw new NotFoundException();
|
||||
} catch (HTTPException $e) {
|
||||
header("HTTP/1.1 {$e->getCode()} {$e->httpdesc}");
|
||||
return api_error($type, $e, $args);
|
||||
|
@ -623,7 +628,7 @@ function api_get_user(App $a, $contact_id = null)
|
|||
'name' => $contact["name"],
|
||||
'screen_name' => (($contact['nick']) ? $contact['nick'] : $contact['name']),
|
||||
'location' => ($contact["location"] != "") ? $contact["location"] : ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol']),
|
||||
'description' => BBCode::toPlaintext($contact["about"]),
|
||||
'description' => BBCode::toPlaintext($contact["about"] ?? ''),
|
||||
'profile_image_url' => $contact["micro"],
|
||||
'profile_image_url_https' => $contact["micro"],
|
||||
'profile_image_url_profile_size' => $contact["thumb"],
|
||||
|
@ -650,8 +655,8 @@ function api_get_user(App $a, $contact_id = null)
|
|||
'notifications' => false,
|
||||
'statusnet_profile_url' => $contact["url"],
|
||||
'uid' => 0,
|
||||
'cid' => Contact::getIdForURL($contact["url"], api_user(), true),
|
||||
'pid' => Contact::getIdForURL($contact["url"], 0, true),
|
||||
'cid' => Contact::getIdForURL($contact["url"], api_user(), false),
|
||||
'pid' => Contact::getIdForURL($contact["url"], 0, false),
|
||||
'self' => 0,
|
||||
'network' => $contact["network"],
|
||||
];
|
||||
|
@ -675,7 +680,7 @@ function api_get_user(App $a, $contact_id = null)
|
|||
$countfollowers = 0;
|
||||
$starred = 0;
|
||||
|
||||
$pcontact_id = Contact::getIdForURL($uinfo[0]['url'], 0, true);
|
||||
$pcontact_id = Contact::getIdForURL($uinfo[0]['url'], 0, false);
|
||||
|
||||
if (!empty($profile['about'])) {
|
||||
$description = $profile['about'];
|
||||
|
@ -697,7 +702,7 @@ function api_get_user(App $a, $contact_id = null)
|
|||
'name' => (($uinfo[0]['name']) ? $uinfo[0]['name'] : $uinfo[0]['nick']),
|
||||
'screen_name' => (($uinfo[0]['nick']) ? $uinfo[0]['nick'] : $uinfo[0]['name']),
|
||||
'location' => $location,
|
||||
'description' => BBCode::toPlaintext($description),
|
||||
'description' => BBCode::toPlaintext($description ?? ''),
|
||||
'profile_image_url' => $uinfo[0]['micro'],
|
||||
'profile_image_url_https' => $uinfo[0]['micro'],
|
||||
'profile_image_url_profile_size' => $uinfo[0]["thumb"],
|
||||
|
@ -727,7 +732,7 @@ function api_get_user(App $a, $contact_id = null)
|
|||
'statusnet_profile_url' => $uinfo[0]['url'],
|
||||
'uid' => intval($uinfo[0]['uid']),
|
||||
'cid' => intval($uinfo[0]['cid']),
|
||||
'pid' => Contact::getIdForURL($uinfo[0]["url"], 0, true),
|
||||
'pid' => Contact::getIdForURL($uinfo[0]["url"], 0, false),
|
||||
'self' => $uinfo[0]['self'],
|
||||
'network' => $uinfo[0]['network'],
|
||||
];
|
||||
|
@ -1240,7 +1245,7 @@ function api_media_upload()
|
|||
"image_type" => $media["type"],
|
||||
"friendica_preview_url" => $media["preview"]];
|
||||
|
||||
Logger::log("Media uploaded: " . print_r($returndata, true), Logger::DEBUG);
|
||||
Logger::info('Media uploaded', ['return' => $returndata]);
|
||||
|
||||
return ["media" => $returndata];
|
||||
}
|
||||
|
@ -1310,7 +1315,7 @@ api_register_func('api/media/metadata/create', 'api_media_metadata_create', true
|
|||
/**
|
||||
* @param string $type Return format (atom, rss, xml, json)
|
||||
* @param int $item_id
|
||||
* @return string
|
||||
* @return array|string
|
||||
* @throws Exception
|
||||
*/
|
||||
function api_status_show($type, $item_id)
|
||||
|
@ -1558,7 +1563,7 @@ function api_search($type)
|
|||
$params['group_by'] = ['uri-id'];
|
||||
} else {
|
||||
$condition = ["`id` > ?
|
||||
" . ($exclude_replies ? " AND `id` = `parent` " : ' ') . "
|
||||
" . ($exclude_replies ? " AND `gravity` = " . GRAVITY_PARENT : ' ') . "
|
||||
AND (`uid` = 0 OR (`uid` = ? AND NOT `global`))
|
||||
AND `body` LIKE CONCAT('%',?,'%')",
|
||||
$since_id, api_user(), $_REQUEST['q']];
|
||||
|
@ -1646,7 +1651,8 @@ function api_statuses_home_timeline($type)
|
|||
$condition[] = $max_id;
|
||||
}
|
||||
if ($exclude_replies) {
|
||||
$condition[0] .= ' AND `item`.`parent` = `item`.`id`';
|
||||
$condition[0] .= ' AND `item`.`gravity` = ?';
|
||||
$condition[] = GRAVITY_PARENT;
|
||||
}
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `item`.`parent` = ?";
|
||||
|
@ -2033,15 +2039,22 @@ function api_statuses_repeat($type)
|
|||
|
||||
Logger::log('API: api_statuses_repeat: '.$id);
|
||||
|
||||
$fields = ['uri-id', 'body', 'title', 'attach', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
|
||||
$fields = ['uri-id', 'network', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
|
||||
$item = Item::selectFirst($fields, ['id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED]]);
|
||||
|
||||
if (DBA::isResult($item) && $item['body'] != "") {
|
||||
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER])) {
|
||||
if (!Item::performActivity($id, 'announce', local_user())) {
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
|
||||
$item_id = $id;
|
||||
} else {
|
||||
if (strpos($item['body'], "[/share]") !== false) {
|
||||
$pos = strpos($item['body'], "[share");
|
||||
$post = substr($item['body'], $pos);
|
||||
} else {
|
||||
$post = share_header($item['author-name'], $item['author-link'], $item['author-avatar'], $item['guid'], $item['created'], $item['plink']);
|
||||
$post = BBCode::getShareOpeningTag($item['author-name'], $item['author-link'], $item['author-avatar'], $item['plink'], $item['created'], $item['guid']);
|
||||
|
||||
if (!empty($item['title'])) {
|
||||
$post .= '[h3]' . $item['title'] . "[/h3]\n";
|
||||
|
@ -2051,7 +2064,6 @@ function api_statuses_repeat($type)
|
|||
$post .= "[/share]";
|
||||
}
|
||||
$_REQUEST['body'] = $post;
|
||||
$_REQUEST['attach'] = $item['attach'];
|
||||
$_REQUEST['profile_uid'] = api_user();
|
||||
$_REQUEST['api_source'] = true;
|
||||
|
||||
|
@ -2060,8 +2072,7 @@ function api_statuses_repeat($type)
|
|||
}
|
||||
|
||||
$item_id = item_post($a);
|
||||
|
||||
/// @todo Copy tags from the original post to the new one
|
||||
}
|
||||
} else {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
@ -2152,10 +2163,10 @@ function api_statuses_mentions($type)
|
|||
// get last network messages
|
||||
|
||||
// params
|
||||
$since_id = $_REQUEST['since_id'] ?? 0;
|
||||
$max_id = $_REQUEST['max_id'] ?? 0;
|
||||
$count = $_REQUEST['count'] ?? 20;
|
||||
$page = $_REQUEST['page'] ?? 1;
|
||||
$since_id = intval($_REQUEST['since_id'] ?? 0);
|
||||
$max_id = intval($_REQUEST['max_id'] ?? 0);
|
||||
$count = intval($_REQUEST['count'] ?? 20);
|
||||
$page = intval($_REQUEST['page'] ?? 1);
|
||||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
|
@ -2228,12 +2239,7 @@ function api_statuses_user_timeline($type)
|
|||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
Logger::log(
|
||||
"api_statuses_user_timeline: api_user: ". api_user() .
|
||||
"\nuser_info: ".print_r($user_info, true) .
|
||||
"\n_REQUEST: ".print_r($_REQUEST, true),
|
||||
Logger::DEBUG
|
||||
);
|
||||
Logger::info('api_statuses_user_timeline', ['api_user' => api_user(), 'user_info' => $user_info, '_REQUEST' => $_REQUEST]);
|
||||
|
||||
$since_id = $_REQUEST['since_id'] ?? 0;
|
||||
$max_id = $_REQUEST['max_id'] ?? 0;
|
||||
|
@ -2254,7 +2260,8 @@ function api_statuses_user_timeline($type)
|
|||
}
|
||||
|
||||
if ($exclude_replies) {
|
||||
$condition[0] .= ' AND `item`.`parent` = `item`.`id`';
|
||||
$condition[0] .= ' AND `item`.`gravity` = ?';
|
||||
$condition[] = GRAVITY_PARENT;
|
||||
}
|
||||
|
||||
if ($conversation_id > 0) {
|
||||
|
@ -2491,10 +2498,10 @@ function api_format_messages($item, $recipient, $sender)
|
|||
if ($_GET['getText'] == 'html') {
|
||||
$ret['text'] = BBCode::convert($item['body'], false);
|
||||
} elseif ($_GET['getText'] == 'plain') {
|
||||
$ret['text'] = trim(HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, 2, true), 0));
|
||||
$ret['text'] = trim(HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, BBCode::API, true), 0));
|
||||
}
|
||||
} else {
|
||||
$ret['text'] = $item['title'] . "\n" . HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, 2, true), 0);
|
||||
$ret['text'] = $item['title'] . "\n" . HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, BBCode::API, true), 0);
|
||||
}
|
||||
if (!empty($_GET['getUserObjects']) && $_GET['getUserObjects'] == 'false') {
|
||||
unset($ret['sender']);
|
||||
|
@ -2520,7 +2527,7 @@ function api_convert_item($item)
|
|||
$attachments = api_get_attachments($body);
|
||||
|
||||
// Workaround for ostatus messages where the title is identically to the body
|
||||
$html = BBCode::convert(api_clean_plain_items($body), false, 2, true);
|
||||
$html = BBCode::convert(api_clean_plain_items($body), false, BBCode::API, true);
|
||||
$statusbody = trim(HTML::toPlaintext($html, 0));
|
||||
|
||||
// handle data: images
|
||||
|
@ -3027,7 +3034,7 @@ function api_format_item($item, $type = "json", $status_user = null, $author_use
|
|||
$retweeted_item = [];
|
||||
$quoted_item = [];
|
||||
|
||||
if ($item["id"] == $item["parent"]) {
|
||||
if ($item['gravity'] == GRAVITY_PARENT) {
|
||||
$body = $item['body'];
|
||||
$retweeted_item = api_share_as_retweet($item);
|
||||
if ($body != $item['body']) {
|
||||
|
@ -3304,7 +3311,8 @@ function api_lists_statuses($type)
|
|||
$condition[] = $max_id;
|
||||
}
|
||||
if ($exclude_replies > 0) {
|
||||
$condition[0] .= ' AND `item`.`parent` = `item`.`id`';
|
||||
$condition[0] .= ' AND `item`.`gravity` = ?';
|
||||
$condition[] = GRAVITY_PARENT;
|
||||
}
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `item`.`parent` = ?";
|
||||
|
@ -3576,96 +3584,6 @@ function api_statusnet_version($type)
|
|||
api_register_func('api/gnusocial/version', 'api_statusnet_version', false);
|
||||
api_register_func('api/statusnet/version', 'api_statusnet_version', false);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $type Return type (atom, rss, xml, json)
|
||||
*
|
||||
* @param int $rel A contact relationship constant
|
||||
* @return array|string|void
|
||||
* @throws BadRequestException
|
||||
* @throws ForbiddenException
|
||||
* @throws ImagickException
|
||||
* @throws InternalServerErrorException
|
||||
* @throws UnauthorizedException
|
||||
* @todo use api_format_data() to return data
|
||||
*/
|
||||
function api_ff_ids($type, int $rel)
|
||||
{
|
||||
if (!api_user()) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
$a = DI::app();
|
||||
|
||||
api_get_user($a);
|
||||
|
||||
$stringify_ids = $_REQUEST['stringify_ids'] ?? false;
|
||||
|
||||
$contacts = DBA::p("SELECT `pcontact`.`id`
|
||||
FROM `contact`
|
||||
INNER JOIN `contact` AS `pcontact`
|
||||
ON `contact`.`nurl` = `pcontact`.`nurl`
|
||||
AND `pcontact`.`uid` = 0
|
||||
WHERE `contact`.`uid` = ?
|
||||
AND NOT `contact`.`self`
|
||||
AND `contact`.`rel` IN (?, ?)",
|
||||
api_user(),
|
||||
$rel,
|
||||
Contact::FRIEND
|
||||
);
|
||||
|
||||
$ids = [];
|
||||
foreach (DBA::toArray($contacts) as $contact) {
|
||||
if ($stringify_ids) {
|
||||
$ids[] = $contact['id'];
|
||||
} else {
|
||||
$ids[] = intval($contact['id']);
|
||||
}
|
||||
}
|
||||
|
||||
return api_format_data('ids', $type, ['id' => $ids]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of every user the user is following.
|
||||
*
|
||||
* @param string $type Return type (atom, rss, xml, json)
|
||||
*
|
||||
* @return array|string
|
||||
* @throws BadRequestException
|
||||
* @throws ForbiddenException
|
||||
* @throws ImagickException
|
||||
* @throws InternalServerErrorException
|
||||
* @throws UnauthorizedException
|
||||
* @see https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friends-ids
|
||||
*/
|
||||
function api_friends_ids($type)
|
||||
{
|
||||
return api_ff_ids($type, Contact::SHARING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of every user following the user.
|
||||
*
|
||||
* @param string $type Return type (atom, rss, xml, json)
|
||||
*
|
||||
* @return array|string
|
||||
* @throws BadRequestException
|
||||
* @throws ForbiddenException
|
||||
* @throws ImagickException
|
||||
* @throws InternalServerErrorException
|
||||
* @throws UnauthorizedException
|
||||
* @see https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-followers-ids
|
||||
*/
|
||||
function api_followers_ids($type)
|
||||
{
|
||||
return api_ff_ids($type, Contact::FOLLOWER);
|
||||
}
|
||||
|
||||
/// @TODO move to top of file or somewhere better
|
||||
api_register_func('api/friends/ids', 'api_friends_ids', true);
|
||||
api_register_func('api/followers/ids', 'api_followers_ids', true);
|
||||
|
||||
/**
|
||||
* Sends a new direct message.
|
||||
*
|
||||
|
@ -4161,26 +4079,18 @@ function api_fr_photoalbum_delete($type)
|
|||
throw new BadRequestException("no albumname specified");
|
||||
}
|
||||
// check if album is existing
|
||||
$r = q(
|
||||
"SELECT DISTINCT `resource-id` FROM `photo` WHERE `uid` = %d AND `album` = '%s'",
|
||||
intval(api_user()),
|
||||
DBA::escape($album)
|
||||
);
|
||||
if (!DBA::isResult($r)) {
|
||||
|
||||
$photos = DBA::selectToArray('photo', ['resource-id'], ['uid' => api_user(), 'album' => $album], ['group_by' => ['resource-id']]);
|
||||
if (!DBA::isResult($photos)) {
|
||||
throw new BadRequestException("album not available");
|
||||
}
|
||||
|
||||
$resourceIds = array_column($photos, 'resource-id');
|
||||
|
||||
// function for setting the items to "deleted = 1" which ensures that comments, likes etc. are not shown anymore
|
||||
// to the user and the contacts of the users (drop_items() performs the federation of the deletion to other networks
|
||||
foreach ($r as $rr) {
|
||||
$condition = ['uid' => local_user(), 'resource-id' => $rr['resource-id'], 'type' => 'photo'];
|
||||
$photo_item = Item::selectFirstForUser(local_user(), ['id'], $condition);
|
||||
|
||||
if (!DBA::isResult($photo_item)) {
|
||||
throw new InternalServerErrorException("problem with deleting items occured");
|
||||
}
|
||||
Item::deleteForUser(['id' => $photo_item['id']], api_user());
|
||||
}
|
||||
$condition = ['uid' => api_user(), 'resource-id' => $resourceIds, 'type' => 'photo'];
|
||||
Item::deleteForUser($condition, api_user());
|
||||
|
||||
// now let's delete all photos from the album
|
||||
$result = Photo::delete(['uid' => api_user(), 'album' => $album]);
|
||||
|
@ -4309,7 +4219,7 @@ function api_fr_photo_create_update($type)
|
|||
$deny_cid = $_REQUEST['deny_cid' ] ?? null;
|
||||
$allow_gid = $_REQUEST['allow_gid'] ?? null;
|
||||
$deny_gid = $_REQUEST['deny_gid' ] ?? null;
|
||||
$visibility = !empty($_REQUEST['visibility']) && $_REQUEST['visibility'] !== "false";
|
||||
$visibility = !$allow_cid && !$deny_cid && !$allow_gid && !$deny_gid;
|
||||
|
||||
// do several checks on input parameters
|
||||
// we do not allow calls without album string
|
||||
|
@ -4457,19 +4367,13 @@ function api_fr_photo_delete($type)
|
|||
|
||||
// return success of deletion or error message
|
||||
if ($result) {
|
||||
// retrieve the id of the parent element (the photo element)
|
||||
$condition = ['uid' => local_user(), 'resource-id' => $photo_id, 'type' => 'photo'];
|
||||
$photo_item = Item::selectFirstForUser(local_user(), ['id'], $condition);
|
||||
|
||||
if (!DBA::isResult($photo_item)) {
|
||||
throw new InternalServerErrorException("problem with deleting items occured");
|
||||
}
|
||||
// function for setting the items to "deleted = 1" which ensures that comments, likes etc. are not shown anymore
|
||||
// to the user and the contacts of the users (drop_items() do all the necessary magic to avoid orphans in database and federate deletion)
|
||||
Item::deleteForUser(['id' => $photo_item['id']], api_user());
|
||||
$condition = ['uid' => api_user(), 'resource-id' => $photo_id, 'type' => 'photo'];
|
||||
Item::deleteForUser($condition, api_user());
|
||||
|
||||
$answer = ['result' => 'deleted', 'message' => 'photo with id `' . $photo_id . '` has been deleted from server.'];
|
||||
return api_format_data("photo_delete", $type, ['$result' => $answer]);
|
||||
$result = ['result' => 'deleted', 'message' => 'photo with id `' . $photo_id . '` has been deleted from server.'];
|
||||
return api_format_data("photo_delete", $type, ['$result' => $result]);
|
||||
} else {
|
||||
throw new InternalServerErrorException("unknown error on deleting photo from database table");
|
||||
}
|
||||
|
@ -4828,7 +4732,7 @@ function save_media_to_database($mediatype, $media, $type, $album, $allow_cid, $
|
|||
Logger::log("photo upload: new profile image upload ended", Logger::DEBUG);
|
||||
}
|
||||
|
||||
if (isset($r) && $r) {
|
||||
if (!empty($r)) {
|
||||
// create entry in 'item'-table on new uploads to enable users to comment/like/dislike the photo
|
||||
if ($photo_id == null && $mediatype == "photo") {
|
||||
post_photo_item($resource_id, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $filetype, $visibility);
|
||||
|
@ -4861,7 +4765,6 @@ function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $f
|
|||
$arr['guid'] = System::createUUID();
|
||||
$arr['uid'] = intval(api_user());
|
||||
$arr['uri'] = $uri;
|
||||
$arr['parent-uri'] = $uri;
|
||||
$arr['type'] = 'photo';
|
||||
$arr['wall'] = 1;
|
||||
$arr['resource-id'] = $hash;
|
||||
|
@ -4975,8 +4878,8 @@ function prepare_photo_data($type, $scale, $photo_id)
|
|||
}
|
||||
|
||||
// retrieve item element for getting activities (like, dislike etc.) related to photo
|
||||
$condition = ['uid' => local_user(), 'resource-id' => $photo_id, 'type' => 'photo'];
|
||||
$item = Item::selectFirstForUser(local_user(), ['id'], $condition);
|
||||
$condition = ['uid' => api_user(), 'resource-id' => $photo_id, 'type' => 'photo'];
|
||||
$item = Item::selectFirst(['id', 'uid', 'uri', 'parent', 'allow_cid', 'deny_cid', 'allow_gid', 'deny_gid'], $condition);
|
||||
if (!DBA::isResult($item)) {
|
||||
throw new NotFoundException('Photo-related item not found.');
|
||||
}
|
||||
|
@ -4985,7 +4888,7 @@ function prepare_photo_data($type, $scale, $photo_id)
|
|||
|
||||
// retrieve comments on photo
|
||||
$condition = ["`parent` = ? AND `uid` = ? AND (`gravity` IN (?, ?) OR `type`='photo')",
|
||||
$item[0]['parent'], api_user(), GRAVITY_PARENT, GRAVITY_COMMENT];
|
||||
$item['parent'], api_user(), GRAVITY_PARENT, GRAVITY_COMMENT];
|
||||
|
||||
$statuses = Item::selectForUser(api_user(), [], $condition);
|
||||
|
||||
|
@ -5005,10 +4908,10 @@ function prepare_photo_data($type, $scale, $photo_id)
|
|||
$data['photo']['friendica_comments'] = $comments;
|
||||
|
||||
// include info if rights on photo and rights on item are mismatching
|
||||
$rights_mismatch = $data['photo']['allow_cid'] != $item[0]['allow_cid'] ||
|
||||
$data['photo']['deny_cid'] != $item[0]['deny_cid'] ||
|
||||
$data['photo']['allow_gid'] != $item[0]['allow_gid'] ||
|
||||
$data['photo']['deny_cid'] != $item[0]['deny_cid'];
|
||||
$rights_mismatch = $data['photo']['allow_cid'] != $item['allow_cid'] ||
|
||||
$data['photo']['deny_cid'] != $item['deny_cid'] ||
|
||||
$data['photo']['allow_gid'] != $item['allow_gid'] ||
|
||||
$data['photo']['deny_gid'] != $item['deny_gid'];
|
||||
$data['photo']['rights_mismatch'] = $rights_mismatch;
|
||||
|
||||
return $data;
|
||||
|
@ -5102,8 +5005,7 @@ function api_get_announce($item)
|
|||
}
|
||||
|
||||
$fields = ['author-id', 'author-name', 'author-link', 'author-avatar'];
|
||||
$activity = Item::activityToIndex(Activity::ANNOUNCE);
|
||||
$condition = ['parent-uri' => $item['uri'], 'gravity' => GRAVITY_ACTIVITY, 'uid' => [0, $item['uid']], 'activity' => $activity];
|
||||
$condition = ['parent-uri' => $item['uri'], 'gravity' => GRAVITY_ACTIVITY, 'uid' => [0, $item['uid']], 'vid' => Verb::getID(Activity::ANNOUNCE)];
|
||||
$announce = Item::selectFirstForUser($item['uid'], $fields, $condition, ['order' => ['received' => true]]);
|
||||
if (!DBA::isResult($announce)) {
|
||||
return [];
|
||||
|
@ -5155,7 +5057,7 @@ function api_share_as_retweet(&$item)
|
|||
|
||||
$reshared_item["share-pre-body"] = $reshared['comment'];
|
||||
$reshared_item["body"] = $reshared['shared'];
|
||||
$reshared_item["author-id"] = Contact::getIdForURL($reshared['profile'], 0, true);
|
||||
$reshared_item["author-id"] = Contact::getIdForURL($reshared['profile'], 0, false);
|
||||
$reshared_item["author-name"] = $reshared['author'];
|
||||
$reshared_item["author-link"] = $reshared['profile'];
|
||||
$reshared_item["author-avatar"] = $reshared['avatar'];
|
||||
|
@ -5199,7 +5101,7 @@ function api_in_reply_to($item)
|
|||
$in_reply_to['user_id_str'] = null;
|
||||
$in_reply_to['screen_name'] = null;
|
||||
|
||||
if (($item['thr-parent'] != $item['uri']) && (intval($item['parent']) != intval($item['id']))) {
|
||||
if (($item['thr-parent'] != $item['uri']) && ($item['gravity'] != GRAVITY_PARENT)) {
|
||||
$parent = Item::selectFirst(['id'], ['uid' => $item['uid'], 'uri' => $item['thr-parent']]);
|
||||
if (DBA::isResult($parent)) {
|
||||
$in_reply_to['status_id'] = intval($parent['id']);
|
||||
|
@ -5374,7 +5276,7 @@ function api_friendica_group_show($type)
|
|||
// loop through all groups and retrieve all members for adding data in the user array
|
||||
$grps = [];
|
||||
foreach ($r as $rr) {
|
||||
$members = Contact::getByGroupId($rr['id']);
|
||||
$members = Contact\Group::getById($rr['id']);
|
||||
$users = [];
|
||||
|
||||
if ($type == "xml") {
|
||||
|
@ -5699,7 +5601,7 @@ function api_friendica_group_update($type)
|
|||
}
|
||||
|
||||
// remove members
|
||||
$members = Contact::getByGroupId($gid);
|
||||
$members = Contact\Group::getById($gid);
|
||||
foreach ($members as $member) {
|
||||
$cid = $member['id'];
|
||||
foreach ($users as $user) {
|
||||
|
@ -5813,7 +5715,7 @@ function api_friendica_activity($type)
|
|||
|
||||
$id = $_REQUEST['id'] ?? 0;
|
||||
|
||||
$res = Item::performActivity($id, $verb);
|
||||
$res = Item::performActivity($id, $verb, api_user());
|
||||
|
||||
if ($res) {
|
||||
if ($type == "xml") {
|
||||
|
|
|
@ -28,18 +28,19 @@ use Friendica\Core\Logger;
|
|||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Core\Theme;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Object\Post;
|
||||
use Friendica\Object\Thread;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Util\Crypto;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Temporal;
|
||||
use Friendica\Util\XML;
|
||||
|
@ -255,7 +256,7 @@ function localize_item(&$item)
|
|||
// add zrl's to public images
|
||||
$photo_pattern = "/\[url=(.*?)\/photos\/(.*?)\/image\/(.*?)\]\[img(.*?)\]h(.*?)\[\/img\]\[\/url\]/is";
|
||||
if (preg_match($photo_pattern, $item['body'])) {
|
||||
$photo_replace = '[url=' . Profile::zrl('$1' . '/photos/' . '$2' . '/image/' . '$3' ,true) . '][img' . '$4' . ']h' . '$5' . '[/img][/url]';
|
||||
$photo_replace = '[url=' . Profile::zrl('$1' . '/photos/' . '$2' . '/image/' . '$3' , true) . '][img' . '$4' . ']h' . '$5' . '[/img][/url]';
|
||||
$item['body'] = BBCode::pregReplaceInTag($photo_pattern, $photo_replace, 'url', $item['body']);
|
||||
}
|
||||
|
||||
|
@ -315,7 +316,7 @@ function conv_get_blocklist()
|
|||
return [];
|
||||
}
|
||||
|
||||
$str_blocked = DI::pConfig()->get(local_user(), 'system', 'blocked');
|
||||
$str_blocked = str_replace(["\n", "\r"], ",", DI::pConfig()->get(local_user(), 'system', 'blocked'));
|
||||
if (empty($str_blocked)) {
|
||||
return [];
|
||||
}
|
||||
|
@ -323,8 +324,7 @@ function conv_get_blocklist()
|
|||
$blocklist = [];
|
||||
|
||||
foreach (explode(',', $str_blocked) as $entry) {
|
||||
// The 4th parameter guarantees that there always will be a public contact entry
|
||||
$cid = Contact::getIdForURL(trim($entry), 0, true, ['url' => trim($entry)]);
|
||||
$cid = Contact::getIdForURL(trim($entry), 0, false);
|
||||
if (!empty($cid)) {
|
||||
$blocklist[] = $cid;
|
||||
}
|
||||
|
@ -354,6 +354,13 @@ function conv_get_blocklist()
|
|||
*/
|
||||
function conversation(App $a, array $items, $mode, $update, $preview = false, $order = 'commented', $uid = 0)
|
||||
{
|
||||
$page = DI::page();
|
||||
|
||||
$page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
|
||||
$page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
|
||||
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
|
||||
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
|
||||
|
||||
$ssl_state = (local_user() ? true : false);
|
||||
|
||||
$profile_owner = 0;
|
||||
|
@ -376,7 +383,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
. "<script> var profile_uid = " . $_SESSION['uid']
|
||||
. "; var netargs = '" . substr(DI::args()->getCommand(), 8)
|
||||
. '?f='
|
||||
. (!empty($_GET['cid']) ? '&cid=' . rawurlencode($_GET['cid']) : '')
|
||||
. (!empty($_GET['contactid']) ? '&contactid=' . rawurlencode($_GET['contactid']) : '')
|
||||
. (!empty($_GET['search']) ? '&search=' . rawurlencode($_GET['search']) : '')
|
||||
. (!empty($_GET['star']) ? '&star=' . rawurlencode($_GET['star']) : '')
|
||||
. (!empty($_GET['order']) ? '&order=' . rawurlencode($_GET['order']) : '')
|
||||
|
@ -435,15 +442,17 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
if (!$update) {
|
||||
$live_update_div = '<div id="live-community"></div>' . "\r\n"
|
||||
. "<script> var profile_uid = -1; var netargs = '" . substr(DI::args()->getCommand(), 10)
|
||||
."/?f='; </script>\r\n";
|
||||
. '?f='
|
||||
. (!empty($_GET['no_sharer']) ? '&no_sharer=' . rawurlencode($_GET['no_sharer']) : '')
|
||||
. "'; </script>\r\n";
|
||||
}
|
||||
} elseif ($mode === 'contacts') {
|
||||
$items = conversation_add_children($items, false, $order, $uid);
|
||||
$profile_owner = 0;
|
||||
|
||||
if (!$update) {
|
||||
$live_update_div = '<div id="live-contacts"></div>' . "\r\n"
|
||||
. "<script> var profile_uid = -1; var netargs = '" . substr(DI::args()->getCommand(), 9)
|
||||
$live_update_div = '<div id="live-contact"></div>' . "\r\n"
|
||||
. "<script> var profile_uid = -1; var netargs = '" . substr(DI::args()->getCommand(), 8)
|
||||
."/?f='; </script>\r\n";
|
||||
}
|
||||
} elseif ($mode === 'search') {
|
||||
|
@ -457,7 +466,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
}
|
||||
|
||||
$cb = ['items' => $items, 'mode' => $mode, 'update' => $update, 'preview' => $preview];
|
||||
Hook::callAll('conversation_start',$cb);
|
||||
Hook::callAll('conversation_start', $cb);
|
||||
|
||||
$items = $cb['items'];
|
||||
|
||||
|
@ -491,7 +500,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
$writable = false;
|
||||
}
|
||||
|
||||
if (in_array($mode, ['network-new', 'search', 'contact-posts'])) {
|
||||
if (in_array($mode, ['filed', 'search', 'contact-posts'])) {
|
||||
|
||||
/*
|
||||
* "New Item View" on network page or search page results
|
||||
|
@ -512,10 +521,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
|
||||
$threadsid++;
|
||||
|
||||
$owner_url = '';
|
||||
$owner_name = '';
|
||||
$sparkle = '';
|
||||
|
||||
// prevent private email from leaking.
|
||||
if ($item['network'] === Protocol::MAIL && local_user() != $item['uid']) {
|
||||
continue;
|
||||
|
@ -532,17 +537,17 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
'network' => $item['author-network'], 'url' => $item['author-link']];
|
||||
$profile_link = Contact::magicLinkByContact($author);
|
||||
|
||||
$sparkle = '';
|
||||
if (strpos($profile_link, 'redir/') === 0) {
|
||||
$sparkle = ' sparkle';
|
||||
}
|
||||
|
||||
$locate = ['location' => $item['location'], 'coord' => $item['coord'], 'html' => ''];
|
||||
Hook::callAll('render_location',$locate);
|
||||
|
||||
$location = ((strlen($locate['html'])) ? $locate['html'] : render_location_dummy($locate));
|
||||
Hook::callAll('render_location', $locate);
|
||||
$location_html = $locate['html'] ?: Strings::escapeHtml($locate['location'] ?: $locate['coord'] ?: '');
|
||||
|
||||
localize_item($item);
|
||||
if ($mode === 'network-new') {
|
||||
if ($mode === 'filed') {
|
||||
$dropping = true;
|
||||
} else {
|
||||
$dropping = false;
|
||||
|
@ -555,21 +560,18 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
'delete' => DI::l10n()->t('Delete'),
|
||||
];
|
||||
|
||||
$star = false;
|
||||
$isstarred = "unstarred";
|
||||
|
||||
$lock = false;
|
||||
$likebuttons = [
|
||||
'like' => null,
|
||||
'dislike' => null,
|
||||
'share' => null,
|
||||
'announce' => null,
|
||||
];
|
||||
|
||||
if (DI::pConfig()->get(local_user(), 'system', 'hide_dislike')) {
|
||||
unset($likebuttons['dislike']);
|
||||
}
|
||||
|
||||
$body = Item::prepareBody($item, true, $preview);
|
||||
$body_html = Item::prepareBody($item, true, $preview);
|
||||
|
||||
list($categories, $folders) = DI::contentItem()->determineCategoriesTerms($item);
|
||||
|
||||
|
@ -583,18 +585,22 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
'template' => $tpl,
|
||||
'id' => ($preview ? 'P0' : $item['id']),
|
||||
'guid' => ($preview ? 'Q0' : $item['guid']),
|
||||
'commented' => $item['commented'],
|
||||
'received' => $item['received'],
|
||||
'created_date' => $item['created'],
|
||||
'uriid' => $item['uri-id'],
|
||||
'network' => $item['network'],
|
||||
'network_name' => ContactSelector::networkToName($item['author-network'], $item['author-link'], $item['network']),
|
||||
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link']),
|
||||
'linktitle' => DI::l10n()->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
|
||||
'profile_url' => $profile_link,
|
||||
'item_photo_menu' => item_photo_menu($item),
|
||||
'item_photo_menu_html' => item_photo_menu($item),
|
||||
'name' => $profile_name,
|
||||
'sparkle' => $sparkle,
|
||||
'lock' => $lock,
|
||||
'thumb' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($item['author-avatar'], false, ProxyUtils::SIZE_THUMB)),
|
||||
'lock' => false,
|
||||
'thumb' => DI::baseUrl()->remove($item['author-avatar']),
|
||||
'title' => $title,
|
||||
'body' => $body,
|
||||
'body_html' => $body_html,
|
||||
'tags' => $tags['tags'],
|
||||
'hashtags' => $tags['hashtags'],
|
||||
'mentions' => $tags['mentions'],
|
||||
|
@ -605,23 +611,23 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
'has_folders' => ((count($folders)) ? 'true' : ''),
|
||||
'categories' => $categories,
|
||||
'folders' => $folders,
|
||||
'text' => strip_tags($body),
|
||||
'text' => strip_tags($body_html),
|
||||
'localtime' => DateTimeFormat::local($item['created'], 'r'),
|
||||
'ago' => (($item['app']) ? DI::l10n()->t('%s from %s', Temporal::getRelativeDate($item['created']),$item['app']) : Temporal::getRelativeDate($item['created'])),
|
||||
'location' => $location,
|
||||
'ago' => (($item['app']) ? DI::l10n()->t('%s from %s', Temporal::getRelativeDate($item['created']), $item['app']) : Temporal::getRelativeDate($item['created'])),
|
||||
'location_html' => $location_html,
|
||||
'indent' => '',
|
||||
'owner_name' => $owner_name,
|
||||
'owner_url' => $owner_url,
|
||||
'owner_photo' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($item['owner-avatar'], false, ProxyUtils::SIZE_THUMB)),
|
||||
'owner_name' => '',
|
||||
'owner_url' => '',
|
||||
'owner_photo' => DI::baseUrl()->remove($item['owner-avatar']),
|
||||
'plink' => Item::getPlink($item),
|
||||
'edpost' => false,
|
||||
'isstarred' => $isstarred,
|
||||
'star' => $star,
|
||||
'isstarred' => 'unstarred',
|
||||
'star' => false,
|
||||
'drop' => $drop,
|
||||
'vote' => $likebuttons,
|
||||
'like' => '',
|
||||
'dislike' => '',
|
||||
'comment' => '',
|
||||
'like_html' => '',
|
||||
'dislike_html' => '',
|
||||
'comment_html' => '',
|
||||
'conv' => (($preview) ? '' : ['href'=> 'display/'.$item['guid'], 'title'=> DI::l10n()->t('View in context')]),
|
||||
'previewing' => $previewing,
|
||||
'wait' => DI::l10n()->t('Please wait'),
|
||||
|
@ -670,7 +676,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
|
||||
$item['pagedrop'] = $page_dropping;
|
||||
|
||||
if ($item['id'] == $item['parent']) {
|
||||
if ($item['gravity'] == GRAVITY_PARENT) {
|
||||
$item_object = new Post($item);
|
||||
$conv->addParent($item_object);
|
||||
}
|
||||
|
@ -690,6 +696,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
'$live_update' => $live_update_div,
|
||||
'$remove' => DI::l10n()->t('remove'),
|
||||
'$mode' => $mode,
|
||||
'$update' => $update,
|
||||
'$user' => $a->user,
|
||||
'$threads' => $threads,
|
||||
'$dropping' => ($page_dropping ? DI::l10n()->t('Delete Selected Items') : False),
|
||||
|
@ -703,25 +710,77 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
|
|||
*
|
||||
* @param mixed $thread_items Database statement with thread posts
|
||||
* @param boolean $pinned Is the item pinned?
|
||||
* @param array $activity Contact data of the resharer
|
||||
*
|
||||
* @return array items with parents and comments
|
||||
*/
|
||||
function conversation_fetch_comments($thread_items, $pinned) {
|
||||
function conversation_fetch_comments($thread_items, bool $pinned, array $activity) {
|
||||
$comments = [];
|
||||
$parentlines = [];
|
||||
$lineno = 0;
|
||||
$actor = [];
|
||||
$received = '';
|
||||
|
||||
while ($row = Item::fetch($thread_items)) {
|
||||
if (($row['verb'] == Activity::ANNOUNCE) && !empty($row['contact-uid']) && ($row['received'] > $received) && ($row['thr-parent'] == $row['parent-uri'])) {
|
||||
$actor = ['link' => $row['author-link'], 'avatar' => $row['author-avatar'], 'name' => $row['author-name']];
|
||||
$received = $row['received'];
|
||||
if (!empty($activity)) {
|
||||
if (($row['gravity'] == GRAVITY_PARENT)) {
|
||||
$row['post-type'] = Item::PT_ANNOUNCEMENT;
|
||||
$row = array_merge($row, $activity);
|
||||
$contact = Contact::getById($activity['causer-id'], ['url', 'name', 'thumb']);
|
||||
$row['causer-link'] = $contact['url'];
|
||||
$row['causer-avatar'] = $contact['thumb'];
|
||||
$row['causer-name'] = $contact['name'];
|
||||
} elseif (($row['gravity'] == GRAVITY_ACTIVITY) && ($row['verb'] == Activity::ANNOUNCE) &&
|
||||
($row['author-id'] == $activity['causer-id'])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ((($row['gravity'] == GRAVITY_PARENT) && !$row['origin'] && !in_array($row['network'], [Protocol::DIASPORA])) &&
|
||||
(empty($row['contact-uid']) || !in_array($row['network'], Protocol::NATIVE_SUPPORT))) {
|
||||
$parentlines[] = $lineno;
|
||||
$name = $row['causer-contact-type'] == Contact::TYPE_RELAY ? $row['causer-link'] : $row['causer-name'];
|
||||
|
||||
switch ($row['post-type']) {
|
||||
case Item::PT_TO:
|
||||
$row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'to')];
|
||||
break;
|
||||
case Item::PT_CC:
|
||||
$row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'cc')];
|
||||
break;
|
||||
case Item::PT_BTO:
|
||||
$row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bto')];
|
||||
break;
|
||||
case Item::PT_BCC:
|
||||
$row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bcc')];
|
||||
break;
|
||||
case Item::PT_FOLLOWER:
|
||||
$row['direction'] = ['direction' => 6, 'title' => DI::l10n()->t('You are following %s.', $row['author-name'])];
|
||||
break;
|
||||
case Item::PT_TAG:
|
||||
$row['direction'] = ['direction' => 4, 'title' => DI::l10n()->t('Tagged')];
|
||||
break;
|
||||
case Item::PT_ANNOUNCEMENT:
|
||||
if (!empty($row['causer-id']) && DI::pConfig()->get(local_user(), 'system', 'display_resharer')) {
|
||||
$row['owner-id'] = $row['causer-id'];
|
||||
$row['owner-link'] = $row['causer-link'];
|
||||
$row['owner-avatar'] = $row['causer-avatar'];
|
||||
$row['owner-name'] = $row['causer-name'];
|
||||
}
|
||||
|
||||
if (($row['gravity'] == GRAVITY_PARENT) && !empty($row['causer-id'])) {
|
||||
$row['reshared'] = DI::l10n()->t('%s reshared this.', '<a href="'. htmlentities(Contact::magicLinkById($row['causer-id'])) .'">' . htmlentities($name) . '</a>');
|
||||
}
|
||||
$row['direction'] = ['direction' => 3, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Reshared') : DI::l10n()->t('Reshared by %s', $name))];
|
||||
break;
|
||||
case Item::PT_COMMENT:
|
||||
$row['direction'] = ['direction' => 5, 'title' => DI::l10n()->t('%s is participating in this thread.', $row['author-name'])];
|
||||
break;
|
||||
case Item::PT_STORED:
|
||||
$row['direction'] = ['direction' => 8, 'title' => DI::l10n()->t('Stored')];
|
||||
break;
|
||||
case Item::PT_GLOBAL:
|
||||
$row['direction'] = ['direction' => 9, 'title' => DI::l10n()->t('Global')];
|
||||
break;
|
||||
case Item::PT_RELAY:
|
||||
$row['direction'] = ['direction' => 10, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Relayed') : DI::l10n()->t('Relayed by %s.', $name))];
|
||||
break;
|
||||
case Item::PT_FETCHED:
|
||||
$row['direction'] = ['direction' => 2, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Fetched') : DI::l10n()->t('Fetched because of %s', $name))];
|
||||
break;
|
||||
}
|
||||
|
||||
if ($row['gravity'] == GRAVITY_PARENT) {
|
||||
|
@ -729,18 +788,10 @@ function conversation_fetch_comments($thread_items, $pinned) {
|
|||
}
|
||||
|
||||
$comments[] = $row;
|
||||
$lineno++;
|
||||
}
|
||||
|
||||
DBA::close($thread_items);
|
||||
|
||||
if (!empty($actor)) {
|
||||
foreach ($parentlines as $line) {
|
||||
$comments[$line]['owner-link'] = $actor['link'];
|
||||
$comments[$line]['owner-avatar'] = $actor['avatar'];
|
||||
$comments[$line]['owner-name'] = $actor['name'];
|
||||
}
|
||||
}
|
||||
return $comments;
|
||||
}
|
||||
|
||||
|
@ -759,9 +810,13 @@ function conversation_fetch_comments($thread_items, $pinned) {
|
|||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function conversation_add_children(array $parents, $block_authors, $order, $uid) {
|
||||
if (count($parents) > 1) {
|
||||
$max_comments = DI::config()->get('system', 'max_comments', 100);
|
||||
} else {
|
||||
$max_comments = DI::config()->get('system', 'max_display_comments', 1000);
|
||||
}
|
||||
|
||||
$params = ['order' => ['uid', 'commented' => true]];
|
||||
$params = ['order' => ['gravity', 'uid', 'commented' => true]];
|
||||
|
||||
if ($max_comments > 0) {
|
||||
$params['limit'] = $max_comments;
|
||||
|
@ -770,20 +825,24 @@ function conversation_add_children(array $parents, $block_authors, $order, $uid)
|
|||
$items = [];
|
||||
|
||||
foreach ($parents AS $parent) {
|
||||
$condition = ["`item`.`parent-uri` = ? AND `item`.`uid` IN (0, ?) ",
|
||||
$parent['uri'], $uid];
|
||||
if ($block_authors) {
|
||||
$condition[0] .= "AND NOT `author`.`hidden`";
|
||||
if (!empty($parent['thr-parent-id']) && !empty($parent['gravity']) && ($parent['gravity'] == GRAVITY_ACTIVITY)) {
|
||||
$condition = ["`item`.`parent-uri-id` = ? AND `item`.`uid` IN (0, ?) AND (`vid` != ? OR `vid` IS NULL)",
|
||||
$parent['thr-parent-id'], $uid, Verb::getID(Activity::FOLLOW)];
|
||||
if (!empty($parent['author-id'])) {
|
||||
$activity = ['causer-id' => $parent['author-id']];
|
||||
foreach (['commented', 'received', 'created'] as $orderfields) {
|
||||
if (!empty($parent[$orderfields])) {
|
||||
$activity[$orderfields] = $parent[$orderfields];
|
||||
}
|
||||
|
||||
$thread_items = Item::selectForUser(local_user(), array_merge(Item::DISPLAY_FIELDLIST, ['contact-uid', 'gravity']), $condition, $params);
|
||||
|
||||
$comments = conversation_fetch_comments($thread_items, $parent['pinned'] ?? false);
|
||||
|
||||
if (count($comments) != 0) {
|
||||
$items = array_merge($items, $comments);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$condition = ["`item`.`parent-uri` = ? AND `item`.`uid` IN (0, ?) AND (`vid` != ? OR `vid` IS NULL)",
|
||||
$parent['uri'], $uid, Verb::getID(Activity::FOLLOW)];
|
||||
$activity = [];
|
||||
}
|
||||
$items = conversation_fetch_items($parent, $items, $condition, $block_authors, $params, $activity);
|
||||
}
|
||||
|
||||
foreach ($items as $index => $item) {
|
||||
if ($item['uid'] == 0) {
|
||||
|
@ -796,6 +855,32 @@ function conversation_add_children(array $parents, $block_authors, $order, $uid)
|
|||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch conversation items
|
||||
*
|
||||
* @param array $parent Parent Item array
|
||||
* @param array $items Item array
|
||||
* @param array $condition SQL condition
|
||||
* @param boolean $block_authors Don't show posts from contacts that are hidden (used on the community page)
|
||||
* @param array $params SQL parameters
|
||||
* @param array $activity Contact data of the resharer
|
||||
* @return array
|
||||
*/
|
||||
function conversation_fetch_items(array $parent, array $items, array $condition, bool $block_authors, array $params, array $activity) {
|
||||
if ($block_authors) {
|
||||
$condition[0] .= " AND NOT `author`.`hidden`";
|
||||
}
|
||||
|
||||
$thread_items = Item::selectForUser(local_user(), array_merge(Item::DISPLAY_FIELDLIST, ['contact-uid', 'gravity', 'post-type']), $condition, $params);
|
||||
|
||||
$comments = conversation_fetch_comments($thread_items, $parent['pinned'] ?? false, $activity);
|
||||
|
||||
if (count($comments) != 0) {
|
||||
$items = array_merge($items, $comments);
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
function item_photo_menu($item) {
|
||||
$sub_link = '';
|
||||
$poke_link = '';
|
||||
|
@ -807,7 +892,7 @@ function item_photo_menu($item) {
|
|||
$block_link = '';
|
||||
$ignore_link = '';
|
||||
|
||||
if (local_user() && local_user() == $item['uid'] && $item['parent'] == $item['id'] && !$item['self']) {
|
||||
if (local_user() && local_user() == $item['uid'] && $item['gravity'] == GRAVITY_PARENT && !$item['self']) {
|
||||
$sub_link = 'javascript:dosubthread(' . $item['id'] . '); return false;';
|
||||
}
|
||||
|
||||
|
@ -817,7 +902,7 @@ function item_photo_menu($item) {
|
|||
$sparkle = (strpos($profile_link, 'redir/') === 0);
|
||||
|
||||
$cid = 0;
|
||||
$pcid = Contact::getIdForURL($item['author-link'], 0, true);
|
||||
$pcid = Contact::getIdForURL($item['author-link'], 0, false);
|
||||
$network = '';
|
||||
$rel = 0;
|
||||
$condition = ['uid' => local_user(), 'nurl' => Strings::normaliseLink($item['author-link'])];
|
||||
|
@ -864,13 +949,17 @@ function item_photo_menu($item) {
|
|||
DI::l10n()->t('Ignore') => $ignore_link
|
||||
];
|
||||
|
||||
if (!empty($item['language'])) {
|
||||
$menu[DI::l10n()->t('Languages')] = 'javascript:alert(\'' . Item::getLanguageMessage($item) . '\');';
|
||||
}
|
||||
|
||||
if ($network == Protocol::DFRN) {
|
||||
$menu[DI::l10n()->t("Poke")] = $poke_link;
|
||||
}
|
||||
|
||||
if ((($cid == 0) || ($rel == Contact::FOLLOWER)) &&
|
||||
in_array($item['network'], Protocol::FEDERATED)) {
|
||||
$menu[DI::l10n()->t('Connect/Follow')] = 'follow?url=' . urlencode($item['author-link']);
|
||||
$menu[DI::l10n()->t('Connect/Follow')] = 'follow?url=' . urlencode($item['author-link']) . '&auto=1';
|
||||
}
|
||||
} else {
|
||||
$menu = [DI::l10n()->t('View Profile') => $item['author-link']];
|
||||
|
@ -899,13 +988,14 @@ function item_photo_menu($item) {
|
|||
*
|
||||
* Increments the count of each matching activity and adds a link to the author as needed.
|
||||
*
|
||||
* @param array $item
|
||||
* @param array $activity
|
||||
* @param array &$conv_responses (already created with builtin activity structure)
|
||||
* @return void
|
||||
* @throws ImagickException
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function builtin_activity_puller($item, &$conv_responses) {
|
||||
function builtin_activity_puller(array $activity, array &$conv_responses)
|
||||
{
|
||||
foreach ($conv_responses as $mode => $v) {
|
||||
$sparkle = '';
|
||||
|
||||
|
@ -932,41 +1022,45 @@ function builtin_activity_puller($item, &$conv_responses) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!empty($item['verb']) && DI::activity()->match($item['verb'], $verb) && ($item['id'] != $item['parent'])) {
|
||||
$author = ['uid' => 0, 'id' => $item['author-id'],
|
||||
'network' => $item['author-network'], 'url' => $item['author-link']];
|
||||
if (!empty($activity['verb']) && DI::activity()->match($activity['verb'], $verb) && ($activity['gravity'] != GRAVITY_PARENT)) {
|
||||
$author = [
|
||||
'uid' => 0,
|
||||
'id' => $activity['author-id'],
|
||||
'network' => $activity['author-network'],
|
||||
'url' => $activity['author-link']
|
||||
];
|
||||
$url = Contact::magicLinkByContact($author);
|
||||
if (strpos($url, 'redir/') === 0) {
|
||||
$sparkle = ' class="sparkle" ';
|
||||
}
|
||||
|
||||
$url = '<a href="'. $url . '"'. $sparkle .'>' . htmlentities($item['author-name']) . '</a>';
|
||||
$link = '<a href="' . $url . '"' . $sparkle . '>' . htmlentities($activity['author-name']) . '</a>';
|
||||
|
||||
if (empty($item['thr-parent'])) {
|
||||
$item['thr-parent'] = $item['parent-uri'];
|
||||
if (empty($activity['thr-parent'])) {
|
||||
$activity['thr-parent'] = $activity['parent-uri'];
|
||||
}
|
||||
|
||||
if (!(isset($conv_responses[$mode][$item['thr-parent'] . '-l'])
|
||||
&& is_array($conv_responses[$mode][$item['thr-parent'] . '-l']))) {
|
||||
$conv_responses[$mode][$item['thr-parent'] . '-l'] = [];
|
||||
}
|
||||
|
||||
// only list each unique author once
|
||||
if (in_array($url,$conv_responses[$mode][$item['thr-parent'] . '-l'])) {
|
||||
// Skip when the causer of the parent is the same than the author of the announce
|
||||
if (($verb == Activity::ANNOUNCE) && Item::exists(['uri' => $activity['thr-parent'],
|
||||
'uid' => $activity['uid'], 'causer-id' => $activity['author-id'], 'gravity' => GRAVITY_PARENT])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($conv_responses[$mode][$item['thr-parent']])) {
|
||||
$conv_responses[$mode][$item['thr-parent']] = 1;
|
||||
} else {
|
||||
$conv_responses[$mode][$item['thr-parent']] ++;
|
||||
if (!isset($conv_responses[$mode][$activity['thr-parent']])) {
|
||||
$conv_responses[$mode][$activity['thr-parent']] = [
|
||||
'links' => [],
|
||||
'self' => 0,
|
||||
];
|
||||
} elseif (in_array($link, $conv_responses[$mode][$activity['thr-parent']]['links'])) {
|
||||
// only list each unique author once
|
||||
continue;
|
||||
}
|
||||
|
||||
if (public_contact() == $item['author-id']) {
|
||||
$conv_responses[$mode][$item['thr-parent'] . '-self'] = 1;
|
||||
if (public_contact() == $activity['author-id']) {
|
||||
$conv_responses[$mode][$activity['thr-parent']]['self'] = 1;
|
||||
}
|
||||
|
||||
$conv_responses[$mode][$item['thr-parent'] . '-l'][] = $url;
|
||||
$conv_responses[$mode][$activity['thr-parent']]['links'][] = $link;
|
||||
|
||||
// there can only be one activity verb per item so if we found anything, we can stop looking
|
||||
return;
|
||||
|
@ -975,26 +1069,26 @@ function builtin_activity_puller($item, &$conv_responses) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Format the vote text for a profile item
|
||||
* Format the activity text for an item/photo/video
|
||||
*
|
||||
* @param int $cnt = number of people who vote the item
|
||||
* @param array $arr = array of pre-linked names of likers/dislikers
|
||||
* @param string $type = one of 'like, 'dislike', 'attendyes', 'attendno', 'attendmaybe'
|
||||
* @param array $links = array of pre-linked names of actors
|
||||
* @param string $verb = one of 'like, 'dislike', 'attendyes', 'attendno', 'attendmaybe'
|
||||
* @param int $id = item id
|
||||
* @return string formatted text
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function format_like($cnt, array $arr, $type, $id) {
|
||||
function format_activity(array $links, $verb, $id) {
|
||||
$o = '';
|
||||
$expanded = '';
|
||||
$phrase = '';
|
||||
|
||||
if ($cnt == 1) {
|
||||
$likers = $arr[0];
|
||||
$total = count($links);
|
||||
if ($total == 1) {
|
||||
$likers = $links[0];
|
||||
|
||||
// Phrase if there is only one liker. In other cases it will be uses for the expanded
|
||||
// list which show all likers
|
||||
switch ($type) {
|
||||
switch ($verb) {
|
||||
case 'like' :
|
||||
$phrase = DI::l10n()->t('%s likes this.', $likers);
|
||||
break;
|
||||
|
@ -1014,56 +1108,51 @@ function format_like($cnt, array $arr, $type, $id) {
|
|||
$phrase = DI::l10n()->t('%s reshared this.', $likers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($cnt > 1) {
|
||||
$total = count($arr);
|
||||
} elseif ($total > 1) {
|
||||
if ($total < MAX_LIKERS) {
|
||||
$last = DI::l10n()->t('and') . ' ' . $arr[count($arr)-1];
|
||||
$arr2 = array_slice($arr, 0, -1);
|
||||
$likers = implode(', ', $arr2) . ' ' . $last;
|
||||
$likers = implode(', ', array_slice($links, 0, -1));
|
||||
$likers .= ' ' . DI::l10n()->t('and') . ' ' . $links[count($links)-1];
|
||||
} else {
|
||||
$arr = array_slice($arr, 0, MAX_LIKERS - 1);
|
||||
$likers = implode(', ', $arr);
|
||||
$likers .= DI::l10n()->t('and %d other people', $total - MAX_LIKERS);
|
||||
$likers = implode(', ', array_slice($links, 0, MAX_LIKERS - 1));
|
||||
$likers .= ' ' . DI::l10n()->t('and %d other people', $total - MAX_LIKERS);
|
||||
}
|
||||
|
||||
$spanatts = "class=\"fakelink\" onclick=\"openClose('{$type}list-$id');\"";
|
||||
$spanatts = "class=\"fakelink\" onclick=\"openClose('{$verb}list-$id');\"";
|
||||
|
||||
$explikers = '';
|
||||
switch ($type) {
|
||||
switch ($verb) {
|
||||
case 'like':
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> like this', $spanatts, $cnt);
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> like this', $spanatts, $total);
|
||||
$explikers = DI::l10n()->t('%s like this.', $likers);
|
||||
break;
|
||||
case 'dislike':
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> don\'t like this', $spanatts, $cnt);
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> don\'t like this', $spanatts, $total);
|
||||
$explikers = DI::l10n()->t('%s don\'t like this.', $likers);
|
||||
break;
|
||||
case 'attendyes':
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> attend', $spanatts, $cnt);
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> attend', $spanatts, $total);
|
||||
$explikers = DI::l10n()->t('%s attend.', $likers);
|
||||
break;
|
||||
case 'attendno':
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> don\'t attend', $spanatts, $cnt);
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> don\'t attend', $spanatts, $total);
|
||||
$explikers = DI::l10n()->t('%s don\'t attend.', $likers);
|
||||
break;
|
||||
case 'attendmaybe':
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> attend maybe', $spanatts, $cnt);
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> attend maybe', $spanatts, $total);
|
||||
$explikers = DI::l10n()->t('%s attend maybe.', $likers);
|
||||
break;
|
||||
case 'announce':
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> reshared this', $spanatts, $cnt);
|
||||
$phrase = DI::l10n()->t('<span %1$s>%2$d people</span> reshared this', $spanatts, $total);
|
||||
$explikers = DI::l10n()->t('%s reshared this.', $likers);
|
||||
break;
|
||||
}
|
||||
|
||||
$expanded .= "\t" . '<p class="wall-item-' . $type . '-expanded" id="' . $type . 'list-' . $id . '" style="display: none;" >' . $explikers . EOL . '</p>';
|
||||
$expanded .= "\t" . '<p class="wall-item-' . $verb . '-expanded" id="' . $verb . 'list-' . $id . '" style="display: none;" >' . $explikers . EOL . '</p>';
|
||||
}
|
||||
|
||||
$o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('voting_fakelink.tpl'), [
|
||||
'$phrase' => $phrase,
|
||||
'$type' => $type,
|
||||
'$type' => $verb,
|
||||
'$id' => $id
|
||||
]);
|
||||
$o .= $expanded;
|
||||
|
@ -1088,40 +1177,18 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
|
|||
'$term' => DI::l10n()->t('Tag term:'),
|
||||
'$fileas' => DI::l10n()->t('Save to Folder:'),
|
||||
'$whereareu' => DI::l10n()->t('Where are you right now?'),
|
||||
'$delitems' => DI::l10n()->t("Delete item\x28s\x29?")
|
||||
'$delitems' => DI::l10n()->t("Delete item\x28s\x29?"),
|
||||
'$is_mobile' => DI::mode()->isMobile(),
|
||||
]);
|
||||
|
||||
$jotplugins = '';
|
||||
Hook::callAll('jot_tool', $jotplugins);
|
||||
|
||||
// Private/public post links for the non-JS ACL form
|
||||
$private_post = 1;
|
||||
if (!empty($_REQUEST['public'])) {
|
||||
$private_post = 0;
|
||||
}
|
||||
|
||||
$query_str = DI::args()->getQueryString();
|
||||
if (strpos($query_str, 'public=1') !== false) {
|
||||
$query_str = str_replace(['?public=1', '&public=1'], ['', ''], $query_str);
|
||||
}
|
||||
|
||||
/*
|
||||
* I think $a->query_string may never have ? in it, but I could be wrong
|
||||
* It looks like it's from the index.php?q=[etc] rewrite that the web
|
||||
* server does, which converts any ? to &, e.g. suggest&ignore=61 for suggest?ignore=61
|
||||
*/
|
||||
if (strpos($query_str, '?') === false) {
|
||||
$public_post_link = '?public=1';
|
||||
} else {
|
||||
$public_post_link = '&public=1';
|
||||
}
|
||||
|
||||
// $tpl = Renderer::replaceMacros($tpl,array('$jotplugins' => $jotplugins));
|
||||
$tpl = Renderer::getMarkupTemplate("jot.tpl");
|
||||
|
||||
$o .= Renderer::replaceMacros($tpl,[
|
||||
$o .= Renderer::replaceMacros($tpl, [
|
||||
'$new_post' => DI::l10n()->t('New Post'),
|
||||
'$return_path' => $query_str,
|
||||
'$return_path' => DI::args()->getQueryString(),
|
||||
'$action' => 'item',
|
||||
'$share' => ($x['button'] ?? '') ?: DI::l10n()->t('Share'),
|
||||
'$loading' => DI::l10n()->t('Loading...'),
|
||||
|
@ -1147,7 +1214,7 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
|
|||
'$placeholdercategory' => Feature::isEnabled(local_user(), 'categories') ? DI::l10n()->t("Categories \x28comma-separated list\x29") : '',
|
||||
'$wait' => DI::l10n()->t('Please wait'),
|
||||
'$permset' => DI::l10n()->t('Permission settings'),
|
||||
'$shortpermset' => DI::l10n()->t('permissions'),
|
||||
'$shortpermset' => DI::l10n()->t('Permissions'),
|
||||
'$wall' => $notes_cid ? 0 : 1,
|
||||
'$posttype' => $notes_cid ? Item::PT_PERSONAL_NOTE : Item::PT_ARTICLE,
|
||||
'$content' => $x['content'] ?? '',
|
||||
|
@ -1169,11 +1236,6 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
|
|||
|
||||
// ACL permissions box
|
||||
'$acl' => $x['acl'],
|
||||
'$group_perms' => DI::l10n()->t('Post to Groups'),
|
||||
'$contact_perms' => DI::l10n()->t('Post to Contacts'),
|
||||
'$private' => DI::l10n()->t('Private post'),
|
||||
'$is_private' => $private_post,
|
||||
'$public_link' => $public_post_link,
|
||||
|
||||
//jot nav tab (used in some themes)
|
||||
'$message' => DI::l10n()->t('Message'),
|
||||
|
@ -1202,7 +1264,7 @@ function get_item_children(array &$item_list, array $parent, $recursive = true)
|
|||
{
|
||||
$children = [];
|
||||
foreach ($item_list as $i => $item) {
|
||||
if ($item['id'] != $item['parent']) {
|
||||
if ($item['gravity'] != GRAVITY_PARENT) {
|
||||
if ($recursive) {
|
||||
// Fallback to parent-uri if thr-parent is not set
|
||||
$thr_parent = $item['thr-parent'];
|
||||
|
@ -1350,7 +1412,7 @@ function conv_sort(array $item_list, $order)
|
|||
|
||||
// Extract the top level items
|
||||
foreach ($item_array as $item) {
|
||||
if ($item['id'] == $item['parent']) {
|
||||
if ($item['gravity'] == GRAVITY_PARENT) {
|
||||
$parents[] = $item;
|
||||
}
|
||||
}
|
||||
|
@ -1447,13 +1509,3 @@ function sort_thr_commented(array $a, array $b)
|
|||
{
|
||||
return strcmp($b['commented'], $a['commented']);
|
||||
}
|
||||
|
||||
function render_location_dummy(array $item) {
|
||||
if (!empty($item['location']) && !empty($item['location'])) {
|
||||
return $item['location'];
|
||||
}
|
||||
|
||||
if (!empty($item['coord']) && !empty($item['coord'])) {
|
||||
return $item['coord'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ use Friendica\Core\Renderer;
|
|||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\ItemContent;
|
||||
use Friendica\Model\Notify;
|
||||
|
@ -37,10 +38,10 @@ use Friendica\Protocol\Activity;
|
|||
* Creates a notification entry and possibly sends a mail
|
||||
*
|
||||
* @param array $params Array with the elements:
|
||||
* uid, item, parent, type, otype, verb, event,
|
||||
* link, subject, body, to_name, to_email, source_name,
|
||||
* source_link, activity, preamble, notify_flags,
|
||||
* language, show_in_notification_page
|
||||
* type, event, otype, activity, verb, uid, cid, origin_cid, item, link,
|
||||
* source_name, source_mail, source_nick, source_link, source_photo,
|
||||
* show_in_notification_page
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
|
@ -55,7 +56,7 @@ function notification($params)
|
|||
}
|
||||
|
||||
// Ensure that the important fields are set at any time
|
||||
$fields = ['notify-flags', 'language', 'username', 'email'];
|
||||
$fields = ['nickname', 'page-flags', 'notify-flags', 'language', 'username', 'email'];
|
||||
$user = DBA::selectFirst('user', $fields, ['uid' => $params['uid']]);
|
||||
|
||||
if (!DBA::isResult($user)) {
|
||||
|
@ -63,14 +64,39 @@ function notification($params)
|
|||
return false;
|
||||
}
|
||||
|
||||
$params['notify_flags'] = ($params['notify_flags'] ?? '') ?: $user['notify-flags'];
|
||||
$params['language'] = ($params['language'] ?? '') ?: $user['language'];
|
||||
$params['to_name'] = ($params['to_name'] ?? '') ?: $user['username'];
|
||||
$params['to_email'] = ($params['to_email'] ?? '') ?: $user['email'];
|
||||
// There is no need to create notifications for forum accounts
|
||||
if (in_array($user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$nickname = $user['nickname'];
|
||||
|
||||
$params['notify_flags'] = $user['notify-flags'];
|
||||
$params['language'] = $user['language'];
|
||||
$params['to_name'] = $user['username'];
|
||||
$params['to_email'] = $user['email'];
|
||||
|
||||
// from here on everything is in the recipients language
|
||||
$l10n = DI::l10n()->withLang($params['language']);
|
||||
|
||||
if (!empty($params['cid'])) {
|
||||
$contact = Contact::getById($params['cid'], ['url', 'name', 'photo']);
|
||||
if (DBA::isResult($contact)) {
|
||||
$params['source_link'] = $contact['url'];
|
||||
$params['source_name'] = $contact['name'];
|
||||
$params['source_photo'] = $contact['photo'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($params['origin_cid'])) {
|
||||
$contact = Contact::getById($params['origin_cid'], ['url', 'name', 'photo']);
|
||||
if (DBA::isResult($contact)) {
|
||||
$params['origin_link'] = $contact['url'];
|
||||
$params['origin_name'] = $contact['name'];
|
||||
$params['origin_photo'] = $contact['photo'];
|
||||
}
|
||||
}
|
||||
|
||||
$siteurl = DI::baseUrl()->get(true);
|
||||
$sitename = DI::config()->get('config', 'sitename');
|
||||
|
||||
|
@ -79,51 +105,23 @@ function notification($params)
|
|||
$hostname = substr($hostname, 0, strpos($hostname, ':'));
|
||||
}
|
||||
|
||||
$user = User::getById($params['uid'], ['nickname', 'page-flags']);
|
||||
|
||||
// There is no need to create notifications for forum accounts
|
||||
if (!DBA::isResult($user) || in_array($user["page-flags"], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])) {
|
||||
return false;
|
||||
}
|
||||
$nickname = $user["nickname"];
|
||||
// Creates a new email builder for the notification email
|
||||
$emailBuilder = DI::emailer()->newNotifyMail();
|
||||
|
||||
// with $params['show_in_notification_page'] == false, the notification isn't inserted into
|
||||
// the database, and an email is sent if applicable.
|
||||
// default, if not specified: true
|
||||
$show_in_notification_page = isset($params['show_in_notification_page']) ? $params['show_in_notification_page'] : true;
|
||||
|
||||
$additional_mail_header = "X-Friendica-Account: <".$nickname."@".$hostname.">\n";
|
||||
$emailBuilder->setHeader('X-Friendica-Account', '<' . $nickname . '@' . $hostname . '>');
|
||||
|
||||
if (array_key_exists('item', $params)) {
|
||||
$title = $params['item']['title'];
|
||||
$body = $params['item']['body'];
|
||||
} else {
|
||||
$title = $body = '';
|
||||
}
|
||||
$title = $params['item']['title'] ?? '';
|
||||
$body = $params['item']['body'] ?? '';
|
||||
|
||||
if (isset($params['item']['id'])) {
|
||||
$item_id = $params['item']['id'];
|
||||
} else {
|
||||
$item_id = 0;
|
||||
}
|
||||
|
||||
if (isset($params['item']['uri-id'])) {
|
||||
$uri_id = $params['item']['uri-id'];
|
||||
} else {
|
||||
$uri_id = 0;
|
||||
}
|
||||
|
||||
if (isset($params['parent'])) {
|
||||
$parent_id = $params['parent'];
|
||||
} else {
|
||||
$parent_id = 0;
|
||||
}
|
||||
|
||||
if (isset($params['item']['parent-uri-id'])) {
|
||||
$parent_uri_id = $params['item']['parent-uri-id'];
|
||||
} else {
|
||||
$parent_uri_id = 0;
|
||||
}
|
||||
$item_id = $params['item']['id'] ?? 0;
|
||||
$uri_id = $params['item']['uri-id'] ?? 0;
|
||||
$parent_id = $params['item']['parent'] ?? 0;
|
||||
$parent_uri_id = $params['item']['parent-uri-id'] ?? 0;
|
||||
|
||||
$epreamble = '';
|
||||
$preamble = '';
|
||||
|
@ -134,8 +132,7 @@ function notification($params)
|
|||
$itemlink = '';
|
||||
|
||||
if ($params['type'] == Notify\Type::MAIL) {
|
||||
$itemlink = $siteurl.'/message/'.$params['item']['id'];
|
||||
$params["link"] = $itemlink;
|
||||
$itemlink = $params['link'];
|
||||
|
||||
$subject = $l10n->t('%s New mail received at %s', $subjectPrefix, $sitename);
|
||||
|
||||
|
@ -143,8 +140,11 @@ function notification($params)
|
|||
$epreamble = $l10n->t('%1$s sent you %2$s.', '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', '[url=' . $itemlink . ']' . $l10n->t('a private message').'[/url]');
|
||||
|
||||
$sitelink = $l10n->t('Please visit %s to view and/or reply to your private messages.');
|
||||
$tsitelink = sprintf($sitelink, $siteurl.'/message/'.$params['item']['id']);
|
||||
$hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'/message/'.$params['item']['id'].'">'.$sitename.'</a>');
|
||||
$tsitelink = sprintf($sitelink, $itemlink);
|
||||
$hsitelink = sprintf($sitelink, '<a href="' . $itemlink . '">' . $sitename . '</a>');
|
||||
|
||||
// Mail notifications aren't using the "notify" table entry
|
||||
$show_in_notification_page = false;
|
||||
}
|
||||
|
||||
if ($params['type'] == Notify\Type::COMMENT || $params['type'] == Notify\Type::TAG_SELF) {
|
||||
|
@ -259,6 +259,7 @@ function notification($params)
|
|||
}
|
||||
|
||||
if ($params['type'] == Notify\Type::SHARE) {
|
||||
if ($params['origin_link'] == $params['source_link']) {
|
||||
$subject = $l10n->t('%s %s shared a new post', $subjectPrefix, $params['source_name']);
|
||||
|
||||
$preamble = $l10n->t('%1$s shared a new post at %2$s', $params['source_name'], $sitename);
|
||||
|
@ -266,6 +267,15 @@ function notification($params)
|
|||
'[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
|
||||
$params['link']
|
||||
);
|
||||
} else {
|
||||
$subject = $l10n->t('%s %s shared a post from %s', $subjectPrefix, $params['source_name'], $params['origin_name']);
|
||||
|
||||
$preamble = $l10n->t('%1$s shared a post from %2$s at %3$s', $params['source_name'], $params['origin_name'], $sitename);
|
||||
$epreamble = $l10n->t('%1$s [url=%2$s]shared a post[/url] from %3$s.',
|
||||
'[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
|
||||
$params['link'], '[url='.$params['origin_link'].']'.$params['origin_name'].'[/url]'
|
||||
);
|
||||
}
|
||||
|
||||
$sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf($sitelink, $siteurl);
|
||||
|
@ -463,21 +473,35 @@ function notification($params)
|
|||
$notify_id = 0;
|
||||
|
||||
if ($show_in_notification_page) {
|
||||
$notification = DI::notify()->insert([
|
||||
$fields = [
|
||||
'name' => $params['source_name'] ?? '',
|
||||
'name_cache' => substr(strip_tags(BBCode::convert($params['source_name'] ?? '')), 0, 255),
|
||||
'name_cache' => substr(strip_tags(BBCode::convert($params['source_name'])), 0, 255),
|
||||
'url' => $params['source_link'] ?? '',
|
||||
'photo' => $params['source_photo'] ?? '',
|
||||
'link' => $itemlink ?? '',
|
||||
'uid' => $params['uid'] ?? 0,
|
||||
'iid' => $item_id,
|
||||
'uri-id' => $uri_id,
|
||||
'parent' => $parent_id,
|
||||
'parent-uri-id' => $parent_uri_id,
|
||||
'type' => $params['type'] ?? '',
|
||||
'verb' => $params['verb'] ?? '',
|
||||
'otype' => $params['otype'] ?? '',
|
||||
]);
|
||||
];
|
||||
if (!empty($item_id)) {
|
||||
$fields['iid'] = $item_id;
|
||||
}
|
||||
if (!empty($uri_id)) {
|
||||
$fields['uri-id'] = $uri_id;
|
||||
}
|
||||
if (!empty($parent_id)) {
|
||||
$fields['parent'] = $parent_id;
|
||||
}
|
||||
if (!empty($parent_uri_id)) {
|
||||
$fields['parent-uri-id'] = $parent_uri_id;
|
||||
}
|
||||
$notification = DI::notify()->insert($fields);
|
||||
|
||||
// Notification insertion can be intercepted by an addon registering the 'enotify_store' hook
|
||||
if (!$notification) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$notification->msg = Renderer::replaceMacros($epreamble, ['$itemlink' => $notification->link]);
|
||||
|
||||
|
@ -494,7 +518,8 @@ function notification($params)
|
|||
Logger::log('sending notification email');
|
||||
|
||||
if (isset($params['parent']) && (intval($params['parent']) != 0)) {
|
||||
$id_for_parent = $params['parent'] . "@" . $hostname;
|
||||
$parent = Item::selectFirst(['guid'], ['id' => $params['parent']]);
|
||||
$message_id = "<" . $parent['guid'] . "@" . gethostname() . ">";
|
||||
|
||||
// Is this the first email notification for this parent item and user?
|
||||
if (!DBA::exists('notify-threads', ['master-parent-item' => $params['parent'], 'receiver-uid' => $params['uid']])) {
|
||||
|
@ -505,13 +530,14 @@ function notification($params)
|
|||
'receiver-uid' => $params['uid'], 'parent-item' => 0];
|
||||
DBA::insert('notify-threads', $fields);
|
||||
|
||||
$additional_mail_header .= "Message-ID: <${id_for_parent}>\n";
|
||||
$emailBuilder->setHeader('Message-ID', $message_id);
|
||||
$log_msg = "include/enotify: No previous notification found for this parent:\n" .
|
||||
" parent: ${params['parent']}\n" . " uid : ${params['uid']}\n";
|
||||
Logger::log($log_msg, Logger::DEBUG);
|
||||
} else {
|
||||
// If not, just "follow" the thread.
|
||||
$additional_mail_header .= "References: <${id_for_parent}>\nIn-Reply-To: <${id_for_parent}>\n";
|
||||
$emailBuilder->setHeader('References', $message_id);
|
||||
$emailBuilder->setHeader('In-Reply-To', $message_id);
|
||||
Logger::log("There's already a notification for this parent.", Logger::DEBUG);
|
||||
}
|
||||
}
|
||||
|
@ -530,14 +556,13 @@ function notification($params)
|
|||
'title' => $title,
|
||||
'body' => $body,
|
||||
'subject' => $subject,
|
||||
'headers' => $additional_mail_header,
|
||||
'headers' => $emailBuilder->getHeaders(),
|
||||
];
|
||||
|
||||
Hook::callAll('enotify_mail', $datarray);
|
||||
|
||||
$builder = DI::emailer()
|
||||
->newNotifyMail()
|
||||
->addHeaders($datarray['headers'])
|
||||
$emailBuilder
|
||||
->withHeaders($datarray['headers'])
|
||||
->withRecipient($params['to_email'])
|
||||
->forUser([
|
||||
'uid' => $datarray['uid'],
|
||||
|
@ -549,13 +574,13 @@ function notification($params)
|
|||
|
||||
// If a photo is present, add it to the email
|
||||
if (!empty($datarray['source_photo'])) {
|
||||
$builder->withPhoto(
|
||||
$emailBuilder->withPhoto(
|
||||
$datarray['source_photo'],
|
||||
$datarray['source_link'] ?? $sitelink,
|
||||
$datarray['source_name'] ?? $sitename);
|
||||
}
|
||||
|
||||
$email = $builder->build();
|
||||
$email = $emailBuilder->build();
|
||||
|
||||
// use the Emailer class to send the message
|
||||
return DI::emailer()->send($email);
|
||||
|
@ -589,10 +614,10 @@ function check_user_notification($itemid) {
|
|||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function check_item_notification($itemid, $uid, $notification_type) {
|
||||
$fields = ['id', 'uri-id', 'mention', 'parent', 'parent-uri-id', 'title', 'body',
|
||||
'author-link', 'author-name', 'author-avatar', 'author-id',
|
||||
'guid', 'parent-uri', 'uri', 'contact-id', 'network'];
|
||||
$condition = ['id' => $itemid, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'deleted' => false];
|
||||
$fields = ['id', 'uri-id', 'mention', 'parent', 'parent-uri-id', 'thr-parent-id',
|
||||
'title', 'body', 'author-link', 'author-name', 'author-avatar', 'author-id',
|
||||
'gravity', 'guid', 'parent-uri', 'uri', 'contact-id', 'network'];
|
||||
$condition = ['id' => $itemid, 'deleted' => false];
|
||||
$item = Item::selectFirstForUser($uid, $fields, $condition);
|
||||
if (!DBA::isResult($item)) {
|
||||
return false;
|
||||
|
@ -600,14 +625,11 @@ function check_item_notification($itemid, $uid, $notification_type) {
|
|||
|
||||
// Generate the notification array
|
||||
$params = [];
|
||||
$params['otype'] = Notify\ObjectType::ITEM;
|
||||
$params['uid'] = $uid;
|
||||
$params['origin_cid'] = $params['cid'] = $item['author-id'];
|
||||
$params['item'] = $item;
|
||||
$params['parent'] = $item['parent'];
|
||||
$params['link'] = DI::baseUrl() . '/display/' . urlencode($item['guid']);
|
||||
$params['otype'] = 'item';
|
||||
$params['source_name'] = $item['author-name'];
|
||||
$params['source_link'] = $item['author-link'];
|
||||
$params['source_photo'] = $item['author-avatar'];
|
||||
|
||||
// Set the activity flags
|
||||
$params['activity']['explicit_tagged'] = ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED);
|
||||
|
@ -625,6 +647,20 @@ function check_item_notification($itemid, $uid, $notification_type) {
|
|||
if ($notification_type & UserItem::NOTIF_SHARED) {
|
||||
$params['type'] = Notify\Type::SHARE;
|
||||
$params['verb'] = Activity::POST;
|
||||
|
||||
// Special treatment for posts that had been shared via "announce"
|
||||
if ($item['gravity'] == GRAVITY_ACTIVITY) {
|
||||
$parent_item = Item::selectFirst($fields, ['uri-id' => $item['thr-parent-id'], 'uid' => [$uid, 0]]);
|
||||
if (DBA::isResult($parent_item)) {
|
||||
// Don't notify on own entries
|
||||
if (User::getIdForURL($parent_item['author-link']) == $uid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$params['origin_cid'] = $parent_item['author-id'];
|
||||
$params['item'] = $parent_item;
|
||||
}
|
||||
}
|
||||
} elseif ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED) {
|
||||
$params['type'] = Notify\Type::TAG_SELF;
|
||||
$params['verb'] = Activity::TAG;
|
||||
|
|
|
@ -1,448 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2020, Friendica
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Protocol\DFRN;
|
||||
use Friendica\Protocol\Feed;
|
||||
use Friendica\Protocol\OStatus;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\ParseUrl;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
require_once __DIR__ . '/../mod/share.php';
|
||||
|
||||
function add_page_info_data(array $data, $no_photos = false)
|
||||
{
|
||||
Hook::callAll('page_info_data', $data);
|
||||
|
||||
if (empty($data['type'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// It maybe is a rich content, but if it does have everything that a link has,
|
||||
// then treat it that way
|
||||
if (($data["type"] == "rich") && is_string($data["title"]) &&
|
||||
is_string($data["text"]) && !empty($data["images"])) {
|
||||
$data["type"] = "link";
|
||||
}
|
||||
|
||||
$data["title"] = $data["title"] ?? '';
|
||||
|
||||
if ((($data["type"] != "link") && ($data["type"] != "video") && ($data["type"] != "photo")) || ($data["title"] == $data["url"])) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if ($no_photos && ($data["type"] == "photo")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Escape some bad characters
|
||||
$data["url"] = str_replace(["[", "]"], ["[", "]"], htmlentities($data["url"], ENT_QUOTES, 'UTF-8', false));
|
||||
$data["title"] = str_replace(["[", "]"], ["[", "]"], htmlentities($data["title"], ENT_QUOTES, 'UTF-8', false));
|
||||
|
||||
$text = "[attachment type='".$data["type"]."'";
|
||||
|
||||
if (empty($data["text"])) {
|
||||
$data["text"] = $data["title"];
|
||||
}
|
||||
|
||||
if (empty($data["text"])) {
|
||||
$data["text"] = $data["url"];
|
||||
}
|
||||
|
||||
if (!empty($data["url"])) {
|
||||
$text .= " url='".$data["url"]."'";
|
||||
}
|
||||
|
||||
if (!empty($data["title"])) {
|
||||
$text .= " title='".$data["title"]."'";
|
||||
}
|
||||
|
||||
// Only embedd a picture link when it seems to be a valid picture ("width" is set)
|
||||
if (!empty($data["images"]) && !empty($data["images"][0]["width"])) {
|
||||
$preview = str_replace(["[", "]"], ["[", "]"], htmlentities($data["images"][0]["src"], ENT_QUOTES, 'UTF-8', false));
|
||||
// if the preview picture is larger than 500 pixels then show it in a larger mode
|
||||
// But only, if the picture isn't higher than large (To prevent huge posts)
|
||||
if (!DI::config()->get('system', 'always_show_preview') && ($data["images"][0]["width"] >= 500)
|
||||
&& ($data["images"][0]["width"] >= $data["images"][0]["height"])) {
|
||||
$text .= " image='".$preview."'";
|
||||
} else {
|
||||
$text .= " preview='".$preview."'";
|
||||
}
|
||||
}
|
||||
|
||||
$text .= "]".$data["text"]."[/attachment]";
|
||||
|
||||
$hashtags = "";
|
||||
if (isset($data["keywords"]) && count($data["keywords"])) {
|
||||
$hashtags = "\n";
|
||||
foreach ($data["keywords"] as $keyword) {
|
||||
/// @TODO make a positive list of allowed characters
|
||||
$hashtag = str_replace([' ', '+', '/', '.', '#', '@', "'", '"', '’', '`', '(', ')', '„', '“'], '', $keyword);
|
||||
$hashtags .= "#[url=" . DI::baseUrl() . "/search?tag=" . $hashtag . "]" . $hashtag . "[/url] ";
|
||||
}
|
||||
}
|
||||
|
||||
return "\n".$text.$hashtags;
|
||||
}
|
||||
|
||||
function query_page_info($url, $photo = "", $keywords = false, $keyword_blacklist = "")
|
||||
{
|
||||
$data = ParseUrl::getSiteinfoCached($url, true);
|
||||
|
||||
if ($photo != "") {
|
||||
$data["images"][0]["src"] = $photo;
|
||||
}
|
||||
|
||||
Logger::log('fetch page info for ' . $url . ' ' . print_r($data, true), Logger::DEBUG);
|
||||
|
||||
if (!$keywords && isset($data["keywords"])) {
|
||||
unset($data["keywords"]);
|
||||
}
|
||||
|
||||
if (($keyword_blacklist != "") && isset($data["keywords"])) {
|
||||
$list = explode(", ", $keyword_blacklist);
|
||||
|
||||
foreach ($list as $keyword) {
|
||||
$keyword = trim($keyword);
|
||||
|
||||
$index = array_search($keyword, $data["keywords"]);
|
||||
if ($index !== false) {
|
||||
unset($data["keywords"][$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function get_page_keywords($url, $photo = "", $keywords = false, $keyword_blacklist = "")
|
||||
{
|
||||
$data = query_page_info($url, $photo, $keywords, $keyword_blacklist);
|
||||
if (empty($data["keywords"]) || !is_array($data["keywords"])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$taglist = [];
|
||||
foreach ($data['keywords'] as $keyword) {
|
||||
$hashtag = str_replace([" ", "+", "/", ".", "#", "'"],
|
||||
["", "", "", "", "", ""], $keyword);
|
||||
|
||||
$taglist[] = $hashtag;
|
||||
}
|
||||
|
||||
return $taglist;
|
||||
}
|
||||
|
||||
function add_page_info($url, $no_photos = false, $photo = "", $keywords = false, $keyword_blacklist = "")
|
||||
{
|
||||
$data = query_page_info($url, $photo, $keywords, $keyword_blacklist);
|
||||
|
||||
$text = '';
|
||||
|
||||
if (is_array($data)) {
|
||||
$text = add_page_info_data($data, $no_photos);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
function add_page_info_to_body($body, $texturl = false, $no_photos = false)
|
||||
{
|
||||
Logger::log('add_page_info_to_body: fetch page info for body ' . $body, Logger::DEBUG);
|
||||
|
||||
$URLSearchString = "^\[\]";
|
||||
|
||||
// Fix for Mastodon where the mentions are in a different format
|
||||
$body = preg_replace("/\[url\=([$URLSearchString]*)\]([#!@])(.*?)\[\/url\]/ism",
|
||||
'$2[url=$1]$3[/url]', $body);
|
||||
|
||||
// Adding these spaces is a quick hack due to my problems with regular expressions :)
|
||||
preg_match("/[^!#@]\[url\]([$URLSearchString]*)\[\/url\]/ism", " " . $body, $matches);
|
||||
|
||||
if (!$matches) {
|
||||
preg_match("/[^!#@]\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", " " . $body, $matches);
|
||||
}
|
||||
|
||||
// Convert urls without bbcode elements
|
||||
if (!$matches && $texturl) {
|
||||
preg_match("/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", " ".$body, $matches);
|
||||
|
||||
// Yeah, a hack. I really hate regular expressions :)
|
||||
if ($matches) {
|
||||
$matches[1] = $matches[2];
|
||||
}
|
||||
}
|
||||
|
||||
if ($matches) {
|
||||
$footer = add_page_info($matches[1], $no_photos);
|
||||
}
|
||||
|
||||
// Remove the link from the body if the link is attached at the end of the post
|
||||
if (isset($footer) && (trim($footer) != "") && (strpos($footer, $matches[1]))) {
|
||||
$removedlink = trim(str_replace($matches[1], "", $body));
|
||||
if (($removedlink == "") || strstr($body, $removedlink)) {
|
||||
$body = $removedlink;
|
||||
}
|
||||
|
||||
$removedlink = preg_replace("/\[url\=" . preg_quote($matches[1], '/') . "\](.*?)\[\/url\]/ism", '', $body);
|
||||
if (($removedlink == "") || strstr($body, $removedlink)) {
|
||||
$body = $removedlink;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the page information to the bottom
|
||||
if (isset($footer) && (trim($footer) != "")) {
|
||||
$body .= $footer;
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* consume_feed - process atom feed and update anything/everything we might need to update
|
||||
*
|
||||
* $xml = the (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds.
|
||||
*
|
||||
* $importer = the contact_record (joined to user_record) of the local user who owns this relationship.
|
||||
* It is this person's stuff that is going to be updated.
|
||||
* $contact = the person who is sending us stuff. If not set, we MAY be processing a "follow" activity
|
||||
* from an external network and MAY create an appropriate contact record. Otherwise, we MUST
|
||||
* have a contact record.
|
||||
* $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or
|
||||
* might not) try and subscribe to it.
|
||||
* $datedir sorts in reverse order
|
||||
* $pass - by default ($pass = 0) we cannot guarantee that a parent item has been
|
||||
* imported prior to its children being seen in the stream unless we are certain
|
||||
* of how the feed is arranged/ordered.
|
||||
* With $pass = 1, we only pull parent items out of the stream.
|
||||
* With $pass = 2, we only pull children (comments/likes).
|
||||
*
|
||||
* So running this twice, first with pass 1 and then with pass 2 will do the right
|
||||
* thing regardless of feed ordering. This won't be adequate in a fully-threaded
|
||||
* model where comments can have sub-threads. That would require some massive sorting
|
||||
* to get all the feed items into a mostly linear ordering, and might still require
|
||||
* recursion.
|
||||
*
|
||||
* @param $xml
|
||||
* @param array $importer
|
||||
* @param array $contact
|
||||
* @param $hub
|
||||
* @throws ImagickException
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function consume_feed($xml, array $importer, array $contact, &$hub)
|
||||
{
|
||||
if ($contact['network'] === Protocol::OSTATUS) {
|
||||
Logger::log("Consume OStatus messages ", Logger::DEBUG);
|
||||
OStatus::import($xml, $importer, $contact, $hub);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($contact['network'] === Protocol::FEED) {
|
||||
Logger::log("Consume feeds", Logger::DEBUG);
|
||||
Feed::import($xml, $importer, $contact);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($contact['network'] === Protocol::DFRN) {
|
||||
Logger::log("Consume DFRN messages", Logger::DEBUG);
|
||||
$dfrn_importer = DFRN::getImporter($contact["id"], $importer["uid"]);
|
||||
if (!empty($dfrn_importer)) {
|
||||
Logger::log("Now import the DFRN feed");
|
||||
DFRN::import($xml, $dfrn_importer, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function subscribe_to_hub($url, array $importer, array $contact, $hubmode = 'subscribe')
|
||||
{
|
||||
/*
|
||||
* Diaspora has different message-ids in feeds than they do
|
||||
* through the direct Diaspora protocol. If we try and use
|
||||
* the feed, we'll get duplicates. So don't.
|
||||
*/
|
||||
if ($contact['network'] === Protocol::DIASPORA) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Without an importer we don't have a user id - so we quit
|
||||
if (empty($importer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = DBA::selectFirst('user', ['nickname'], ['uid' => $importer['uid']]);
|
||||
|
||||
// No user, no nickname, we quit
|
||||
if (!DBA::isResult($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$push_url = DI::baseUrl() . '/pubsub/' . $user['nickname'] . '/' . $contact['id'];
|
||||
|
||||
// Use a single verify token, even if multiple hubs
|
||||
$verify_token = ((strlen($contact['hub-verify'])) ? $contact['hub-verify'] : Strings::getRandomHex());
|
||||
|
||||
$params= 'hub.mode=' . $hubmode . '&hub.callback=' . urlencode($push_url) . '&hub.topic=' . urlencode($contact['poll']) . '&hub.verify=async&hub.verify_token=' . $verify_token;
|
||||
|
||||
Logger::log('subscribe_to_hub: ' . $hubmode . ' ' . $contact['name'] . ' to hub ' . $url . ' endpoint: ' . $push_url . ' with verifier ' . $verify_token);
|
||||
|
||||
if (!strlen($contact['hub-verify']) || ($contact['hub-verify'] != $verify_token)) {
|
||||
DBA::update('contact', ['hub-verify' => $verify_token], ['id' => $contact['id']]);
|
||||
}
|
||||
|
||||
$postResult = Network::post($url, $params);
|
||||
|
||||
Logger::log('subscribe_to_hub: returns: ' . $postResult->getReturnCode(), Logger::DEBUG);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
function drop_items(array $items)
|
||||
{
|
||||
$uid = 0;
|
||||
|
||||
if (!Session::isAuthenticated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($items)) {
|
||||
foreach ($items as $item) {
|
||||
$owner = Item::deleteForUser(['id' => $item], local_user());
|
||||
|
||||
if ($owner && !$uid) {
|
||||
$uid = $owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drop_item($id, $return = '')
|
||||
{
|
||||
$a = DI::app();
|
||||
|
||||
// locate item to be deleted
|
||||
|
||||
$fields = ['id', 'uid', 'guid', 'contact-id', 'deleted', 'gravity', 'parent'];
|
||||
$item = Item::selectFirstForUser(local_user(), $fields, ['id' => $id]);
|
||||
|
||||
if (!DBA::isResult($item)) {
|
||||
notice(DI::l10n()->t('Item not found.') . EOL);
|
||||
DI::baseUrl()->redirect('network');
|
||||
}
|
||||
|
||||
if ($item['deleted']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$contact_id = 0;
|
||||
|
||||
// check if logged in user is either the author or owner of this item
|
||||
if (Session::getRemoteContactID($item['uid']) == $item['contact-id']) {
|
||||
$contact_id = $item['contact-id'];
|
||||
}
|
||||
|
||||
if ((local_user() == $item['uid']) || $contact_id) {
|
||||
// Check if we should do HTML-based delete confirmation
|
||||
if (!empty($_REQUEST['confirm'])) {
|
||||
// <form> can't take arguments in its "action" parameter
|
||||
// so add any arguments as hidden inputs
|
||||
$query = explode_querystring(DI::args()->getQueryString());
|
||||
$inputs = [];
|
||||
|
||||
foreach ($query['args'] as $arg) {
|
||||
if (strpos($arg, 'confirm=') === false) {
|
||||
$arg_parts = explode('=', $arg);
|
||||
$inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]];
|
||||
}
|
||||
}
|
||||
|
||||
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
|
||||
'$method' => 'get',
|
||||
'$message' => DI::l10n()->t('Do you really want to delete this item?'),
|
||||
'$extra_inputs' => $inputs,
|
||||
'$confirm' => DI::l10n()->t('Yes'),
|
||||
'$confirm_url' => $query['base'],
|
||||
'$confirm_name' => 'confirmed',
|
||||
'$cancel' => DI::l10n()->t('Cancel'),
|
||||
]);
|
||||
}
|
||||
// Now check how the user responded to the confirmation query
|
||||
if (!empty($_REQUEST['canceled'])) {
|
||||
DI::baseUrl()->redirect('display/' . $item['guid']);
|
||||
}
|
||||
|
||||
$is_comment = ($item['gravity'] == GRAVITY_COMMENT) ? true : false;
|
||||
$parentitem = null;
|
||||
if (!empty($item['parent'])){
|
||||
$fields = ['guid'];
|
||||
$parentitem = Item::selectFirstForUser(local_user(), $fields, ['id' => $item['parent']]);
|
||||
}
|
||||
|
||||
// delete the item
|
||||
Item::deleteForUser(['id' => $item['id']], local_user());
|
||||
|
||||
$return_url = hex2bin($return);
|
||||
|
||||
// removes update_* from return_url to ignore Ajax refresh
|
||||
$return_url = str_replace("update_", "", $return_url);
|
||||
|
||||
// Check if delete a comment
|
||||
if ($is_comment) {
|
||||
// Return to parent guid
|
||||
if (!empty($parentitem)) {
|
||||
DI::baseUrl()->redirect('display/' . $parentitem['guid']);
|
||||
//NOTREACHED
|
||||
}
|
||||
// In case something goes wrong
|
||||
else {
|
||||
DI::baseUrl()->redirect('network');
|
||||
//NOTREACHED
|
||||
}
|
||||
}
|
||||
else {
|
||||
// if unknown location or deleting top level post called from display
|
||||
if (empty($return_url) || strpos($return_url, 'display') !== false) {
|
||||
DI::baseUrl()->redirect('network');
|
||||
//NOTREACHED
|
||||
} else {
|
||||
DI::baseUrl()->redirect($return_url);
|
||||
//NOTREACHED
|
||||
}
|
||||
}
|
||||
} else {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
DI::baseUrl()->redirect('display/' . $item['guid']);
|
||||
//NOTREACHED
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
use Dice\Dice;
|
||||
|
||||
$start_time = microtime(true);
|
||||
|
||||
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
|
||||
die('Vendor path not found. Please execute "bin/composer.phar --no-dev install" on the command line in the web root.');
|
||||
}
|
||||
|
@ -34,10 +36,13 @@ $dice = $dice->addRule(Friendica\App\Mode::class, ['call' => [['determineRunMode
|
|||
|
||||
$a = \Friendica\DI::app();
|
||||
|
||||
\Friendica\DI::mode()->setExecutor(\Friendica\App\Mode::INDEX);
|
||||
|
||||
$a->runFrontend(
|
||||
$dice->create(\Friendica\App\Module::class),
|
||||
$dice->create(\Friendica\App\Router::class),
|
||||
$dice->create(\Friendica\Core\PConfig\IPConfig::class),
|
||||
$dice->create(\Friendica\App\Authentication::class),
|
||||
$dice->create(\Friendica\App\Page::class)
|
||||
$dice->create(\Friendica\Security\Authentication::class),
|
||||
$dice->create(\Friendica\App\Page::class),
|
||||
$start_time
|
||||
);
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
<?php
|
||||
//-----------------------------------------------------------------------------
|
||||
// ASNValue class by A.Oliinyk
|
||||
// contact@pumka.net
|
||||
//-----------------------------------------------------------------------------
|
||||
class ASNValue
|
||||
{
|
||||
const TAG_INTEGER = 0x02;
|
||||
const TAG_BITSTRING = 0x03;
|
||||
const TAG_SEQUENCE = 0x30;
|
||||
|
||||
public $Tag;
|
||||
public $Value;
|
||||
|
||||
function __construct($Tag=0x00, $Value='')
|
||||
{
|
||||
$this->Tag = $Tag;
|
||||
$this->Value = $Value;
|
||||
}
|
||||
|
||||
function Encode()
|
||||
{
|
||||
//Write type
|
||||
$result = chr($this->Tag);
|
||||
|
||||
//Write size
|
||||
$size = strlen($this->Value);
|
||||
if ($size < 127) {
|
||||
//Write size as is
|
||||
$result .= chr($size);
|
||||
}
|
||||
else {
|
||||
//Prepare length sequence
|
||||
$sizeBuf = self::IntToBin($size);
|
||||
|
||||
//Write length sequence
|
||||
$firstByte = 0x80 + strlen($sizeBuf);
|
||||
$result .= chr($firstByte) . $sizeBuf;
|
||||
}
|
||||
|
||||
//Write value
|
||||
$result .= $this->Value;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function Decode(&$Buffer)
|
||||
{
|
||||
//Read type
|
||||
$this->Tag = self::ReadByte($Buffer);
|
||||
|
||||
//Read first byte
|
||||
$firstByte = self::ReadByte($Buffer);
|
||||
|
||||
if ($firstByte < 127) {
|
||||
$size = $firstByte;
|
||||
}
|
||||
else if ($firstByte > 127) {
|
||||
$sizeLen = $firstByte - 0x80;
|
||||
//Read length sequence
|
||||
$size = self::BinToInt(self::ReadBytes($Buffer, $sizeLen));
|
||||
}
|
||||
else {
|
||||
throw new Exception("Invalid ASN length value");
|
||||
}
|
||||
|
||||
$this->Value = self::ReadBytes($Buffer, $size);
|
||||
}
|
||||
|
||||
protected static function ReadBytes(&$Buffer, $Length)
|
||||
{
|
||||
$result = substr($Buffer, 0, $Length);
|
||||
$Buffer = substr($Buffer, $Length);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected static function ReadByte(&$Buffer)
|
||||
{
|
||||
return ord(self::ReadBytes($Buffer, 1));
|
||||
}
|
||||
|
||||
protected static function BinToInt($Bin)
|
||||
{
|
||||
$len = strlen($Bin);
|
||||
$result = 0;
|
||||
for ($i=0; $i<$len; $i++) {
|
||||
$curByte = self::ReadByte($Bin);
|
||||
$result += $curByte << (($len-$i-1)*8);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected static function IntToBin($Int)
|
||||
{
|
||||
$result = '';
|
||||
do {
|
||||
$curByte = $Int % 256;
|
||||
$result .= chr($curByte);
|
||||
|
||||
$Int = ($Int - $curByte) / 256;
|
||||
} while ($Int > 0);
|
||||
|
||||
$result = strrev($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function SetIntBuffer($Value)
|
||||
{
|
||||
if (strlen($Value) > 1) {
|
||||
$firstByte = ord($Value[0]);
|
||||
if ($firstByte & 0x80) { //first bit set
|
||||
$Value = chr(0x00) . $Value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->Value = $Value;
|
||||
}
|
||||
|
||||
function GetIntBuffer()
|
||||
{
|
||||
$result = $this->Value;
|
||||
if (ord($result[0]) == 0x00) {
|
||||
$result = substr($result, 1);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function SetInt($Value)
|
||||
{
|
||||
$Value = self::IntToBin($Value);
|
||||
|
||||
$this->SetIntBuffer($Value);
|
||||
}
|
||||
|
||||
function GetInt()
|
||||
{
|
||||
$result = $this->GetIntBuffer();
|
||||
$result = self::BinToInt($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function SetSequence($Values)
|
||||
{
|
||||
$result = '';
|
||||
foreach ($Values as $item) {
|
||||
$result .= $item->Encode();
|
||||
}
|
||||
|
||||
$this->Value = $result;
|
||||
}
|
||||
|
||||
function GetSequence()
|
||||
{
|
||||
$result = array();
|
||||
$seq = $this->Value;
|
||||
while (strlen($seq)) {
|
||||
$val = new ASNValue();
|
||||
$val->Decode($seq);
|
||||
$result[] = $val;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|