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, desktop browser. Community post displayed.*|
|
||||
|
||||
## Endorsements
|
||||
|
||||
- [](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).
|
||||
|
|
|
|||
2
VERSION
|
|
@ -1 +1 @@
|
|||
2020.06-dev
|
||||
2021.03-dev
|
||||
|
|
|
|||
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;
|
||||
}
|
||||
|
||||
Worker::spawnWorker($do_cron);
|
||||
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
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0d0fe0cafbe7bd050b41a5c9e96dba5f",
|
||||
"content-hash": "7d8031c9b95fd94d8872804759a26509",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asika/simple-console",
|
||||
|
|
@ -87,16 +87,16 @@
|
|||
},
|
||||
{
|
||||
"name": "bower-asset/Chart-js",
|
||||
"version": "v2.8.0",
|
||||
"version": "v2.9.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/chartjs/Chart.js.git",
|
||||
"reference": "947d8a7ccfbfc76dd9d384ea75436fa4a7aeefb1"
|
||||
"reference": "06f73dc3590084b2c464bf08189c7aee2b6b92d2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/chartjs/Chart.js/zipball/947d8a7ccfbfc76dd9d384ea75436fa4a7aeefb1",
|
||||
"reference": "947d8a7ccfbfc76dd9d384ea75436fa4a7aeefb1",
|
||||
"url": "https://api.github.com/repos/chartjs/Chart.js/zipball/06f73dc3590084b2c464bf08189c7aee2b6b92d2",
|
||||
"reference": "06f73dc3590084b2c464bf08189c7aee2b6b92d2",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "bower-asset-library",
|
||||
|
|
@ -115,20 +115,20 @@
|
|||
"MIT"
|
||||
],
|
||||
"description": "Simple HTML5 charts using the canvas element.",
|
||||
"time": "2019-03-14T13:03:00+00:00"
|
||||
"time": "2019-11-14T18:37:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bower-asset/base64",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/davidchambers/Base64.js.git",
|
||||
"reference": "10f0e9990dab0a73009fc106ff2b88102a0a13cf"
|
||||
"reference": "660b299aa4854843fd35d42b30eda9273125b9da"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/davidchambers/Base64.js/zipball/10f0e9990dab0a73009fc106ff2b88102a0a13cf",
|
||||
"reference": "10f0e9990dab0a73009fc106ff2b88102a0a13cf",
|
||||
"url": "https://api.github.com/repos/davidchambers/Base64.js/zipball/660b299aa4854843fd35d42b30eda9273125b9da",
|
||||
"reference": "660b299aa4854843fd35d42b30eda9273125b9da",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "bower-asset-library",
|
||||
|
|
@ -146,7 +146,7 @@
|
|||
"WTFPL"
|
||||
],
|
||||
"description": "Base64 encoding and decoding",
|
||||
"time": "2019-02-12T17:19:36+00:00"
|
||||
"time": "2019-11-02T20:07:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bower-asset/dompurify",
|
||||
|
|
@ -239,16 +239,16 @@
|
|||
},
|
||||
{
|
||||
"name": "bower-asset/vue",
|
||||
"version": "v2.6.10",
|
||||
"version": "v2.6.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vuejs/vue.git",
|
||||
"reference": "e90cc60c4718a69e2c919275a999b7370141f3bf"
|
||||
"reference": "ec78fc8b6d03e59da669be1adf4b4b5abf670a34"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vuejs/vue/zipball/e90cc60c4718a69e2c919275a999b7370141f3bf",
|
||||
"reference": "e90cc60c4718a69e2c919275a999b7370141f3bf",
|
||||
"url": "https://api.github.com/repos/vuejs/vue/zipball/ec78fc8b6d03e59da669be1adf4b4b5abf670a34",
|
||||
"reference": "ec78fc8b6d03e59da669be1adf4b4b5abf670a34",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "bower-asset-library"
|
||||
|
|
@ -394,21 +394,24 @@
|
|||
},
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
"version": "v4.7.0",
|
||||
"version": "v4.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ezyang/htmlpurifier.git",
|
||||
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40"
|
||||
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/ae1828d955112356f7677c465f94f7deb7d27a40",
|
||||
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40",
|
||||
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
|
||||
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
|
|
@ -416,11 +419,14 @@
|
|||
},
|
||||
"files": [
|
||||
"library/HTMLPurifier.composer.php"
|
||||
],
|
||||
"exclude-from-classmap": [
|
||||
"/library/HTMLPurifier/Language/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL"
|
||||
"LGPL-2.1-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
|
|
@ -434,7 +440,7 @@
|
|||
"keywords": [
|
||||
"html"
|
||||
],
|
||||
"time": "2015-08-05T01:03:42+00:00"
|
||||
"time": "2020-06-29T00:56:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "friendica/json-ld",
|
||||
|
|
@ -541,27 +547,29 @@
|
|||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "6.3.3",
|
||||
"version": "6.5.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
|
||||
"reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
|
||||
"reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
|
||||
"reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.0",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"php": ">=5.5"
|
||||
"guzzlehttp/psr7": "^1.6.1",
|
||||
"php": ">=5.5",
|
||||
"symfony/polyfill-intl-idn": "^1.17.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
|
||||
"psr/log": "^1.0"
|
||||
"psr/log": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Required for using the Log middleware"
|
||||
|
|
@ -569,16 +577,16 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "6.3-dev"
|
||||
"dev-master": "6.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
|
|
@ -602,7 +610,7 @@
|
|||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"time": "2018-04-22T15:46:56+00:00"
|
||||
"time": "2020-06-16T21:01:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
|
|
@ -792,16 +800,16 @@
|
|||
},
|
||||
{
|
||||
"name": "level-2/dice",
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Level-2/Dice.git",
|
||||
"reference": "e631f110f0520294fec902814c61cac26566023c"
|
||||
"reference": "b9336d9200d0165c31e982374dc5d8d2552807bc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Level-2/Dice/zipball/e631f110f0520294fec902814c61cac26566023c",
|
||||
"reference": "e631f110f0520294fec902814c61cac26566023c",
|
||||
"url": "https://api.github.com/repos/Level-2/Dice/zipball/b9336d9200d0165c31e982374dc5d8d2552807bc",
|
||||
"reference": "b9336d9200d0165c31e982374dc5d8d2552807bc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -834,7 +842,7 @@
|
|||
"di",
|
||||
"ioc"
|
||||
],
|
||||
"time": "2019-05-01T12:55:36+00:00"
|
||||
"time": "2020-01-28T13:47:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lightopenid/lightopenid",
|
||||
|
|
@ -870,22 +878,69 @@
|
|||
"time": "2013-10-27T16:25:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "michelf/php-markdown",
|
||||
"version": "1.8.0",
|
||||
"name": "matriphe/iso-639",
|
||||
"version": "1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/michelf/php-markdown.git",
|
||||
"reference": "01ab082b355bf188d907b9929cd99b2923053495"
|
||||
"url": "https://github.com/matriphe/php-iso-639.git",
|
||||
"reference": "0245d844daeefdd22a54b47103ffdb0e03c323e1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/01ab082b355bf188d907b9929cd99b2923053495",
|
||||
"reference": "01ab082b355bf188d907b9929cd99b2923053495",
|
||||
"url": "https://api.github.com/repos/matriphe/php-iso-639/zipball/0245d844daeefdd22a54b47103ffdb0e03c323e1",
|
||||
"reference": "0245d844daeefdd22a54b47103ffdb0e03c323e1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Matriphe\\ISO639\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Muhammad Zamroni",
|
||||
"email": "halo@matriphe.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP library to convert ISO-639-1 code to language name.",
|
||||
"keywords": [
|
||||
"639",
|
||||
"iso",
|
||||
"iso-639",
|
||||
"lang",
|
||||
"language",
|
||||
"laravel"
|
||||
],
|
||||
"time": "2017-07-19T15:11:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "michelf/php-markdown",
|
||||
"version": "1.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/michelf/php-markdown.git",
|
||||
"reference": "c83178d49e372ca967d1a8c77ae4e051b3a3c75c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/c83178d49e372ca967d1a8c77ae4e051b3a3c75c",
|
||||
"reference": "c83178d49e372ca967d1a8c77ae4e051b3a3c75c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": ">=4.3 <5.8"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
@ -913,7 +968,7 @@
|
|||
"keywords": [
|
||||
"markdown"
|
||||
],
|
||||
"time": "2018-01-15T00:49:33+00:00"
|
||||
"time": "2019-12-02T02:32:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mobiledetect/mobiledetectlib",
|
||||
|
|
@ -969,16 +1024,16 @@
|
|||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "1.25.1",
|
||||
"version": "1.25.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf"
|
||||
"reference": "3022efff205e2448b560c833c6fbbf91c3139168"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf",
|
||||
"reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/3022efff205e2448b560c833c6fbbf91c3139168",
|
||||
"reference": "3022efff205e2448b560c833c6fbbf91c3139168",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -992,11 +1047,10 @@
|
|||
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
|
||||
"doctrine/couchdb": "~1.0@dev",
|
||||
"graylog2/gelf-php": "~1.0",
|
||||
"jakub-onderka/php-parallel-lint": "0.9",
|
||||
"php-amqplib/php-amqplib": "~2.4",
|
||||
"php-console/php-console": "^3.1.3",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.0",
|
||||
"phpunit/phpunit": "~4.5",
|
||||
"phpunit/phpunit-mock-objects": "2.3.0",
|
||||
"ruflin/elastica": ">=0.90 <3.0",
|
||||
"sentry/sentry": "^0.13",
|
||||
"swiftmailer/swiftmailer": "^5.3|^6.0"
|
||||
|
|
@ -1043,7 +1097,7 @@
|
|||
"logging",
|
||||
"psr-3"
|
||||
],
|
||||
"time": "2019-09-06T13:49:17+00:00"
|
||||
"time": "2020-05-22T07:31:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/fast-route",
|
||||
|
|
@ -1270,12 +1324,69 @@
|
|||
"time": "2017-07-06T13:46:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/fullcalendar",
|
||||
"version": "3.10.1",
|
||||
"name": "npm-asset/eventemitter3",
|
||||
"version": "2.0.3",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-3.10.1.tgz",
|
||||
"shasum": "cca3f9a2656a7e978a3f3facb7f35934a91185db"
|
||||
"url": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
|
||||
"shasum": "b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
|
||||
},
|
||||
"type": "npm-asset-library",
|
||||
"extra": {
|
||||
"npm-asset-bugs": {
|
||||
"url": "https://github.com/primus/eventemitter3/issues"
|
||||
},
|
||||
"npm-asset-main": "index.js",
|
||||
"npm-asset-directories": [],
|
||||
"npm-asset-repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/primus/eventemitter3.git"
|
||||
},
|
||||
"npm-asset-scripts": {
|
||||
"build": "mkdir -p umd && browserify index.js -s EventEmitter3 | uglifyjs -m -o umd/eventemitter3.min.js",
|
||||
"benchmark": "find benchmarks/run -name '*.js' -exec benchmarks/start.sh {} \\;",
|
||||
"test": "nyc --reporter=html --reporter=text mocha",
|
||||
"test-browser": "zuul -- test.js",
|
||||
"prepublish": "npm run build",
|
||||
"sync": "node versions.js"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Arnout Kazemier"
|
||||
}
|
||||
],
|
||||
"description": "EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface.",
|
||||
"homepage": "https://github.com/primus/eventemitter3#readme",
|
||||
"keywords": [
|
||||
"EventEmitter",
|
||||
"EventEmitter2",
|
||||
"EventEmitter3",
|
||||
"Events",
|
||||
"addEventListener",
|
||||
"addListener",
|
||||
"emit",
|
||||
"emits",
|
||||
"emitter",
|
||||
"event",
|
||||
"once",
|
||||
"pub/sub",
|
||||
"publish",
|
||||
"reactor",
|
||||
"subscribe"
|
||||
],
|
||||
"time": "2017-03-31T14:51:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/fullcalendar",
|
||||
"version": "3.10.2",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-3.10.2.tgz",
|
||||
"shasum": "9b1ba84bb02803621b761d1bba91a4f18affafb7"
|
||||
},
|
||||
"type": "npm-asset-library",
|
||||
"extra": {
|
||||
|
|
@ -1313,7 +1424,7 @@
|
|||
"full-sized",
|
||||
"jquery-plugin"
|
||||
],
|
||||
"time": "2019-08-10T16:05:46+00:00"
|
||||
"time": "2020-05-19T03:44:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/imagesloaded",
|
||||
|
|
@ -1647,11 +1758,11 @@
|
|||
},
|
||||
{
|
||||
"name": "npm-asset/moment",
|
||||
"version": "2.24.0",
|
||||
"version": "2.26.0",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||
"shasum": "0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||
"url": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz",
|
||||
"shasum": "5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
|
||||
},
|
||||
"type": "npm-asset-library",
|
||||
"extra": {
|
||||
|
|
@ -1665,8 +1776,12 @@
|
|||
"url": "git+https://github.com/moment/moment.git"
|
||||
},
|
||||
"npm-asset-scripts": {
|
||||
"typescript-test": "tsc --project typing-tests",
|
||||
"ts3.1-typescript-test": "cross-env node_modules/typescript3/bin/tsc --project ts3.1-typing-tests",
|
||||
"typescript-test": "cross-env node_modules/typescript/bin/tsc --project typing-tests",
|
||||
"test": "grunt test",
|
||||
"eslint": "eslint Gruntfile.js tasks src",
|
||||
"prettier-check": "prettier --check Gruntfile.js tasks src",
|
||||
"prettier-fmt": "prettier --write Gruntfile.js tasks src",
|
||||
"coverage": "nyc npm test && nyc report",
|
||||
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls"
|
||||
},
|
||||
|
|
@ -1709,7 +1824,7 @@
|
|||
}
|
||||
],
|
||||
"description": "Parse, validate, manipulate, and display dates",
|
||||
"homepage": "http://momentjs.com",
|
||||
"homepage": "https://momentjs.com",
|
||||
"keywords": [
|
||||
"date",
|
||||
"ender",
|
||||
|
|
@ -1721,20 +1836,78 @@
|
|||
"time",
|
||||
"validate"
|
||||
],
|
||||
"time": "2019-01-21T21:10:34+00:00"
|
||||
"time": "2020-05-20T06:46:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/perfect-scrollbar",
|
||||
"version": "0.6.16",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-0.6.16.tgz",
|
||||
"shasum": "b1d61a5245cf3962bb9a8407a3fc669d923212fc"
|
||||
},
|
||||
"type": "npm-asset-library",
|
||||
"extra": {
|
||||
"npm-asset-bugs": {
|
||||
"url": "https://github.com/noraesae/perfect-scrollbar/issues"
|
||||
},
|
||||
"npm-asset-files": [
|
||||
"dist",
|
||||
"src",
|
||||
"index.js",
|
||||
"jquery.js",
|
||||
"perfect-scrollbar.d.ts"
|
||||
],
|
||||
"npm-asset-main": "./index.js",
|
||||
"npm-asset-directories": [],
|
||||
"npm-asset-repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/noraesae/perfect-scrollbar.git"
|
||||
},
|
||||
"npm-asset-scripts": {
|
||||
"test": "gulp",
|
||||
"before-deploy": "gulp && gulp compress",
|
||||
"release": "rm -rf dist && gulp && npm publish"
|
||||
},
|
||||
"npm-asset-engines": {
|
||||
"node": ">= 0.12.0"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Hyunje Jun",
|
||||
"email": "me@noraesae.net"
|
||||
},
|
||||
{
|
||||
"name": "Hyunje Jun",
|
||||
"email": "me@noraesae.net"
|
||||
}
|
||||
],
|
||||
"description": "Minimalistic but perfect custom scrollbar plugin",
|
||||
"homepage": "https://github.com/noraesae/perfect-scrollbar#readme",
|
||||
"keywords": [
|
||||
"frontend",
|
||||
"jquery-plugin",
|
||||
"scroll",
|
||||
"scrollbar"
|
||||
],
|
||||
"time": "2017-01-10T01:03:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/php-date-formatter",
|
||||
"version": "v1.3.5",
|
||||
"version": "v1.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kartik-v/php-date-formatter.git",
|
||||
"reference": "d842e1c4e6a8d6108017b726321c305bb5ae4fb5"
|
||||
"reference": "514a53660b0d69439236fd3cbc3f41512adb00a0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/kartik-v/php-date-formatter/zipball/d842e1c4e6a8d6108017b726321c305bb5ae4fb5",
|
||||
"reference": "d842e1c4e6a8d6108017b726321c305bb5ae4fb5",
|
||||
"url": "https://api.github.com/repos/kartik-v/php-date-formatter/zipball/514a53660b0d69439236fd3cbc3f41512adb00a0",
|
||||
"reference": "514a53660b0d69439236fd3cbc3f41512adb00a0",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "npm-asset-library",
|
||||
|
|
@ -1759,7 +1932,101 @@
|
|||
],
|
||||
"description": "A Javascript datetime formatting and manipulation library using PHP date-time formats.",
|
||||
"homepage": "https://github.com/kartik-v/php-date-formatter",
|
||||
"time": "2018-07-13T06:56:46+00:00"
|
||||
"time": "2020-04-14T10:16:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/textarea-caret",
|
||||
"version": "3.1.0",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.1.0.tgz",
|
||||
"shasum": "5d5a35bb035fd06b2ff0e25d5359e97f2655087f"
|
||||
},
|
||||
"type": "npm-asset-library",
|
||||
"extra": {
|
||||
"npm-asset-bugs": {
|
||||
"url": "https://github.com/component/textarea-caret-position/issues"
|
||||
},
|
||||
"npm-asset-files": [
|
||||
"index.js"
|
||||
],
|
||||
"npm-asset-main": "index.js",
|
||||
"npm-asset-directories": [],
|
||||
"npm-asset-repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/component/textarea-caret-position.git"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "(x, y) coordinates of the caret in a textarea or input type='text'",
|
||||
"homepage": "https://github.com/component/textarea-caret-position#readme",
|
||||
"keywords": [
|
||||
"caret",
|
||||
"position",
|
||||
"textarea"
|
||||
],
|
||||
"time": "2018-02-20T06:11:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/textcomplete",
|
||||
"version": "0.18.2",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://registry.npmjs.org/textcomplete/-/textcomplete-0.18.2.tgz",
|
||||
"shasum": "de0d806567102f7e32daffcbcc3db05af1515eb5"
|
||||
},
|
||||
"require": {
|
||||
"npm-asset/eventemitter3": ">=2.0.3,<3.0.0",
|
||||
"npm-asset/textarea-caret": ">=3.0.1,<4.0.0",
|
||||
"npm-asset/undate": ">=0.2.3,<0.3.0"
|
||||
},
|
||||
"type": "npm-asset-library",
|
||||
"extra": {
|
||||
"npm-asset-bugs": {
|
||||
"url": "https://github.com/yuku-t/textcomplete/issues"
|
||||
},
|
||||
"npm-asset-main": "lib/index.js",
|
||||
"npm-asset-directories": [],
|
||||
"npm-asset-repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/yuku-t/textcomplete.git"
|
||||
},
|
||||
"npm-asset-scripts": {
|
||||
"build": "yarn run clean && run-p build:*",
|
||||
"build:dist": "webpack && webpack --env=min && run-p print-dist-gz-size",
|
||||
"build:docs": "run-p build:docs:*",
|
||||
"build:docs:html": "webpack --config webpack.doc.config.js && pug -o docs src/doc/index.pug",
|
||||
"build:docs:md": "documentation build src/*.js -f md -o doc/api.md",
|
||||
"build:lib": "babel src -d lib -s && for js in src/*.js; do cp $js lib/${js##*/}.flow; done",
|
||||
"clean": "rm -fr dist docs lib",
|
||||
"format": "prettier --no-semi --trailing-comma all --write 'src/*.js' 'test/**/*.js'",
|
||||
"gh-release": "npm pack textcomplete && gh-release -a textcomplete-$(cat package.json|jq -r .version).tgz",
|
||||
"opener": "wait-on http://localhost:8082 && opener http://localhost:8082",
|
||||
"print-dist-gz-size": "printf 'dist/textcomplete.min.js.gz: %d bytes\\n' \"$(gzip -9kc dist/textcomplete.min.js | wc -c)\"",
|
||||
"start": "run-p watch opener",
|
||||
"test": "run-p test:*",
|
||||
"test:bundlesize": "yarn run build:dist && bundlesize",
|
||||
"test:e2e": "NODE_ENV=test karma start --single-run",
|
||||
"test:lint": "eslint src/*.js test/**/*.js",
|
||||
"test:typecheck": "flow check",
|
||||
"watch": "run-p watch:*",
|
||||
"watch:webpack": "webpack-dev-server --config webpack.doc.config.js",
|
||||
"watch:pug": "pug -o docs --watch src/doc/index.pug"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Yuku Takahashi"
|
||||
}
|
||||
],
|
||||
"description": "Autocomplete for textarea elements",
|
||||
"homepage": "https://github.com/yuku-t/textcomplete#readme",
|
||||
"time": "2020-06-10T06:11:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/typeahead.js",
|
||||
|
|
@ -1813,18 +2080,60 @@
|
|||
],
|
||||
"time": "2015-04-27T04:03:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "npm-asset/undate",
|
||||
"version": "0.2.4",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://registry.npmjs.org/undate/-/undate-0.2.4.tgz",
|
||||
"shasum": "ccb2a8cf38edc035d1006fcb2909c4c6024a8400"
|
||||
},
|
||||
"type": "npm-asset-library",
|
||||
"extra": {
|
||||
"npm-asset-bugs": {
|
||||
"url": "https://github.com/yuku-t/undate/issues"
|
||||
},
|
||||
"npm-asset-main": "lib/index.js",
|
||||
"npm-asset-directories": [],
|
||||
"npm-asset-repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/yuku-t/undate.git"
|
||||
},
|
||||
"npm-asset-scripts": {
|
||||
"build": "babel src -d lib && for js in src/*.js; do cp $js lib/${js##*/}.flow; done",
|
||||
"test": "run-p test:*",
|
||||
"test:eslint": "eslint src/*.js test/*.js",
|
||||
"test:flow": "flow check",
|
||||
"test:karma": "karma start --single-run"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Yuku Takahashi"
|
||||
}
|
||||
],
|
||||
"description": "Undoable update for HTMLTextAreaElement",
|
||||
"homepage": "https://github.com/yuku-t/undate#readme",
|
||||
"keywords": [
|
||||
"textarea"
|
||||
],
|
||||
"time": "2018-01-24T10:49:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/certainty",
|
||||
"version": "v2.5.0",
|
||||
"version": "v2.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/certainty.git",
|
||||
"reference": "cc39b91595e577fdff6128d7ce787892bd117274"
|
||||
"reference": "b0068bc1e5605bd2ebe1ba906f2426d5df123944"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/certainty/zipball/cc39b91595e577fdff6128d7ce787892bd117274",
|
||||
"reference": "cc39b91595e577fdff6128d7ce787892bd117274",
|
||||
"url": "https://api.github.com/repos/paragonie/certainty/zipball/b0068bc1e5605bd2ebe1ba906f2426d5df123944",
|
||||
"reference": "b0068bc1e5605bd2ebe1ba906f2426d5df123944",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1833,7 +2142,7 @@
|
|||
"guzzlehttp/guzzle": "^6",
|
||||
"paragonie/constant_time_encoding": "^1|^2",
|
||||
"paragonie/sodium_compat": "^1.11",
|
||||
"php": "^5.5|^7"
|
||||
"php": "^5.5|^7|^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "^1",
|
||||
|
|
@ -1873,28 +2182,28 @@
|
|||
"ssl",
|
||||
"tls"
|
||||
],
|
||||
"time": "2019-09-27T22:26:33+00:00"
|
||||
"time": "2020-01-02T00:55:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
"version": "v2.2.3",
|
||||
"version": "v2.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/constant_time_encoding.git",
|
||||
"reference": "55af0dc01992b4d0da7f6372e2eac097bbbaffdb"
|
||||
"reference": "47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/55af0dc01992b4d0da7f6372e2eac097bbbaffdb",
|
||||
"reference": "55af0dc01992b4d0da7f6372e2eac097bbbaffdb",
|
||||
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2",
|
||||
"reference": "47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7"
|
||||
"php": "^7|^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6|^7",
|
||||
"vimeo/psalm": "^1|^2"
|
||||
"vimeo/psalm": "^1|^2|^3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
|
@ -1935,7 +2244,7 @@
|
|||
"hex2bin",
|
||||
"rfc4648"
|
||||
],
|
||||
"time": "2019-01-03T20:26:31+00:00"
|
||||
"time": "2019-11-06T19:20:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/hidden-string",
|
||||
|
|
@ -2033,16 +2342,16 @@
|
|||
},
|
||||
{
|
||||
"name": "paragonie/sodium_compat",
|
||||
"version": "v1.11.1",
|
||||
"version": "v1.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/sodium_compat.git",
|
||||
"reference": "a9f968bc99485f85f9303a8524c3485a7e87bc15"
|
||||
"reference": "bbade402cbe84c69b718120911506a3aa2bae653"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/a9f968bc99485f85f9303a8524c3485a7e87bc15",
|
||||
"reference": "a9f968bc99485f85f9303a8524c3485a7e87bc15",
|
||||
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bbade402cbe84c69b718120911506a3aa2bae653",
|
||||
"reference": "bbade402cbe84c69b718120911506a3aa2bae653",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2050,7 +2359,7 @@
|
|||
"php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^3|^4|^5"
|
||||
"phpunit/phpunit": "^3|^4|^5|^6|^7"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.",
|
||||
|
|
@ -2111,7 +2420,53 @@
|
|||
"secret-key cryptography",
|
||||
"side-channel resistant"
|
||||
],
|
||||
"time": "2019-09-12T12:05:58+00:00"
|
||||
"time": "2020-03-20T21:48:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "patrickschur/language-detection",
|
||||
"version": "v3.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/patrickschur/language-detection.git",
|
||||
"reference": "95b55109177d5c4bd6b1bec6e8835cd0df36ef5f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/patrickschur/language-detection/zipball/95b55109177d5c4bd6b1bec6e8835cd0df36ef5f",
|
||||
"reference": "95b55109177d5c4bd6b1bec6e8835cd0df36ef5f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": "^7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"LanguageDetection\\": "src/LanguageDetection"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Patrick Schur",
|
||||
"email": "patrick_schur@outlook.de"
|
||||
}
|
||||
],
|
||||
"description": "A language detection library for PHP. Detects the language from a given text string.",
|
||||
"homepage": "https://github.com/patrickschur/language-detection",
|
||||
"keywords": [
|
||||
"detect",
|
||||
"detection",
|
||||
"language"
|
||||
],
|
||||
"time": "2018-09-19T21:45:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pear/console_table",
|
||||
|
|
@ -2169,48 +2524,109 @@
|
|||
"time": "2018-01-25T20:47:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pear/text_languagedetect",
|
||||
"version": "v1.0.0",
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "2.0.29",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pear/Text_LanguageDetect.git",
|
||||
"reference": "bb9ff6f4970f686fac59081e916b456021fe7ba6"
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "497856a8d997f640b4a516062f84228a772a48a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pear/Text_LanguageDetect/zipball/bb9ff6f4970f686fac59081e916b456021fe7ba6",
|
||||
"reference": "bb9ff6f4970f686fac59081e916b456021fe7ba6",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/497856a8d997f640b4a516062f84228a772a48a8",
|
||||
"reference": "497856a8d997f640b4a516062f84228a772a48a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
"phing/phing": "~2.7",
|
||||
"phpunit/phpunit": "^4.8.35|^5.7|^6.0",
|
||||
"squizlabs/php_codesniffer": "~2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "May require the mbstring PHP extension"
|
||||
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
|
||||
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
|
||||
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
|
||||
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Text": "./"
|
||||
"files": [
|
||||
"phpseclib/bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"phpseclib\\": "phpseclib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"include-path": [
|
||||
"./"
|
||||
],
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicholas Pisarro",
|
||||
"email": "taak@php.net",
|
||||
"role": "Lead"
|
||||
"name": "Jim Wigginton",
|
||||
"email": "terrafrost@php.net",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Patrick Monnerat",
|
||||
"email": "pm@datasphere.ch",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andreas Fischer",
|
||||
"email": "bantu@phpbb.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Hans-Jürgen Petrich",
|
||||
"email": "petrich@tronic-media.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "graham@alt-three.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Identify human languages from text samples",
|
||||
"homepage": "http://pear.php.net/package/Text_LanguageDetect",
|
||||
"time": "2017-03-02T16:14:08+00:00"
|
||||
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
|
||||
"homepage": "http://phpseclib.sourceforge.net",
|
||||
"keywords": [
|
||||
"BigInteger",
|
||||
"aes",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"blowfish",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"encryption",
|
||||
"rsa",
|
||||
"security",
|
||||
"sftp",
|
||||
"signature",
|
||||
"signing",
|
||||
"ssh",
|
||||
"twofish",
|
||||
"x.509",
|
||||
"x509"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/terrafrost",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpseclib",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-09-08T04:24:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pragmarx/google2fa",
|
||||
|
|
@ -2542,16 +2958,16 @@
|
|||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
|
||||
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
|
||||
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
|
||||
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2560,7 +2976,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -2585,7 +3001,7 @@
|
|||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"time": "2018-11-20T15:27:04+00:00"
|
||||
"time": "2020-03-23T09:12:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
|
|
@ -2677,21 +3093,25 @@
|
|||
},
|
||||
{
|
||||
"name": "smarty/smarty",
|
||||
"version": "v3.1.33",
|
||||
"version": "v3.1.36",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/smarty-php/smarty.git",
|
||||
"reference": "dd55b23121e55a3b4f1af90a707a6c4e5969530f"
|
||||
"reference": "fd148f7ade295014fff77f89ee3d5b20d9d55451"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/dd55b23121e55a3b4f1af90a707a6c4e5969530f",
|
||||
"reference": "dd55b23121e55a3b4f1af90a707a6c4e5969530f",
|
||||
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/fd148f7ade295014fff77f89ee3d5b20d9d55451",
|
||||
"reference": "fd148f7ade295014fff77f89ee3d5b20d9d55451",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "6.4.1",
|
||||
"smarty/smarty-lexer": "^3.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
|
|
@ -2699,8 +3119,8 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"libs/bootstrap.php"
|
||||
"classmap": [
|
||||
"libs/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
|
|
@ -2726,20 +3146,141 @@
|
|||
"keywords": [
|
||||
"templating"
|
||||
],
|
||||
"time": "2018-09-12T20:54:16+00:00"
|
||||
"time": "2020-04-14T14:44:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php56",
|
||||
"version": "v1.12.0",
|
||||
"name": "symfony/polyfill-intl-idn",
|
||||
"version": "v1.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php56.git",
|
||||
"reference": "0e3b212e96a51338639d8ce175c046d7729c3403"
|
||||
"url": "https://github.com/symfony/polyfill-intl-idn.git",
|
||||
"reference": "3bff59ea7047e925be6b7f2059d60af31bb46d6a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/0e3b212e96a51338639d8ce175c046d7729c3403",
|
||||
"reference": "0e3b212e96a51338639d8ce175c046d7729c3403",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/3bff59ea7047e925be6b7f2059d60af31bb46d6a",
|
||||
"reference": "3bff59ea7047e925be6b7f2059d60af31bb46d6a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"symfony/polyfill-mbstring": "^1.3",
|
||||
"symfony/polyfill-php72": "^1.10"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.17-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Idn\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Laurent Bassin",
|
||||
"email": "laurent@bassin.info"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"idn",
|
||||
"intl",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2020-05-12T16:47:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "fa79b11539418b02fc5e1897267673ba2c19419c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c",
|
||||
"reference": "fa79b11539418b02fc5e1897267673ba2c19419c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.17-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2020-05-12T16:47:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php56",
|
||||
"version": "v1.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php56.git",
|
||||
"reference": "e3c8c138280cdfe4b81488441555583aa1984e23"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/e3c8c138280cdfe4b81488441555583aa1984e23",
|
||||
"reference": "e3c8c138280cdfe4b81488441555583aa1984e23",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2749,7 +3290,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.12-dev"
|
||||
"dev-master": "1.17-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -2782,20 +3323,20 @@
|
|||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
"time": "2020-05-12T16:47:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-util",
|
||||
"version": "v1.12.0",
|
||||
"name": "symfony/polyfill-php72",
|
||||
"version": "v1.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-util.git",
|
||||
"reference": "4317de1386717b4c22caed7725350a8887ab205c"
|
||||
"url": "https://github.com/symfony/polyfill-php72.git",
|
||||
"reference": "f048e612a3905f34931127360bdd2def19a5e582"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-util/zipball/4317de1386717b4c22caed7725350a8887ab205c",
|
||||
"reference": "4317de1386717b4c22caed7725350a8887ab205c",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/f048e612a3905f34931127360bdd2def19a5e582",
|
||||
"reference": "f048e612a3905f34931127360bdd2def19a5e582",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2804,7 +3345,62 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.12-dev"
|
||||
"dev-master": "1.17-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php72\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2020-05-12T16:47:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-util",
|
||||
"version": "v1.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-util.git",
|
||||
"reference": "4afb4110fc037752cf0ce9869f9ab8162c4e20d7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-util/zipball/4afb4110fc037752cf0ce9869f9ab8162c4e20d7",
|
||||
"reference": "4afb4110fc037752cf0ce9869f9ab8162c4e20d7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.17-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -2834,7 +3430,58 @@
|
|||
"polyfill",
|
||||
"shim"
|
||||
],
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
"time": "2020-05-12T16:14:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "xemlock/htmlpurifier-html5",
|
||||
"version": "v0.1.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/xemlock/htmlpurifier-html5.git",
|
||||
"reference": "f0d563f9fd4a82a3d759043483f9a94c0d8c2255"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/xemlock/htmlpurifier-html5/zipball/f0d563f9fd4a82a3d759043483f9a94c0d8c2255",
|
||||
"reference": "f0d563f9fd4a82a3d759043483f9a94c0d8c2255",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ezyang/htmlpurifier": "^4.8",
|
||||
"php": ">=5.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^1.1|^2.1",
|
||||
"phpunit/phpunit": ">=4.7 <8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"library/HTMLPurifier/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "xemlock",
|
||||
"email": "xemlock@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "HTML5 element definitions for HTML Purifier",
|
||||
"keywords": [
|
||||
"HTML5",
|
||||
"Purifier",
|
||||
"html",
|
||||
"htmlpurifier",
|
||||
"security",
|
||||
"tidy",
|
||||
"validator",
|
||||
"xss"
|
||||
],
|
||||
"time": "2019-08-07T17:19:21+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
|
@ -2940,54 +3587,6 @@
|
|||
],
|
||||
"time": "2016-01-20T08:20:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jakub-onderka/php-parallel-lint",
|
||||
"version": "v1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/JakubOnderka/PHP-Parallel-Lint.git",
|
||||
"reference": "04fbd3f5fb1c83f08724aa58a23db90bd9086ee8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/JakubOnderka/PHP-Parallel-Lint/zipball/04fbd3f5fb1c83f08724aa58a23db90bd9086ee8",
|
||||
"reference": "04fbd3f5fb1c83f08724aa58a23db90bd9086ee8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"jakub-onderka/php-console-highlighter": "~0.3",
|
||||
"nette/tester": "~1.3",
|
||||
"squizlabs/php_codesniffer": "~2.7"
|
||||
},
|
||||
"suggest": {
|
||||
"jakub-onderka/php-console-highlighter": "Highlight syntax in code snippet"
|
||||
},
|
||||
"bin": [
|
||||
"parallel-lint"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"./"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jakub Onderka",
|
||||
"email": "ahoj@jakubonderka.cz"
|
||||
}
|
||||
],
|
||||
"description": "This tool check syntax of PHP files about 20x faster than serial check.",
|
||||
"homepage": "https://github.com/JakubOnderka/PHP-Parallel-Lint",
|
||||
"time": "2018-02-24T15:31:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "johnkary/phpunit-speedtrap",
|
||||
"version": "v1.1.0",
|
||||
|
|
@ -3038,16 +3637,16 @@
|
|||
},
|
||||
{
|
||||
"name": "mikey179/vfsstream",
|
||||
"version": "v1.6.7",
|
||||
"version": "v1.6.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bovigo/vfsStream.git",
|
||||
"reference": "2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb"
|
||||
"reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/bovigo/vfsStream/zipball/2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb",
|
||||
"reference": "2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb",
|
||||
"url": "https://api.github.com/repos/bovigo/vfsStream/zipball/231c73783ebb7dd9ec77916c10037eff5a2b6efe",
|
||||
"reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3080,20 +3679,20 @@
|
|||
],
|
||||
"description": "Virtual file system to mock the real file system in unit tests.",
|
||||
"homepage": "http://vfs.bovigo.org/",
|
||||
"time": "2019-08-01T01:38:37+00:00"
|
||||
"time": "2019-10-30T15:31:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
"version": "1.2.3",
|
||||
"version": "1.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mockery/mockery.git",
|
||||
"reference": "4eff936d83eb809bde2c57a3cea0ee9643769031"
|
||||
"reference": "f69bbde7d7a75d6b2862d9ca8fab1cd28014b4be"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mockery/mockery/zipball/4eff936d83eb809bde2c57a3cea0ee9643769031",
|
||||
"reference": "4eff936d83eb809bde2c57a3cea0ee9643769031",
|
||||
"url": "https://api.github.com/repos/mockery/mockery/zipball/f69bbde7d7a75d6b2862d9ca8fab1cd28014b4be",
|
||||
"reference": "f69bbde7d7a75d6b2862d9ca8fab1cd28014b4be",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3107,7 +3706,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
"dev-master": "1.3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -3145,7 +3744,7 @@
|
|||
"test double",
|
||||
"testing"
|
||||
],
|
||||
"time": "2019-08-07T15:01:07+00:00"
|
||||
"time": "2019-12-26T09:49:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
|
|
@ -3192,6 +3791,59 @@
|
|||
],
|
||||
"time": "2017-10-19T19:58:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-parallel-lint/php-parallel-lint",
|
||||
"version": "v1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git",
|
||||
"reference": "474f18bc6cc6aca61ca40bfab55139de614e51ca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/474f18bc6cc6aca61ca40bfab55139de614e51ca",
|
||||
"reference": "474f18bc6cc6aca61ca40bfab55139de614e51ca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"replace": {
|
||||
"grogy/php-parallel-lint": "*",
|
||||
"jakub-onderka/php-parallel-lint": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^1.3 || ^2.0",
|
||||
"php-parallel-lint/php-console-highlighter": "~0.3",
|
||||
"squizlabs/php_codesniffer": "~3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet"
|
||||
},
|
||||
"bin": [
|
||||
"parallel-lint"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"./"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jakub Onderka",
|
||||
"email": "ahoj@jakubonderka.cz"
|
||||
}
|
||||
],
|
||||
"description": "This tool check syntax of PHP files about 20x faster than serial check.",
|
||||
"homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
|
||||
"time": "2020-04-04T12:18:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
"version": "1.0.1",
|
||||
|
|
@ -3340,33 +3992,33 @@
|
|||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "1.8.1",
|
||||
"version": "v1.10.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76"
|
||||
"reference": "451c3cd1418cf640de218914901e51b064abb093"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
|
||||
"reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
|
||||
"reference": "451c3cd1418cf640de218914901e51b064abb093",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.2",
|
||||
"php": "^5.3|^7.0",
|
||||
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
|
||||
"sebastian/comparator": "^1.1|^2.0|^3.0",
|
||||
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
|
||||
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
|
||||
"sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
|
||||
"sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "^2.5|^3.2",
|
||||
"phpspec/phpspec": "^2.5 || ^3.2",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.8.x-dev"
|
||||
"dev-master": "1.10.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -3399,7 +4051,7 @@
|
|||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"time": "2019-06-13T12:50:23+00:00"
|
||||
"time": "2020-03-05T15:02:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
|
@ -3648,6 +4300,7 @@
|
|||
"keywords": [
|
||||
"tokenizer"
|
||||
],
|
||||
"abandoned": true,
|
||||
"time": "2017-12-04T08:55:13+00:00"
|
||||
},
|
||||
{
|
||||
|
|
@ -4307,16 +4960,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.12.0",
|
||||
"version": "v1.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "550ebaac289296ce228a706d0867afc34687e3f4"
|
||||
"reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4",
|
||||
"reference": "550ebaac289296ce228a706d0867afc34687e3f4",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
|
||||
"reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4328,7 +4981,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.12-dev"
|
||||
"dev-master": "1.17-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -4361,31 +5014,41 @@
|
|||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-05-12T16:14:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v3.4.32",
|
||||
"version": "v3.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "768f817446da74a776a31eea335540f9dcb53942"
|
||||
"reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/768f817446da74a776a31eea335540f9dcb53942",
|
||||
"reference": "768f817446da74a776a31eea335540f9dcb53942",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/ddc23324e6cfe066f3dd34a37ff494fa80b617ed",
|
||||
"reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5.9|>=7.0.8",
|
||||
"symfony/polyfill-ctype": "~1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/console": "<3.4"
|
||||
"php": ">=5.5.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/console": "~3.4|~4.0"
|
||||
"symfony/console": "~2.8|~3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/console": "For validating YAML files using the lint command"
|
||||
|
|
@ -4393,7 +5056,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.4-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -4420,35 +5083,34 @@
|
|||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-09-10T10:38:46+00:00"
|
||||
"time": "2017-07-23T12:43:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
"version": "1.5.0",
|
||||
"version": "1.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/webmozart/assert.git",
|
||||
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4"
|
||||
"reference": "9dc4f203e36f2b486149058bade43c851dd97451"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4",
|
||||
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4",
|
||||
"url": "https://api.github.com/repos/webmozart/assert/zipball/9dc4f203e36f2b486149058bade43c851dd97451",
|
||||
"reference": "9dc4f203e36f2b486149058bade43c851dd97451",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.3 || ^7.0",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan": "<0.12.20",
|
||||
"vimeo/psalm": "<3.9.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Webmozart\\Assert\\": "src/"
|
||||
|
|
@ -4470,7 +5132,7 @@
|
|||
"check",
|
||||
"validate"
|
||||
],
|
||||
"time": "2019-08-24T08:43:50+00:00"
|
||||
"time": "2020-06-16T10:16:42+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
@ -4495,5 +5157,9 @@
|
|||
"ext-simplexml": "*",
|
||||
"ext-xml": "*"
|
||||
},
|
||||
"platform-dev": []
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "7.0"
|
||||
},
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
|||
1159
database.sql
|
|
@ -1,164 +1,95 @@
|
|||
-- ------------------------------------------
|
||||
-- Friendica 2020.06-dev (Red Hot Poker)
|
||||
-- DB_UPDATE_VERSION 1346
|
||||
-- Friendica 2021.03-dev (Red Hot Poker)
|
||||
-- DB_UPDATE_VERSION 1385
|
||||
-- ------------------------------------------
|
||||
|
||||
|
||||
--
|
||||
-- TABLE 2fa_app_specific_password
|
||||
-- TABLE gserver
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `2fa_app_specific_password` (
|
||||
`id` mediumint unsigned NOT NULL auto_increment COMMENT 'Password ID for revocation',
|
||||
`uid` mediumint unsigned NOT NULL COMMENT 'User ID',
|
||||
`description` varchar(255) COMMENT 'Description of the usage of the password',
|
||||
`hashed_password` varchar(255) NOT NULL COMMENT 'Hashed password',
|
||||
`generated` datetime NOT NULL COMMENT 'Datetime the password was generated',
|
||||
`last_used` datetime COMMENT 'Datetime the password was last used',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid_description` (`uid`,`description`(190))
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Two-factor app-specific _password';
|
||||
|
||||
--
|
||||
-- TABLE 2fa_recovery_codes
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `2fa_recovery_codes` (
|
||||
`uid` mediumint unsigned NOT NULL COMMENT 'User ID',
|
||||
`code` varchar(50) NOT NULL COMMENT 'Recovery code string',
|
||||
`generated` datetime NOT NULL COMMENT 'Datetime the code was generated',
|
||||
`used` datetime COMMENT 'Datetime the code was used',
|
||||
PRIMARY KEY(`uid`,`code`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Two-factor authentication recovery codes';
|
||||
|
||||
--
|
||||
-- TABLE addon
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `addon` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
`name` varchar(50) NOT NULL DEFAULT '' COMMENT 'addon base (file)name',
|
||||
`version` varchar(50) NOT NULL DEFAULT '' COMMENT 'currently unused',
|
||||
`installed` boolean NOT NULL DEFAULT '0' COMMENT 'currently always 1',
|
||||
`hidden` boolean NOT NULL DEFAULT '0' COMMENT 'currently unused',
|
||||
`timestamp` int unsigned NOT NULL DEFAULT 0 COMMENT 'file timestamp to check for reloads',
|
||||
`plugin_admin` boolean NOT NULL DEFAULT '0' COMMENT '1 = has admin config, 0 = has no admin config',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `name` (`name`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='registered addons';
|
||||
|
||||
--
|
||||
-- TABLE apcontact
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `apcontact` (
|
||||
`url` varbinary(255) NOT NULL COMMENT 'URL of the contact',
|
||||
`uuid` varchar(255) COMMENT '',
|
||||
`type` varchar(20) NOT NULL COMMENT '',
|
||||
`following` varchar(255) COMMENT '',
|
||||
`followers` varchar(255) COMMENT '',
|
||||
`inbox` varchar(255) NOT NULL COMMENT '',
|
||||
`outbox` varchar(255) COMMENT '',
|
||||
`sharedinbox` varchar(255) COMMENT '',
|
||||
`manually-approve` boolean COMMENT '',
|
||||
`nick` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`name` varchar(255) COMMENT '',
|
||||
`about` text COMMENT '',
|
||||
`photo` varchar(255) COMMENT '',
|
||||
`addr` varchar(255) COMMENT '',
|
||||
`alias` varchar(255) COMMENT '',
|
||||
`pubkey` text COMMENT '',
|
||||
`baseurl` varchar(255) COMMENT 'baseurl of the ap contact',
|
||||
`generator` varchar(255) COMMENT 'Name of the contact\'s system',
|
||||
`following_count` int unsigned DEFAULT 0 COMMENT 'Number of following contacts',
|
||||
`followers_count` int unsigned DEFAULT 0 COMMENT 'Number of followers',
|
||||
`statuses_count` int unsigned DEFAULT 0 COMMENT 'Number of posts',
|
||||
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
PRIMARY KEY(`url`),
|
||||
INDEX `addr` (`addr`(32)),
|
||||
INDEX `alias` (`alias`(190)),
|
||||
INDEX `url` (`followers`(190))
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='ActivityPub compatible contacts - used in the ActivityPub implementation';
|
||||
|
||||
--
|
||||
-- TABLE attach
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `attach` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'generated index',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id',
|
||||
`hash` varchar(64) NOT NULL DEFAULT '' COMMENT 'hash',
|
||||
`filename` varchar(255) NOT NULL DEFAULT '' COMMENT 'filename of original',
|
||||
`filetype` varchar(64) NOT NULL DEFAULT '' COMMENT 'mimetype',
|
||||
`filesize` int unsigned NOT NULL DEFAULT 0 COMMENT 'size in bytes',
|
||||
`data` longblob NOT NULL COMMENT 'file data',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation time',
|
||||
`edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'last edit time',
|
||||
`allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups',
|
||||
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
|
||||
`backend-class` tinytext COMMENT 'Storage backend class',
|
||||
`backend-ref` text COMMENT 'Storage backend data reference',
|
||||
PRIMARY KEY(`id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='file attachments';
|
||||
|
||||
--
|
||||
-- TABLE auth_codes
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `auth_codes` (
|
||||
`id` varchar(40) NOT NULL COMMENT '',
|
||||
`client_id` varchar(20) NOT NULL DEFAULT '' COMMENT '',
|
||||
`redirect_uri` varchar(200) NOT NULL DEFAULT '' COMMENT '',
|
||||
`expires` int NOT NULL DEFAULT 0 COMMENT '',
|
||||
`scope` varchar(250) NOT NULL DEFAULT '' COMMENT '',
|
||||
PRIMARY KEY(`id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage';
|
||||
|
||||
--
|
||||
-- TABLE cache
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `cache` (
|
||||
`k` varbinary(255) NOT NULL COMMENT 'cache key',
|
||||
`v` mediumtext COMMENT 'cached serialized value',
|
||||
`expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of cache expiration',
|
||||
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of cache insertion',
|
||||
PRIMARY KEY(`k`),
|
||||
INDEX `k_expires` (`k`,`expires`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Stores temporary data';
|
||||
|
||||
--
|
||||
-- TABLE challenge
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `challenge` (
|
||||
CREATE TABLE IF NOT EXISTS `gserver` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`challenge` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`expire` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`type` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`last_update` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
PRIMARY KEY(`id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
|
||||
|
||||
--
|
||||
-- TABLE clients
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `clients` (
|
||||
`client_id` varchar(20) NOT NULL COMMENT '',
|
||||
`pw` varchar(20) NOT NULL DEFAULT '' COMMENT '',
|
||||
`redirect_uri` varchar(200) NOT NULL DEFAULT '' COMMENT '',
|
||||
`name` text COMMENT '',
|
||||
`icon` text COMMENT '',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
PRIMARY KEY(`client_id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage';
|
||||
|
||||
--
|
||||
-- TABLE config
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `config` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
`cat` varbinary(50) NOT NULL DEFAULT '' COMMENT '',
|
||||
`k` varbinary(50) NOT NULL DEFAULT '' COMMENT '',
|
||||
`v` mediumtext COMMENT '',
|
||||
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`version` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`site_name` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`info` text COMMENT '',
|
||||
`register_policy` tinyint NOT NULL DEFAULT 0 COMMENT '',
|
||||
`registered-users` int unsigned NOT NULL DEFAULT 0 COMMENT 'Number of registered users',
|
||||
`directory-type` tinyint DEFAULT 0 COMMENT 'Type of directory service (Poco, Mastodon)',
|
||||
`poco` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`noscrape` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`network` char(4) NOT NULL DEFAULT '' COMMENT '',
|
||||
`protocol` tinyint unsigned COMMENT 'The protocol of the server',
|
||||
`platform` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`relay-subscribe` boolean NOT NULL DEFAULT '0' COMMENT 'Has the server subscribed to the relay system',
|
||||
`relay-scope` varchar(10) NOT NULL DEFAULT '' COMMENT 'The scope of messages that the server wants to get',
|
||||
`detection-method` tinyint unsigned COMMENT 'Method that had been used to detect that server',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`last_poco_query` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`last_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT 'Last successful connection request',
|
||||
`last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT 'Last failed connection request',
|
||||
`failed` boolean COMMENT 'Connection failed',
|
||||
`next_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT 'Next connection request',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `cat_k` (`cat`,`k`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='main configuration storage';
|
||||
UNIQUE INDEX `nurl` (`nurl`(190)),
|
||||
INDEX `next_contact` (`next_contact`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Global servers';
|
||||
|
||||
--
|
||||
-- TABLE user
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `user` (
|
||||
`uid` mediumint unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`parent-uid` mediumint unsigned COMMENT 'The parent user that has full control about this user',
|
||||
`guid` varchar(64) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this user',
|
||||
`username` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this user is known by',
|
||||
`password` varchar(255) NOT NULL DEFAULT '' COMMENT 'encrypted password',
|
||||
`legacy_password` boolean NOT NULL DEFAULT '0' COMMENT 'Is the password hash double-hashed?',
|
||||
`nickname` varchar(255) NOT NULL DEFAULT '' COMMENT 'nick- and user name',
|
||||
`email` varchar(255) NOT NULL DEFAULT '' COMMENT 'the users email address',
|
||||
`openid` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`timezone` varchar(128) NOT NULL DEFAULT '' COMMENT 'PHP-legal timezone',
|
||||
`language` varchar(32) NOT NULL DEFAULT 'en' COMMENT 'default language',
|
||||
`register_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of registration',
|
||||
`login_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last login',
|
||||
`default-location` varchar(255) NOT NULL DEFAULT '' COMMENT 'Default for item.location',
|
||||
`allow_location` boolean NOT NULL DEFAULT '0' COMMENT '1 allows to display the location',
|
||||
`theme` varchar(255) NOT NULL DEFAULT '' COMMENT 'user theme preference',
|
||||
`pubkey` text COMMENT 'RSA public key 4096 bit',
|
||||
`prvkey` text COMMENT 'RSA private key 4096 bit',
|
||||
`spubkey` text COMMENT '',
|
||||
`sprvkey` text COMMENT '',
|
||||
`verified` boolean NOT NULL DEFAULT '0' COMMENT 'user is verified through email',
|
||||
`blocked` boolean NOT NULL DEFAULT '0' COMMENT '1 for user is blocked',
|
||||
`blockwall` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to post to the profile page of the user',
|
||||
`hidewall` boolean NOT NULL DEFAULT '0' COMMENT 'Hide profile details from unkown viewers',
|
||||
`blocktags` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to tag the post of this user',
|
||||
`unkmail` boolean NOT NULL DEFAULT '0' COMMENT 'Permit unknown people to send private mails to this user',
|
||||
`cntunkmail` int unsigned NOT NULL DEFAULT 10 COMMENT '',
|
||||
`notify-flags` smallint unsigned NOT NULL DEFAULT 65535 COMMENT 'email notification options',
|
||||
`page-flags` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'page/profile type',
|
||||
`account-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`prvnets` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`pwdreset` varchar(255) COMMENT 'Password reset request token',
|
||||
`pwdreset_time` datetime COMMENT 'Timestamp of the last password reset request',
|
||||
`maxreq` int unsigned NOT NULL DEFAULT 10 COMMENT '',
|
||||
`expire` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`account_removed` boolean NOT NULL DEFAULT '0' COMMENT 'if 1 the account is removed',
|
||||
`account_expired` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`account_expires_on` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp when account expires and will be deleted',
|
||||
`expire_notification_sent` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last warning of account expiration',
|
||||
`def_gid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`allow_cid` mediumtext COMMENT 'default permission for this user',
|
||||
`allow_gid` mediumtext COMMENT 'default permission for this user',
|
||||
`deny_cid` mediumtext COMMENT 'default permission for this user',
|
||||
`deny_gid` mediumtext COMMENT 'default permission for this user',
|
||||
`openidserver` text COMMENT '',
|
||||
PRIMARY KEY(`uid`),
|
||||
INDEX `nickname` (`nickname`(32)),
|
||||
INDEX `parent-uid` (`parent-uid`),
|
||||
FOREIGN KEY (`parent-uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='The local users';
|
||||
|
||||
--
|
||||
-- TABLE contact
|
||||
|
|
@ -200,6 +131,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
|
|||
`notify` varchar(255) COMMENT '',
|
||||
`poll` varchar(255) COMMENT '',
|
||||
`confirm` varchar(255) COMMENT '',
|
||||
`subscribe` varchar(255) COMMENT '',
|
||||
`poco` varchar(255) COMMENT '',
|
||||
`aes_allow` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`ret-aes` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
|
|
@ -209,11 +141,13 @@ CREATE TABLE IF NOT EXISTS `contact` (
|
|||
`last-update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last try to update the contact info',
|
||||
`success_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last successful contact update',
|
||||
`failure_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last failed update',
|
||||
`failed` boolean COMMENT 'Connection failed',
|
||||
`name-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`uri-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`avatar-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`term-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`last-item` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last post',
|
||||
`last-discovery` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last follower discovery',
|
||||
`priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`blocked` boolean NOT NULL DEFAULT '1' COMMENT 'Node-wide block status',
|
||||
`block_reason` text COMMENT 'Node-wide block reason',
|
||||
|
|
@ -222,6 +156,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
|
|||
`forum` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a forum',
|
||||
`prv` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a private group',
|
||||
`contact-type` tinyint NOT NULL DEFAULT 0 COMMENT '',
|
||||
`manually-approve` boolean COMMENT '',
|
||||
`hidden` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`archive` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`pending` boolean NOT NULL DEFAULT '1' COMMENT '',
|
||||
|
|
@ -230,6 +165,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
|
|||
`unsearchable` boolean NOT NULL DEFAULT '0' COMMENT 'Contact prefers to not be searchable',
|
||||
`sensitive` boolean NOT NULL DEFAULT '0' COMMENT 'Contact posts sensitive content',
|
||||
`baseurl` varchar(255) DEFAULT '' COMMENT 'baseurl of the contact',
|
||||
`gsid` int unsigned COMMENT 'Global Server ID',
|
||||
`reason` text COMMENT '',
|
||||
`closeness` tinyint unsigned NOT NULL DEFAULT 99 COMMENT '',
|
||||
`info` mediumtext COMMENT '',
|
||||
|
|
@ -238,22 +174,247 @@ CREATE TABLE IF NOT EXISTS `contact` (
|
|||
`bd` date NOT NULL DEFAULT '0001-01-01' COMMENT '',
|
||||
`notify_new_posts` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`fetch_further_information` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`ffi_keyword_blacklist` text COMMENT '',
|
||||
`ffi_keyword_denylist` text COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid_name` (`uid`,`name`(190)),
|
||||
INDEX `self_uid` (`self`,`uid`),
|
||||
INDEX `alias_uid` (`alias`(32),`uid`),
|
||||
INDEX `alias_uid` (`alias`(128),`uid`),
|
||||
INDEX `pending_uid` (`pending`,`uid`),
|
||||
INDEX `blocked_uid` (`blocked`,`uid`),
|
||||
INDEX `uid_rel_network_poll` (`uid`,`rel`,`network`,`poll`(64),`archive`),
|
||||
INDEX `uid_network_batch` (`uid`,`network`,`batch`(64)),
|
||||
INDEX `addr_uid` (`addr`(32),`uid`),
|
||||
INDEX `nurl_uid` (`nurl`(32),`uid`),
|
||||
INDEX `nick_uid` (`nick`(32),`uid`),
|
||||
INDEX `addr_uid` (`addr`(128),`uid`),
|
||||
INDEX `nurl_uid` (`nurl`(128),`uid`),
|
||||
INDEX `nick_uid` (`nick`(128),`uid`),
|
||||
INDEX `attag_uid` (`attag`(96),`uid`),
|
||||
INDEX `dfrn-id` (`dfrn-id`(64)),
|
||||
INDEX `issued-id` (`issued-id`(64))
|
||||
INDEX `issued-id` (`issued-id`(64)),
|
||||
INDEX `network_uid_lastupdate` (`network`,`uid`,`last-update`),
|
||||
INDEX `uid_network_self_lastupdate` (`uid`,`network`,`self`,`last-update`),
|
||||
INDEX `uid_lastitem` (`uid`,`last-item`),
|
||||
INDEX `gsid` (`gsid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contact table';
|
||||
|
||||
--
|
||||
-- TABLE item-uri
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `item-uri` (
|
||||
`id` int unsigned NOT NULL auto_increment,
|
||||
`uri` varbinary(255) NOT NULL COMMENT 'URI of an item',
|
||||
`guid` varbinary(255) COMMENT 'A unique identifier for an item',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `uri` (`uri`),
|
||||
INDEX `guid` (`guid`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='URI and GUID for items';
|
||||
|
||||
--
|
||||
-- TABLE tag
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `tag` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
`name` varchar(96) NOT NULL DEFAULT '' COMMENT '',
|
||||
`url` varbinary(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `type_name_url` (`name`,`url`),
|
||||
INDEX `url` (`url`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='tags and mentions';
|
||||
|
||||
--
|
||||
-- TABLE clients
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `clients` (
|
||||
`client_id` varchar(20) NOT NULL COMMENT '',
|
||||
`pw` varchar(20) NOT NULL DEFAULT '' COMMENT '',
|
||||
`redirect_uri` varchar(200) NOT NULL DEFAULT '' COMMENT '',
|
||||
`name` text COMMENT '',
|
||||
`icon` text COMMENT '',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
PRIMARY KEY(`client_id`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage';
|
||||
|
||||
--
|
||||
-- TABLE permissionset
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `permissionset` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner id of this permission set',
|
||||
`allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>\'',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups',
|
||||
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid_allow_cid_allow_gid_deny_cid_deny_gid` (`uid`,`allow_cid`(50),`allow_gid`(30),`deny_cid`(50),`deny_gid`(30)),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
|
||||
|
||||
--
|
||||
-- TABLE verb
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `verb` (
|
||||
`id` smallint unsigned NOT NULL auto_increment,
|
||||
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '',
|
||||
PRIMARY KEY(`id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Activity Verbs';
|
||||
|
||||
--
|
||||
-- TABLE 2fa_app_specific_password
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `2fa_app_specific_password` (
|
||||
`id` mediumint unsigned NOT NULL auto_increment COMMENT 'Password ID for revocation',
|
||||
`uid` mediumint unsigned NOT NULL COMMENT 'User ID',
|
||||
`description` varchar(255) COMMENT 'Description of the usage of the password',
|
||||
`hashed_password` varchar(255) NOT NULL COMMENT 'Hashed password',
|
||||
`generated` datetime NOT NULL COMMENT 'Datetime the password was generated',
|
||||
`last_used` datetime COMMENT 'Datetime the password was last used',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid_description` (`uid`,`description`(190)),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Two-factor app-specific _password';
|
||||
|
||||
--
|
||||
-- TABLE 2fa_recovery_codes
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `2fa_recovery_codes` (
|
||||
`uid` mediumint unsigned NOT NULL COMMENT 'User ID',
|
||||
`code` varchar(50) NOT NULL COMMENT 'Recovery code string',
|
||||
`generated` datetime NOT NULL COMMENT 'Datetime the code was generated',
|
||||
`used` datetime COMMENT 'Datetime the code was used',
|
||||
PRIMARY KEY(`uid`,`code`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Two-factor authentication recovery codes';
|
||||
|
||||
--
|
||||
-- TABLE addon
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `addon` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
`name` varchar(50) NOT NULL DEFAULT '' COMMENT 'addon base (file)name',
|
||||
`version` varchar(50) NOT NULL DEFAULT '' COMMENT 'currently unused',
|
||||
`installed` boolean NOT NULL DEFAULT '0' COMMENT 'currently always 1',
|
||||
`hidden` boolean NOT NULL DEFAULT '0' COMMENT 'currently unused',
|
||||
`timestamp` int unsigned NOT NULL DEFAULT 0 COMMENT 'file timestamp to check for reloads',
|
||||
`plugin_admin` boolean NOT NULL DEFAULT '0' COMMENT '1 = has admin config, 0 = has no admin config',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `name` (`name`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='registered addons';
|
||||
|
||||
--
|
||||
-- TABLE apcontact
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `apcontact` (
|
||||
`url` varbinary(255) NOT NULL COMMENT 'URL of the contact',
|
||||
`uuid` varchar(255) COMMENT '',
|
||||
`type` varchar(20) NOT NULL COMMENT '',
|
||||
`following` varchar(255) COMMENT '',
|
||||
`followers` varchar(255) COMMENT '',
|
||||
`inbox` varchar(255) NOT NULL COMMENT '',
|
||||
`outbox` varchar(255) COMMENT '',
|
||||
`sharedinbox` varchar(255) COMMENT '',
|
||||
`manually-approve` boolean COMMENT '',
|
||||
`nick` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`name` varchar(255) COMMENT '',
|
||||
`about` text COMMENT '',
|
||||
`photo` varchar(255) COMMENT '',
|
||||
`addr` varchar(255) COMMENT '',
|
||||
`alias` varchar(255) COMMENT '',
|
||||
`pubkey` text COMMENT '',
|
||||
`subscribe` varchar(255) COMMENT '',
|
||||
`baseurl` varchar(255) COMMENT 'baseurl of the ap contact',
|
||||
`gsid` int unsigned COMMENT 'Global Server ID',
|
||||
`generator` varchar(255) COMMENT 'Name of the contact\'s system',
|
||||
`following_count` int unsigned DEFAULT 0 COMMENT 'Number of following contacts',
|
||||
`followers_count` int unsigned DEFAULT 0 COMMENT 'Number of followers',
|
||||
`statuses_count` int unsigned DEFAULT 0 COMMENT 'Number of posts',
|
||||
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
PRIMARY KEY(`url`),
|
||||
INDEX `addr` (`addr`(32)),
|
||||
INDEX `alias` (`alias`(190)),
|
||||
INDEX `followers` (`followers`(190)),
|
||||
INDEX `baseurl` (`baseurl`(190)),
|
||||
INDEX `sharedinbox` (`sharedinbox`(190)),
|
||||
INDEX `gsid` (`gsid`),
|
||||
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='ActivityPub compatible contacts - used in the ActivityPub implementation';
|
||||
|
||||
--
|
||||
-- TABLE attach
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `attach` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'generated index',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id',
|
||||
`hash` varchar(64) NOT NULL DEFAULT '' COMMENT 'hash',
|
||||
`filename` varchar(255) NOT NULL DEFAULT '' COMMENT 'filename of original',
|
||||
`filetype` varchar(64) NOT NULL DEFAULT '' COMMENT 'mimetype',
|
||||
`filesize` int unsigned NOT NULL DEFAULT 0 COMMENT 'size in bytes',
|
||||
`data` longblob NOT NULL COMMENT 'file data',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation time',
|
||||
`edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'last edit time',
|
||||
`allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups',
|
||||
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
|
||||
`backend-class` tinytext COMMENT 'Storage backend class',
|
||||
`backend-ref` text COMMENT 'Storage backend data reference',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='file attachments';
|
||||
|
||||
--
|
||||
-- TABLE auth_codes
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `auth_codes` (
|
||||
`id` varchar(40) NOT NULL COMMENT '',
|
||||
`client_id` varchar(20) NOT NULL DEFAULT '' COMMENT '',
|
||||
`redirect_uri` varchar(200) NOT NULL DEFAULT '' COMMENT '',
|
||||
`expires` int NOT NULL DEFAULT 0 COMMENT '',
|
||||
`scope` varchar(250) NOT NULL DEFAULT '' COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `client_id` (`client_id`),
|
||||
FOREIGN KEY (`client_id`) REFERENCES `clients` (`client_id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage';
|
||||
|
||||
--
|
||||
-- TABLE cache
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `cache` (
|
||||
`k` varbinary(255) NOT NULL COMMENT 'cache key',
|
||||
`v` mediumtext COMMENT 'cached serialized value',
|
||||
`expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of cache expiration',
|
||||
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of cache insertion',
|
||||
PRIMARY KEY(`k`),
|
||||
INDEX `k_expires` (`k`,`expires`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Stores temporary data';
|
||||
|
||||
--
|
||||
-- TABLE challenge
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `challenge` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`challenge` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`expire` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`type` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`last_update` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
PRIMARY KEY(`id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
|
||||
|
||||
--
|
||||
-- TABLE config
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `config` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
`cat` varbinary(50) NOT NULL DEFAULT '' COMMENT '',
|
||||
`k` varbinary(50) NOT NULL DEFAULT '' COMMENT '',
|
||||
`v` mediumtext COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `cat_k` (`cat`,`k`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='main configuration storage';
|
||||
|
||||
--
|
||||
-- TABLE contact-relation
|
||||
--
|
||||
|
|
@ -261,8 +422,12 @@ CREATE TABLE IF NOT EXISTS `contact-relation` (
|
|||
`cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact the related contact had interacted with',
|
||||
`relation-cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'related contact who had interacted with the contact',
|
||||
`last-interaction` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last interaction',
|
||||
`follow-updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last update of the contact relationship',
|
||||
`follows` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
PRIMARY KEY(`cid`,`relation-cid`),
|
||||
INDEX `relation-cid` (`relation-cid`)
|
||||
INDEX `relation-cid` (`relation-cid`),
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`relation-cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Contact relations';
|
||||
|
||||
--
|
||||
|
|
@ -278,7 +443,8 @@ CREATE TABLE IF NOT EXISTS `conv` (
|
|||
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'edited timestamp',
|
||||
`subject` text COMMENT 'subject of initial message',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`)
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='private messages';
|
||||
|
||||
--
|
||||
|
|
@ -298,13 +464,27 @@ CREATE TABLE IF NOT EXISTS `conversation` (
|
|||
INDEX `received` (`received`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Raw data and structure information for messages';
|
||||
|
||||
--
|
||||
-- TABLE delayed-post
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `delayed-post` (
|
||||
`id` int unsigned NOT NULL auto_increment,
|
||||
`uri` varchar(255) COMMENT 'URI of the post that will be distributed later',
|
||||
`uid` mediumint unsigned COMMENT 'Owner User id',
|
||||
`delayed` datetime COMMENT 'delay time',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `uid_uri` (`uid`,`uri`(190)),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Posts that are about to be distributed at a later time';
|
||||
|
||||
--
|
||||
-- TABLE diaspora-interaction
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `diaspora-interaction` (
|
||||
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`interaction` mediumtext COMMENT 'The Diaspora interaction',
|
||||
PRIMARY KEY(`uri-id`)
|
||||
PRIMARY KEY(`uri-id`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Signed Diaspora Interaction';
|
||||
|
||||
--
|
||||
|
|
@ -332,7 +512,10 @@ CREATE TABLE IF NOT EXISTS `event` (
|
|||
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid_start` (`uid`,`start`)
|
||||
INDEX `uid_start` (`uid`,`start`),
|
||||
INDEX `cid` (`cid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events';
|
||||
|
||||
--
|
||||
|
|
@ -374,88 +557,12 @@ CREATE TABLE IF NOT EXISTS `fsuggest` (
|
|||
`photo` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`note` text COMMENT '',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
PRIMARY KEY(`id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='friend suggestion stuff';
|
||||
|
||||
--
|
||||
-- TABLE gcign
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `gcign` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Local User id',
|
||||
`gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'gcontact.id of ignored contact',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `cid` (`cid`),
|
||||
INDEX `uid` (`uid`),
|
||||
INDEX `gcid` (`gcid`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contacts ignored by friend suggestions';
|
||||
|
||||
--
|
||||
-- TABLE gcontact
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `gcontact` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this contact is known by',
|
||||
`nick` varchar(255) NOT NULL DEFAULT '' COMMENT 'Nick- and user name of the contact',
|
||||
`url` varchar(255) NOT NULL DEFAULT '' COMMENT 'Link to the contacts profile page',
|
||||
`nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`photo` varchar(255) NOT NULL DEFAULT '' COMMENT 'Link to the profile photo',
|
||||
`connect` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`updated` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`last_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`last_discovery` datetime DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last contact discovery',
|
||||
`archive_date` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`archived` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`location` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`about` text COMMENT '',
|
||||
`keywords` text COMMENT 'puplic keywords (interests)',
|
||||
`gender` varchar(32) NOT NULL DEFAULT '' COMMENT 'Deprecated',
|
||||
`birthday` varchar(32) NOT NULL DEFAULT '0001-01-01' COMMENT '',
|
||||
`community` boolean NOT NULL DEFAULT '0' COMMENT '1 if contact is forum account',
|
||||
`contact-type` tinyint NOT NULL DEFAULT -1 COMMENT '',
|
||||
`hide` boolean NOT NULL DEFAULT '0' COMMENT '1 = should be hidden from search',
|
||||
`nsfw` boolean NOT NULL DEFAULT '0' COMMENT '1 = contact posts nsfw content',
|
||||
`network` char(4) NOT NULL DEFAULT '' COMMENT 'social network protocol',
|
||||
`addr` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`notify` varchar(255) COMMENT '',
|
||||
`alias` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`generation` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`server_url` varchar(255) NOT NULL DEFAULT '' COMMENT 'baseurl of the contacts server',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `nurl` (`nurl`(190)),
|
||||
INDEX `name` (`name`(64)),
|
||||
INDEX `nick` (`nick`(32)),
|
||||
INDEX `addr` (`addr`(64)),
|
||||
INDEX `hide_network_updated` (`hide`,`network`,`updated`),
|
||||
INDEX `updated` (`updated`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='global contacts';
|
||||
|
||||
--
|
||||
-- TABLE gfollower
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `gfollower` (
|
||||
`gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'global contact',
|
||||
`follower-gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'global contact of the follower',
|
||||
`deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates that the connection has been deleted',
|
||||
PRIMARY KEY(`gcid`,`follower-gcid`),
|
||||
INDEX `follower-gcid` (`follower-gcid`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Followers of global contacts';
|
||||
|
||||
--
|
||||
-- TABLE glink
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `glink` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`cid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
`gcid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`zcid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `cid_uid_gcid_zcid` (`cid`,`uid`,`gcid`,`zcid`),
|
||||
INDEX `gcid` (`gcid`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='\'friends of friends\' linkages derived from poco';
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='friend suggestion stuff';
|
||||
|
||||
--
|
||||
-- TABLE group
|
||||
|
|
@ -467,7 +574,8 @@ CREATE TABLE IF NOT EXISTS `group` (
|
|||
`deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the group has been deleted',
|
||||
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'human readable name of group',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`)
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, group info';
|
||||
|
||||
--
|
||||
|
|
@ -479,36 +587,11 @@ CREATE TABLE IF NOT EXISTS `group_member` (
|
|||
`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id of the member assigned to the associated group',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `contactid` (`contact-id`),
|
||||
UNIQUE INDEX `gid_contactid` (`gid`,`contact-id`)
|
||||
UNIQUE INDEX `gid_contactid` (`gid`,`contact-id`),
|
||||
FOREIGN KEY (`gid`) REFERENCES `group` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, member info';
|
||||
|
||||
--
|
||||
-- TABLE gserver
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `gserver` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`version` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`site_name` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`info` text COMMENT '',
|
||||
`register_policy` tinyint NOT NULL DEFAULT 0 COMMENT '',
|
||||
`registered-users` int unsigned NOT NULL DEFAULT 0 COMMENT 'Number of registered users',
|
||||
`directory-type` tinyint DEFAULT 0 COMMENT 'Type of directory service (Poco, Mastodon)',
|
||||
`poco` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`noscrape` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`network` char(4) NOT NULL DEFAULT '' COMMENT '',
|
||||
`platform` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`relay-subscribe` boolean NOT NULL DEFAULT '0' COMMENT 'Has the server subscribed to the relay system',
|
||||
`relay-scope` varchar(10) NOT NULL DEFAULT '' COMMENT 'The scope of messages that the server wants to get',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`last_poco_query` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`last_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `nurl` (`nurl`(190))
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Global servers';
|
||||
|
||||
--
|
||||
-- TABLE gserver-tag
|
||||
--
|
||||
|
|
@ -516,7 +599,8 @@ CREATE TABLE IF NOT EXISTS `gserver-tag` (
|
|||
`gserver-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'The id of the gserver',
|
||||
`tag` varchar(100) NOT NULL DEFAULT '' COMMENT 'Tag that the server has subscribed',
|
||||
PRIMARY KEY(`gserver-id`,`tag`),
|
||||
INDEX `tag` (`tag`)
|
||||
INDEX `tag` (`tag`),
|
||||
FOREIGN KEY (`gserver-id`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Tags that the server has subscribed';
|
||||
|
||||
--
|
||||
|
|
@ -532,6 +616,16 @@ CREATE TABLE IF NOT EXISTS `hook` (
|
|||
UNIQUE INDEX `hook_file_function` (`hook`,`file`,`function`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='addon hook registry';
|
||||
|
||||
--
|
||||
-- TABLE host
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `host` (
|
||||
`id` tinyint unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`name` varchar(128) NOT NULL DEFAULT '' COMMENT 'The hostname',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `name` (`name`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Hostname';
|
||||
|
||||
--
|
||||
-- TABLE inbox-status
|
||||
--
|
||||
|
|
@ -552,7 +646,7 @@ CREATE TABLE IF NOT EXISTS `inbox-status` (
|
|||
CREATE TABLE IF NOT EXISTS `intro` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
`fid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`fid` int unsigned COMMENT '',
|
||||
`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`knowyou` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`duplex` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
|
|
@ -561,7 +655,11 @@ CREATE TABLE IF NOT EXISTS `intro` (
|
|||
`datetime` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`blocked` boolean NOT NULL DEFAULT '1' COMMENT '',
|
||||
`ignore` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
PRIMARY KEY(`id`)
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `contact-id` (`contact-id`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
|
||||
|
||||
--
|
||||
|
|
@ -573,9 +671,9 @@ CREATE TABLE IF NOT EXISTS `item` (
|
|||
`uri` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`uri-hash` varchar(80) NOT NULL DEFAULT '' COMMENT 'RIPEMD-128 hash from uri',
|
||||
`parent` int unsigned NOT NULL DEFAULT 0 COMMENT 'item.id of the parent to this item if it is a reply of some form; otherwise this must be set to the id of this item',
|
||||
`parent-uri` varchar(255) NOT NULL DEFAULT '' COMMENT 'uri of the parent to this item',
|
||||
`parent-uri-id` int unsigned COMMENT 'Id of the item-uri table that contains the parent uri',
|
||||
`parent` int unsigned COMMENT 'item.id of the parent to this item if it is a reply of some form; otherwise this must be set to the id of this item',
|
||||
`parent-uri` varchar(255) NOT NULL DEFAULT '' COMMENT 'uri of the top-level parent to this item',
|
||||
`parent-uri-id` int unsigned COMMENT 'Id of the item-uri table that contains the top-level parent uri',
|
||||
`thr-parent` varchar(255) NOT NULL DEFAULT '' COMMENT 'If the parent of this item is not the top-level item in the conversation, the uri of the immediate parent; otherwise set to parent-uri',
|
||||
`thr-parent-id` int unsigned COMMENT 'Id of the item-uri table that contains the thread parent uri',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation timestamp.',
|
||||
|
|
@ -587,8 +685,9 @@ CREATE TABLE IF NOT EXISTS `item` (
|
|||
`network` char(4) NOT NULL DEFAULT '' COMMENT 'Network from where the item comes from',
|
||||
`owner-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Link to the contact table with uid=0 of the owner of this item',
|
||||
`author-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Link to the contact table with uid=0 of the author of this item',
|
||||
`causer-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Link to the contact table with uid=0 of the contact that caused the item creation',
|
||||
`icid` int unsigned COMMENT 'Id of the item-content table entry that contains the whole item content',
|
||||
`iaid` int unsigned COMMENT 'Id of the item-activity table entry that contains the activity data',
|
||||
`vid` smallint unsigned COMMENT 'Id of the verb table entry that contains the activity verbs',
|
||||
`extid` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`post-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Post type (personal note, bookmark, ...)',
|
||||
`global` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
|
|
@ -607,8 +706,9 @@ CREATE TABLE IF NOT EXISTS `item` (
|
|||
`forum_mode` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`psid` int unsigned COMMENT 'ID of the permission set of this post',
|
||||
`resource-id` varchar(32) NOT NULL DEFAULT '' COMMENT 'Used to link other tables to items, it identifies the linked resource (e.g. photo) and if set must also set resource_type',
|
||||
`event-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Used to link to the event.id',
|
||||
`attach` mediumtext COMMENT 'JSON structure representing attachments to this item',
|
||||
`event-id` int unsigned COMMENT 'Used to link to the event.id',
|
||||
`iaid` int unsigned COMMENT 'Deprecated',
|
||||
`attach` mediumtext COMMENT 'Deprecated',
|
||||
`allow_cid` mediumtext COMMENT 'Deprecated',
|
||||
`allow_gid` mediumtext COMMENT 'Deprecated',
|
||||
`deny_cid` mediumtext COMMENT 'Deprecated',
|
||||
|
|
@ -662,12 +762,27 @@ CREATE TABLE IF NOT EXISTS `item` (
|
|||
INDEX `resource-id` (`resource-id`),
|
||||
INDEX `deleted_changed` (`deleted`,`changed`),
|
||||
INDEX `uid_wall_changed` (`uid`,`wall`,`changed`),
|
||||
INDEX `uid_unseen_wall` (`uid`,`unseen`,`wall`),
|
||||
INDEX `mention_uid_id` (`mention`,`uid`,`id`),
|
||||
INDEX `uid_eventid` (`uid`,`event-id`),
|
||||
INDEX `icid` (`icid`),
|
||||
INDEX `iaid` (`iaid`),
|
||||
INDEX `vid` (`vid`),
|
||||
INDEX `psid_wall` (`psid`,`wall`),
|
||||
INDEX `uri-id` (`uri-id`)
|
||||
INDEX `uri-id` (`uri-id`),
|
||||
INDEX `parent-uri-id` (`parent-uri-id`),
|
||||
INDEX `thr-parent-id` (`thr-parent-id`),
|
||||
INDEX `causer-id` (`causer-id`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`parent-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`thr-parent-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`owner-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
FOREIGN KEY (`author-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
FOREIGN KEY (`causer-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
FOREIGN KEY (`vid`) REFERENCES `verb` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`psid`) REFERENCES `permissionset` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Structure for all posts';
|
||||
|
||||
--
|
||||
|
|
@ -682,7 +797,8 @@ CREATE TABLE IF NOT EXISTS `item-activity` (
|
|||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `uri-hash` (`uri-hash`),
|
||||
INDEX `uri` (`uri`(191)),
|
||||
INDEX `uri-id` (`uri-id`)
|
||||
INDEX `uri-id` (`uri-id`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Activities for items';
|
||||
|
||||
--
|
||||
|
|
@ -696,6 +812,7 @@ CREATE TABLE IF NOT EXISTS `item-content` (
|
|||
`title` varchar(255) NOT NULL DEFAULT '' COMMENT 'item title',
|
||||
`content-warning` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`body` mediumtext COMMENT 'item body content',
|
||||
`raw-body` mediumtext COMMENT 'Body without embedded media links',
|
||||
`location` varchar(255) NOT NULL DEFAULT '' COMMENT 'text location where this item originated',
|
||||
`coord` varchar(255) NOT NULL DEFAULT '' COMMENT 'longitude/latitude pair representing location where this item originated',
|
||||
`language` text COMMENT 'Language information about this post',
|
||||
|
|
@ -710,23 +827,13 @@ CREATE TABLE IF NOT EXISTS `item-content` (
|
|||
`verb` varchar(100) NOT NULL DEFAULT '' COMMENT 'ActivityStreams verb',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `uri-plink-hash` (`uri-plink-hash`),
|
||||
FULLTEXT INDEX `title-content-warning-body` (`title`,`content-warning`,`body`),
|
||||
INDEX `uri` (`uri`(191)),
|
||||
INDEX `plink` (`plink`(191)),
|
||||
INDEX `uri-id` (`uri-id`)
|
||||
INDEX `uri-id` (`uri-id`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Content for all posts';
|
||||
|
||||
--
|
||||
-- TABLE item-uri
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `item-uri` (
|
||||
`id` int unsigned NOT NULL auto_increment,
|
||||
`uri` varbinary(255) NOT NULL COMMENT 'URI of an item',
|
||||
`guid` varbinary(255) COMMENT 'A unique identifier for an item',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `uri` (`uri`),
|
||||
INDEX `guid` (`guid`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='URI and GUID for items';
|
||||
|
||||
--
|
||||
-- TABLE locks
|
||||
--
|
||||
|
|
@ -750,8 +857,8 @@ CREATE TABLE IF NOT EXISTS `mail` (
|
|||
`from-name` varchar(255) NOT NULL DEFAULT '' COMMENT 'name of the sender',
|
||||
`from-photo` varchar(255) NOT NULL DEFAULT '' COMMENT 'contact photo link of the sender',
|
||||
`from-url` varchar(255) NOT NULL DEFAULT '' COMMENT 'profile linke of the sender',
|
||||
`contact-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'contact.id',
|
||||
`convid` int unsigned NOT NULL DEFAULT 0 COMMENT 'conv.id',
|
||||
`contact-id` varchar(255) COMMENT 'contact.id',
|
||||
`convid` int unsigned COMMENT 'conv.id',
|
||||
`title` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`body` mediumtext COMMENT '',
|
||||
`seen` boolean NOT NULL DEFAULT '0' COMMENT 'if message visited it is 1',
|
||||
|
|
@ -766,7 +873,8 @@ CREATE TABLE IF NOT EXISTS `mail` (
|
|||
INDEX `convid` (`convid`),
|
||||
INDEX `uri` (`uri`(64)),
|
||||
INDEX `parent-uri` (`parent-uri`(64)),
|
||||
INDEX `contactid` (`contact-id`(32))
|
||||
INDEX `contactid` (`contact-id`(32)),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='private messages';
|
||||
|
||||
--
|
||||
|
|
@ -786,7 +894,9 @@ CREATE TABLE IF NOT EXISTS `mailacct` (
|
|||
`movetofolder` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`pubmail` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`last_check` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
PRIMARY KEY(`id`)
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Mail account data for fetching mails';
|
||||
|
||||
--
|
||||
|
|
@ -797,7 +907,10 @@ CREATE TABLE IF NOT EXISTS `manage` (
|
|||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
`mid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `uid_mid` (`uid`,`mid`)
|
||||
UNIQUE INDEX `uid_mid` (`uid`,`mid`),
|
||||
INDEX `mid` (`mid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`mid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='table of accounts that can manage each other';
|
||||
|
||||
--
|
||||
|
|
@ -813,8 +926,8 @@ CREATE TABLE IF NOT EXISTS `notify` (
|
|||
`msg` mediumtext COMMENT '',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id',
|
||||
`link` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`iid` int unsigned NOT NULL DEFAULT 0 COMMENT 'item.id',
|
||||
`parent` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`iid` int unsigned COMMENT 'item.id',
|
||||
`parent` int unsigned COMMENT '',
|
||||
`uri-id` int unsigned COMMENT 'Item-uri id of the related post',
|
||||
`parent-uri-id` int unsigned COMMENT 'Item-uri id of the parent of the related post',
|
||||
`seen` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
|
|
@ -825,7 +938,12 @@ CREATE TABLE IF NOT EXISTS `notify` (
|
|||
PRIMARY KEY(`id`),
|
||||
INDEX `seen_uid_date` (`seen`,`uid`,`date`),
|
||||
INDEX `uid_date` (`uid`,`date`),
|
||||
INDEX `uid_type_link` (`uid`,`type`,`link`(190))
|
||||
INDEX `uid_type_link` (`uid`,`type`,`link`(190)),
|
||||
INDEX `uri-id` (`uri-id`),
|
||||
INDEX `parent-uri-id` (`parent-uri-id`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`parent-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='notifications';
|
||||
|
||||
--
|
||||
|
|
@ -834,11 +952,19 @@ CREATE TABLE IF NOT EXISTS `notify` (
|
|||
CREATE TABLE IF NOT EXISTS `notify-threads` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`notify-id` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`master-parent-item` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`master-parent-item` int unsigned COMMENT '',
|
||||
`master-parent-uri-id` int unsigned COMMENT 'Item-uri id of the parent of the related post',
|
||||
`parent-item` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`receiver-uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
PRIMARY KEY(`id`)
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `master-parent-item` (`master-parent-item`),
|
||||
INDEX `master-parent-uri-id` (`master-parent-uri-id`),
|
||||
INDEX `receiver-uid` (`receiver-uid`),
|
||||
INDEX `notify-id` (`notify-id`),
|
||||
FOREIGN KEY (`notify-id`) REFERENCES `notify` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`master-parent-item`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`master-parent-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`receiver-uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
|
||||
|
||||
--
|
||||
|
|
@ -858,12 +984,14 @@ CREATE TABLE IF NOT EXISTS `oembed` (
|
|||
--
|
||||
CREATE TABLE IF NOT EXISTS `openwebauth-token` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id - currently unused',
|
||||
`type` varchar(32) NOT NULL DEFAULT '' COMMENT 'Verify type',
|
||||
`token` varchar(255) NOT NULL DEFAULT '' COMMENT 'A generated token',
|
||||
`meta` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of creation',
|
||||
PRIMARY KEY(`id`)
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Store OpenWebAuth token to verify contacts';
|
||||
|
||||
--
|
||||
|
|
@ -889,36 +1017,26 @@ CREATE TABLE IF NOT EXISTS `participation` (
|
|||
`fid` int unsigned NOT NULL COMMENT '',
|
||||
PRIMARY KEY(`iid`,`server`),
|
||||
INDEX `cid` (`cid`),
|
||||
INDEX `fid` (`fid`)
|
||||
INDEX `fid` (`fid`),
|
||||
FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`fid`) REFERENCES `fcontact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Storage for participation messages from Diaspora';
|
||||
|
||||
--
|
||||
-- TABLE pconfig
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `pconfig` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'Primary key',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
`cat` varbinary(50) NOT NULL DEFAULT '' COMMENT '',
|
||||
`k` varbinary(100) NOT NULL DEFAULT '' COMMENT '',
|
||||
`v` mediumtext COMMENT '',
|
||||
`cat` varchar(50) NOT NULL DEFAULT '' COMMENT 'Category',
|
||||
`k` varchar(100) NOT NULL DEFAULT '' COMMENT 'Key',
|
||||
`v` mediumtext COMMENT 'Value',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `uid_cat_k` (`uid`,`cat`,`k`)
|
||||
UNIQUE INDEX `uid_cat_k` (`uid`,`cat`,`k`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='personal (per user) configuration storage';
|
||||
|
||||
--
|
||||
-- TABLE permissionset
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `permissionset` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner id of this permission set',
|
||||
`allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>\'',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups',
|
||||
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid_allow_cid_allow_gid_deny_cid_deny_gid` (`allow_cid`(50),`allow_gid`(30),`deny_cid`(50),`deny_gid`(30))
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
|
||||
|
||||
--
|
||||
-- TABLE photo
|
||||
--
|
||||
|
|
@ -928,6 +1046,7 @@ CREATE TABLE IF NOT EXISTS `photo` (
|
|||
`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id',
|
||||
`guid` char(16) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this photo',
|
||||
`resource-id` char(32) NOT NULL DEFAULT '' COMMENT '',
|
||||
`hash` char(32) COMMENT 'hash value of the photo',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation date',
|
||||
`edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'last edited date',
|
||||
`title` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
|
|
@ -955,39 +1074,105 @@ CREATE TABLE IF NOT EXISTS `photo` (
|
|||
INDEX `uid_profile` (`uid`,`profile`),
|
||||
INDEX `uid_album_scale_created` (`uid`,`album`(32),`scale`,`created`),
|
||||
INDEX `uid_album_resource-id_created` (`uid`,`album`(32),`resource-id`,`created`),
|
||||
INDEX `resource-id` (`resource-id`)
|
||||
INDEX `resource-id` (`resource-id`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='photo storage';
|
||||
|
||||
--
|
||||
-- TABLE poll
|
||||
-- TABLE post-category
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `poll` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
CREATE TABLE IF NOT EXISTS `post-category` (
|
||||
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
`q0` text COMMENT '',
|
||||
`q1` text COMMENT '',
|
||||
`q2` text COMMENT '',
|
||||
`q3` text COMMENT '',
|
||||
`q4` text COMMENT '',
|
||||
`q5` text COMMENT '',
|
||||
`q6` text COMMENT '',
|
||||
`q7` text COMMENT '',
|
||||
`q8` text COMMENT '',
|
||||
`q9` text COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Currently unused table for storing poll results';
|
||||
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`tid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
PRIMARY KEY(`uri-id`,`uid`,`type`,`tid`),
|
||||
INDEX `uri-id` (`tid`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`tid`) REFERENCES `tag` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='post relation to categories';
|
||||
|
||||
--
|
||||
-- TABLE poll_result
|
||||
-- TABLE post-delivery-data
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `poll_result` (
|
||||
CREATE TABLE IF NOT EXISTS `post-delivery-data` (
|
||||
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`postopts` text COMMENT 'External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery',
|
||||
`inform` mediumtext COMMENT 'Additional receivers of the linked item',
|
||||
`queue_count` mediumint NOT NULL DEFAULT 0 COMMENT 'Initial number of delivery recipients, used as item.delivery_queue_count',
|
||||
`queue_done` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries, used as item.delivery_queue_done',
|
||||
`queue_failed` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of unsuccessful deliveries, used as item.delivery_queue_failed',
|
||||
`activitypub` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via ActivityPub',
|
||||
`dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via DFRN',
|
||||
`legacy_dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via legacy DFRN',
|
||||
`diaspora` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via Diaspora',
|
||||
`ostatus` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via OStatus',
|
||||
PRIMARY KEY(`uri-id`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items';
|
||||
|
||||
--
|
||||
-- TABLE post-media
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `post-media` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`poll_id` int unsigned NOT NULL DEFAULT 0,
|
||||
`choice` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`url` varbinary(511) NOT NULL COMMENT 'Media URL',
|
||||
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Media type',
|
||||
`mimetype` varchar(60) COMMENT '',
|
||||
`height` smallint unsigned COMMENT 'Height of the media',
|
||||
`width` smallint unsigned COMMENT 'Width of the media',
|
||||
`size` int unsigned COMMENT 'Media size',
|
||||
`preview` varbinary(255) COMMENT 'Preview URL',
|
||||
`preview-height` smallint unsigned COMMENT 'Height of the preview picture',
|
||||
`preview-width` smallint unsigned COMMENT 'Width of the preview picture',
|
||||
`description` text COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `poll_id` (`poll_id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='data for polls - currently unused';
|
||||
UNIQUE INDEX `uri-id-url` (`uri-id`,`url`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Attached media';
|
||||
|
||||
--
|
||||
-- TABLE post-tag
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `post-tag` (
|
||||
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`tid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Contact id of the mentioned public contact',
|
||||
PRIMARY KEY(`uri-id`,`type`,`tid`,`cid`),
|
||||
INDEX `tid` (`tid`),
|
||||
INDEX `cid` (`cid`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`tid`) REFERENCES `tag` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='post relation to tags';
|
||||
|
||||
--
|
||||
-- TABLE post-user
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `post-user` (
|
||||
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`uid` mediumint unsigned NOT NULL COMMENT 'Owner id which owns this copy of the item',
|
||||
`protocol` tinyint unsigned COMMENT 'Protocol used to deliver the item for this user',
|
||||
`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id',
|
||||
`unseen` boolean NOT NULL DEFAULT '1' COMMENT 'post has not been seen',
|
||||
`hidden` boolean NOT NULL DEFAULT '0' COMMENT 'Marker to hide the post from the user',
|
||||
`notification-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`origin` boolean NOT NULL DEFAULT '0' COMMENT 'item originated at this site',
|
||||
`psid` int unsigned COMMENT 'ID of the permission set of this post',
|
||||
PRIMARY KEY(`uid`,`uri-id`),
|
||||
INDEX `uri-id` (`uri-id`),
|
||||
INDEX `contact-id` (`contact-id`),
|
||||
INDEX `psid` (`psid`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`psid`) REFERENCES `permissionset` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific post data';
|
||||
|
||||
--
|
||||
-- TABLE process
|
||||
|
|
@ -1048,7 +1233,8 @@ CREATE TABLE IF NOT EXISTS `profile` (
|
|||
`net-publish` boolean NOT NULL DEFAULT '0' COMMENT 'publish profile in global directory',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid_is-default` (`uid`,`is-default`),
|
||||
FULLTEXT INDEX `pub_keywords` (`pub_keywords`)
|
||||
FULLTEXT INDEX `pub_keywords` (`pub_keywords`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='user profiles data';
|
||||
|
||||
--
|
||||
|
|
@ -1061,7 +1247,11 @@ CREATE TABLE IF NOT EXISTS `profile_check` (
|
|||
`dfrn_id` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`sec` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`expire` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
PRIMARY KEY(`id`)
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`),
|
||||
INDEX `cid` (`cid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='DFRN remote auth use';
|
||||
|
||||
--
|
||||
|
|
@ -1079,7 +1269,9 @@ CREATE TABLE IF NOT EXISTS `profile_field` (
|
|||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`),
|
||||
INDEX `order` (`order`),
|
||||
INDEX `psid` (`psid`)
|
||||
INDEX `psid` (`psid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`psid`) REFERENCES `permissionset` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Custom profile fields';
|
||||
|
||||
--
|
||||
|
|
@ -1097,7 +1289,9 @@ CREATE TABLE IF NOT EXISTS `push_subscriber` (
|
|||
`renewed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of last subscription renewal',
|
||||
`secret` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `next_try` (`next_try`)
|
||||
INDEX `next_try` (`next_try`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Used for OStatus: Contains feed subscribers';
|
||||
|
||||
--
|
||||
|
|
@ -1111,7 +1305,9 @@ CREATE TABLE IF NOT EXISTS `register` (
|
|||
`password` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`language` varchar(16) NOT NULL DEFAULT '' COMMENT '',
|
||||
`note` text COMMENT '',
|
||||
PRIMARY KEY(`id`)
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='registrations requiring admin approval';
|
||||
|
||||
--
|
||||
|
|
@ -1122,7 +1318,8 @@ CREATE TABLE IF NOT EXISTS `search` (
|
|||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
`term` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`)
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
|
||||
|
||||
--
|
||||
|
|
@ -1139,88 +1336,20 @@ CREATE TABLE IF NOT EXISTS `session` (
|
|||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='web session storage';
|
||||
|
||||
--
|
||||
-- TABLE term
|
||||
-- TABLE storage
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `term` (
|
||||
`tid` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
`oid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`otype` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`term` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`guid` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`received` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`global` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
PRIMARY KEY(`tid`),
|
||||
INDEX `term_type` (`term`(64),`type`),
|
||||
INDEX `oid_otype_type_term` (`oid`,`otype`,`type`,`term`(32)),
|
||||
INDEX `uid_otype_type_term_global_created` (`uid`,`otype`,`type`,`term`(32),`global`,`created`),
|
||||
INDEX `uid_otype_type_url` (`uid`,`otype`,`type`,`url`(64)),
|
||||
INDEX `guid` (`guid`(64))
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='item taxonomy (categories, tags, etc.) table';
|
||||
|
||||
--
|
||||
-- TABLE tag
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `tag` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
`name` varchar(96) NOT NULL DEFAULT '' COMMENT '',
|
||||
`url` varbinary(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `type_name_url` (`name`,`url`),
|
||||
INDEX `url` (`url`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='tags and mentions';
|
||||
|
||||
--
|
||||
-- TABLE post-category
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `post-category` (
|
||||
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`tid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
PRIMARY KEY(`uri-id`,`uid`,`type`,`tid`),
|
||||
INDEX `uri-id` (`tid`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='post relation to categories';
|
||||
|
||||
--
|
||||
-- TABLE post-delivery-data
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `post-delivery-data` (
|
||||
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`postopts` text COMMENT 'External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery',
|
||||
`inform` mediumtext COMMENT 'Additional receivers of the linked item',
|
||||
`queue_count` mediumint NOT NULL DEFAULT 0 COMMENT 'Initial number of delivery recipients, used as item.delivery_queue_count',
|
||||
`queue_done` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries, used as item.delivery_queue_done',
|
||||
`queue_failed` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of unsuccessful deliveries, used as item.delivery_queue_failed',
|
||||
`activitypub` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via ActivityPub',
|
||||
`dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via DFRN',
|
||||
`legacy_dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via legacy DFRN',
|
||||
`diaspora` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via Diaspora',
|
||||
`ostatus` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via OStatus',
|
||||
PRIMARY KEY(`uri-id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items';
|
||||
|
||||
--
|
||||
-- TABLE post-tag
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `post-tag` (
|
||||
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`tid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Contact id of the mentioned public contact',
|
||||
PRIMARY KEY(`uri-id`,`type`,`tid`,`cid`),
|
||||
INDEX `uri-id` (`tid`),
|
||||
INDEX `cid` (`tid`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='post relation to tags';
|
||||
CREATE TABLE IF NOT EXISTS `storage` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'Auto incremented image data id',
|
||||
`data` longblob NOT NULL COMMENT 'file data',
|
||||
PRIMARY KEY(`id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Data stored by Database storage backend';
|
||||
|
||||
--
|
||||
-- TABLE thread
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `thread` (
|
||||
`iid` int unsigned NOT NULL DEFAULT 0 COMMENT 'sequential ID',
|
||||
`uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`owner-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Item owner',
|
||||
|
|
@ -1256,7 +1385,14 @@ CREATE TABLE IF NOT EXISTS `thread` (
|
|||
INDEX `uid_received` (`uid`,`received`),
|
||||
INDEX `uid_commented` (`uid`,`commented`),
|
||||
INDEX `uid_wall_received` (`uid`,`wall`,`received`),
|
||||
INDEX `private_wall_origin_commented` (`private`,`wall`,`origin`,`commented`)
|
||||
INDEX `private_wall_origin_commented` (`private`,`wall`,`origin`,`commented`),
|
||||
INDEX `uri-id` (`uri-id`),
|
||||
FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`owner-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
FOREIGN KEY (`author-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Thread related data';
|
||||
|
||||
--
|
||||
|
|
@ -1269,62 +1405,13 @@ CREATE TABLE IF NOT EXISTS `tokens` (
|
|||
`expires` int NOT NULL DEFAULT 0 COMMENT '',
|
||||
`scope` varchar(200) NOT NULL DEFAULT '' COMMENT '',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
|
||||
PRIMARY KEY(`id`)
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `client_id` (`client_id`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`client_id`) REFERENCES `clients` (`client_id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage';
|
||||
|
||||
--
|
||||
-- TABLE user
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `user` (
|
||||
`uid` mediumint unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`parent-uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'The parent user that has full control about this user',
|
||||
`guid` varchar(64) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this user',
|
||||
`username` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this user is known by',
|
||||
`password` varchar(255) NOT NULL DEFAULT '' COMMENT 'encrypted password',
|
||||
`legacy_password` boolean NOT NULL DEFAULT '0' COMMENT 'Is the password hash double-hashed?',
|
||||
`nickname` varchar(255) NOT NULL DEFAULT '' COMMENT 'nick- and user name',
|
||||
`email` varchar(255) NOT NULL DEFAULT '' COMMENT 'the users email address',
|
||||
`openid` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`timezone` varchar(128) NOT NULL DEFAULT '' COMMENT 'PHP-legal timezone',
|
||||
`language` varchar(32) NOT NULL DEFAULT 'en' COMMENT 'default language',
|
||||
`register_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of registration',
|
||||
`login_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last login',
|
||||
`default-location` varchar(255) NOT NULL DEFAULT '' COMMENT 'Default for item.location',
|
||||
`allow_location` boolean NOT NULL DEFAULT '0' COMMENT '1 allows to display the location',
|
||||
`theme` varchar(255) NOT NULL DEFAULT '' COMMENT 'user theme preference',
|
||||
`pubkey` text COMMENT 'RSA public key 4096 bit',
|
||||
`prvkey` text COMMENT 'RSA private key 4096 bit',
|
||||
`spubkey` text COMMENT '',
|
||||
`sprvkey` text COMMENT '',
|
||||
`verified` boolean NOT NULL DEFAULT '0' COMMENT 'user is verified through email',
|
||||
`blocked` boolean NOT NULL DEFAULT '0' COMMENT '1 for user is blocked',
|
||||
`blockwall` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to post to the profile page of the user',
|
||||
`hidewall` boolean NOT NULL DEFAULT '0' COMMENT 'Hide profile details from unkown viewers',
|
||||
`blocktags` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to tag the post of this user',
|
||||
`unkmail` boolean NOT NULL DEFAULT '0' COMMENT 'Permit unknown people to send private mails to this user',
|
||||
`cntunkmail` int unsigned NOT NULL DEFAULT 10 COMMENT '',
|
||||
`notify-flags` smallint unsigned NOT NULL DEFAULT 65535 COMMENT 'email notification options',
|
||||
`page-flags` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'page/profile type',
|
||||
`account-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`prvnets` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`pwdreset` varchar(255) COMMENT 'Password reset request token',
|
||||
`pwdreset_time` datetime COMMENT 'Timestamp of the last password reset request',
|
||||
`maxreq` int unsigned NOT NULL DEFAULT 10 COMMENT '',
|
||||
`expire` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`account_removed` boolean NOT NULL DEFAULT '0' COMMENT 'if 1 the account is removed',
|
||||
`account_expired` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`account_expires_on` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp when account expires and will be deleted',
|
||||
`expire_notification_sent` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last warning of account expiration',
|
||||
`def_gid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`allow_cid` mediumtext COMMENT 'default permission for this user',
|
||||
`allow_gid` mediumtext COMMENT 'default permission for this user',
|
||||
`deny_cid` mediumtext COMMENT 'default permission for this user',
|
||||
`deny_gid` mediumtext COMMENT 'default permission for this user',
|
||||
`openidserver` text COMMENT '',
|
||||
PRIMARY KEY(`uid`),
|
||||
INDEX `nickname` (`nickname`(32))
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='The local users';
|
||||
|
||||
--
|
||||
-- TABLE userd
|
||||
--
|
||||
|
|
@ -1344,7 +1431,10 @@ CREATE TABLE IF NOT EXISTS `user-contact` (
|
|||
`blocked` boolean COMMENT 'Contact is completely blocked for this user',
|
||||
`ignored` boolean COMMENT 'Posts from this contact are ignored',
|
||||
`collapsed` boolean COMMENT 'Posts from this contact are collapsed',
|
||||
PRIMARY KEY(`uid`,`cid`)
|
||||
PRIMARY KEY(`uid`,`cid`),
|
||||
INDEX `cid` (`cid`),
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific public contact data';
|
||||
|
||||
--
|
||||
|
|
@ -1359,7 +1449,9 @@ CREATE TABLE IF NOT EXISTS `user-item` (
|
|||
`notification-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
PRIMARY KEY(`uid`,`iid`),
|
||||
INDEX `uid_pinned` (`uid`,`pinned`),
|
||||
INDEX `iid_uid` (`iid`,`uid`)
|
||||
INDEX `iid_uid` (`iid`,`uid`),
|
||||
FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific item data';
|
||||
|
||||
--
|
||||
|
|
@ -1376,7 +1468,8 @@ CREATE TABLE IF NOT EXISTS `worker-ipc` (
|
|||
--
|
||||
CREATE TABLE IF NOT EXISTS `workerqueue` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'Auto incremented worker task id',
|
||||
`parameter` mediumtext COMMENT 'Task command',
|
||||
`command` varchar(100) COMMENT 'Task command',
|
||||
`parameter` mediumtext COMMENT 'Task parameter',
|
||||
`priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Task priority',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation date',
|
||||
`pid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Process id of the worker',
|
||||
|
|
@ -1385,23 +1478,16 @@ CREATE TABLE IF NOT EXISTS `workerqueue` (
|
|||
`retrial` tinyint NOT NULL DEFAULT 0 COMMENT 'Retrial counter',
|
||||
`done` boolean NOT NULL DEFAULT '0' COMMENT 'Marked 1 when the task was done - will be deleted later',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `done_parameter` (`done`,`parameter`(64)),
|
||||
INDEX `command` (`command`),
|
||||
INDEX `done_command_parameter` (`done`,`command`,`parameter`(64)),
|
||||
INDEX `done_executed` (`done`,`executed`),
|
||||
INDEX `done_priority_created` (`done`,`priority`,`created`),
|
||||
INDEX `done_priority_retrial_created` (`done`,`priority`,`retrial`,`created`),
|
||||
INDEX `done_priority_next_try` (`done`,`priority`,`next_try`),
|
||||
INDEX `done_pid_next_try` (`done`,`pid`,`next_try`),
|
||||
INDEX `done_pid_retrial` (`done`,`pid`,`retrial`),
|
||||
INDEX `done_pid_priority_created` (`done`,`pid`,`priority`,`created`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Background tasks queue entries';
|
||||
|
||||
--
|
||||
-- TABLE storage
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `storage` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'Auto incremented image data id',
|
||||
`data` longblob NOT NULL COMMENT 'file data',
|
||||
PRIMARY KEY(`id`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Data stored by Database storage backend';
|
||||
|
||||
--
|
||||
-- VIEW category-view
|
||||
--
|
||||
|
|
@ -1437,6 +1523,69 @@ CREATE VIEW `tag-view` AS SELECT
|
|||
LEFT JOIN `tag` ON `post-tag`.`tid` = `tag`.`id`
|
||||
LEFT JOIN `contact` ON `post-tag`.`cid` = `contact`.`id`;
|
||||
|
||||
--
|
||||
-- VIEW network-item-view
|
||||
--
|
||||
DROP VIEW IF EXISTS `network-item-view`;
|
||||
CREATE VIEW `network-item-view` AS SELECT
|
||||
`item`.`parent-uri-id` AS `uri-id`,
|
||||
`item`.`parent-uri` AS `uri`,
|
||||
`item`.`parent` AS `parent`,
|
||||
`item`.`received` AS `received`,
|
||||
`item`.`commented` AS `commented`,
|
||||
`item`.`created` AS `created`,
|
||||
`item`.`uid` AS `uid`,
|
||||
`item`.`starred` AS `starred`,
|
||||
`item`.`mention` AS `mention`,
|
||||
`item`.`network` AS `network`,
|
||||
`item`.`unseen` AS `unseen`,
|
||||
`item`.`gravity` AS `gravity`,
|
||||
`item`.`contact-id` AS `contact-id`,
|
||||
`ownercontact`.`contact-type` AS `contact-type`
|
||||
FROM `item`
|
||||
INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent`
|
||||
STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id`
|
||||
LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = `thread`.`uid`
|
||||
LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `thread`.`uid` AND `author`.`cid` = `thread`.`author-id`
|
||||
LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `thread`.`uid` AND `owner`.`cid` = `thread`.`owner-id`
|
||||
LEFT JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `thread`.`owner-id`
|
||||
WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated`
|
||||
AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`)
|
||||
AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
|
||||
AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`)
|
||||
AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`);
|
||||
|
||||
--
|
||||
-- VIEW network-thread-view
|
||||
--
|
||||
DROP VIEW IF EXISTS `network-thread-view`;
|
||||
CREATE VIEW `network-thread-view` AS SELECT
|
||||
`item`.`uri-id` AS `uri-id`,
|
||||
`item`.`uri` AS `uri`,
|
||||
`item`.`parent-uri-id` AS `parent-uri-id`,
|
||||
`thread`.`iid` AS `parent`,
|
||||
`thread`.`received` AS `received`,
|
||||
`thread`.`commented` AS `commented`,
|
||||
`thread`.`created` AS `created`,
|
||||
`thread`.`uid` AS `uid`,
|
||||
`thread`.`starred` AS `starred`,
|
||||
`thread`.`mention` AS `mention`,
|
||||
`thread`.`network` AS `network`,
|
||||
`thread`.`contact-id` AS `contact-id`,
|
||||
`ownercontact`.`contact-type` AS `contact-type`
|
||||
FROM `thread`
|
||||
STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id`
|
||||
STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid`
|
||||
LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = `thread`.`uid`
|
||||
LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `thread`.`uid` AND `author`.`cid` = `thread`.`author-id`
|
||||
LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `thread`.`uid` AND `owner`.`cid` = `thread`.`owner-id`
|
||||
LEFT JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `thread`.`owner-id`
|
||||
WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated`
|
||||
AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`)
|
||||
AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
|
||||
AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`)
|
||||
AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`);
|
||||
|
||||
--
|
||||
-- VIEW owner-view
|
||||
--
|
||||
|
|
@ -1494,18 +1643,18 @@ CREATE VIEW `owner-view` AS SELECT
|
|||
`contact`.`term-date` AS `term-date`,
|
||||
`contact`.`last-item` AS `last-item`,
|
||||
`contact`.`priority` AS `priority`,
|
||||
`contact`.`blocked` AS `blocked`,
|
||||
`user`.`blocked` AS `blocked`,
|
||||
`contact`.`block_reason` AS `block_reason`,
|
||||
`contact`.`readonly` AS `readonly`,
|
||||
`contact`.`writable` AS `writable`,
|
||||
`contact`.`forum` AS `forum`,
|
||||
`contact`.`prv` AS `prv`,
|
||||
`contact`.`contact-type` AS `contact-type`,
|
||||
`contact`.`manually-approve` AS `manually-approve`,
|
||||
`contact`.`hidden` AS `hidden`,
|
||||
`contact`.`archive` AS `archive`,
|
||||
`contact`.`pending` AS `pending`,
|
||||
`contact`.`deleted` AS `deleted`,
|
||||
`contact`.`rating` AS `rating`,
|
||||
`contact`.`unsearchable` AS `unsearchable`,
|
||||
`contact`.`sensitive` AS `sensitive`,
|
||||
`contact`.`baseurl` AS `baseurl`,
|
||||
|
|
@ -1517,7 +1666,7 @@ CREATE VIEW `owner-view` AS SELECT
|
|||
`contact`.`bd` AS `bd`,
|
||||
`contact`.`notify_new_posts` AS `notify_new_posts`,
|
||||
`contact`.`fetch_further_information` AS `fetch_further_information`,
|
||||
`contact`.`ffi_keyword_blacklist` AS `ffi_keyword_blacklist`,
|
||||
`contact`.`ffi_keyword_denylist` AS `ffi_keyword_denylist`,
|
||||
`user`.`parent-uid` AS `parent-uid`,
|
||||
`user`.`guid` AS `guid`,
|
||||
`user`.`nickname` AS `nickname`,
|
||||
|
|
@ -1625,5 +1774,3 @@ CREATE VIEW `workerqueue-view` AS SELECT
|
|||
FROM `process`
|
||||
INNER JOIN `workerqueue` ON `workerqueue`.`pid` = `process`.`pid`
|
||||
WHERE NOT `workerqueue`.`done`;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
286
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,35 +2039,40 @@ 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 (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']);
|
||||
|
||||
if (!empty($item['title'])) {
|
||||
$post .= '[h3]' . $item['title'] . "[/h3]\n";
|
||||
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER])) {
|
||||
if (!Item::performActivity($id, 'announce', local_user())) {
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
|
||||
$post .= $item['body'];
|
||||
$post .= "[/share]";
|
||||
$item_id = $id;
|
||||
} else {
|
||||
if (strpos($item['body'], "[/share]") !== false) {
|
||||
$pos = strpos($item['body'], "[share");
|
||||
$post = substr($item['body'], $pos);
|
||||
} else {
|
||||
$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";
|
||||
}
|
||||
|
||||
$post .= $item['body'];
|
||||
$post .= "[/share]";
|
||||
}
|
||||
$_REQUEST['body'] = $post;
|
||||
$_REQUEST['profile_uid'] = api_user();
|
||||
$_REQUEST['api_source'] = true;
|
||||
|
||||
if (empty($_REQUEST['source'])) {
|
||||
$_REQUEST["source"] = api_source();
|
||||
}
|
||||
|
||||
$item_id = item_post($a);
|
||||
}
|
||||
$_REQUEST['body'] = $post;
|
||||
$_REQUEST['attach'] = $item['attach'];
|
||||
$_REQUEST['profile_uid'] = api_user();
|
||||
$_REQUEST['api_source'] = true;
|
||||
|
||||
if (empty($_REQUEST['source'])) {
|
||||
$_REQUEST["source"] = api_source();
|
||||
}
|
||||
|
||||
$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,17 +383,17 @@ 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['search']) ? '&search=' . rawurlencode($_GET['search']) : '')
|
||||
. (!empty($_GET['star']) ? '&star=' . rawurlencode($_GET['star']) : '')
|
||||
. (!empty($_GET['order']) ? '&order=' . rawurlencode($_GET['order']) : '')
|
||||
. (!empty($_GET['bmark']) ? '&bmark=' . rawurlencode($_GET['bmark']) : '')
|
||||
. (!empty($_GET['liked']) ? '&liked=' . rawurlencode($_GET['liked']) : '')
|
||||
. (!empty($_GET['conv']) ? '&conv=' . rawurlencode($_GET['conv']) : '')
|
||||
. (!empty($_GET['nets']) ? '&nets=' . rawurlencode($_GET['nets']) : '')
|
||||
. (!empty($_GET['cmin']) ? '&cmin=' . rawurlencode($_GET['cmin']) : '')
|
||||
. (!empty($_GET['cmax']) ? '&cmax=' . rawurlencode($_GET['cmax']) : '')
|
||||
. (!empty($_GET['file']) ? '&file=' . rawurlencode($_GET['file']) : '')
|
||||
. (!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']) : '')
|
||||
. (!empty($_GET['bmark']) ? '&bmark=' . rawurlencode($_GET['bmark']) : '')
|
||||
. (!empty($_GET['liked']) ? '&liked=' . rawurlencode($_GET['liked']) : '')
|
||||
. (!empty($_GET['conv']) ? '&conv=' . rawurlencode($_GET['conv']) : '')
|
||||
. (!empty($_GET['nets']) ? '&nets=' . rawurlencode($_GET['nets']) : '')
|
||||
. (!empty($_GET['cmin']) ? '&cmin=' . rawurlencode($_GET['cmin']) : '')
|
||||
. (!empty($_GET['cmax']) ? '&cmax=' . rawurlencode($_GET['cmax']) : '')
|
||||
. (!empty($_GET['file']) ? '&file=' . rawurlencode($_GET['file']) : '')
|
||||
|
||||
. "'; </script>\r\n";
|
||||
}
|
||||
|
|
@ -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,
|
||||
'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,44 +710,88 @@ 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) {
|
||||
$row['pinned'] = $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) {
|
||||
$max_comments = DI::config()->get('system', 'max_comments', 100);
|
||||
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,19 +825,23 @@ 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`";
|
||||
}
|
||||
|
||||
$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);
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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) {
|
||||
|
|
@ -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 int $id = item id
|
||||
* @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,13 +259,23 @@ function notification($params)
|
|||
}
|
||||
|
||||
if ($params['type'] == Notify\Type::SHARE) {
|
||||
$subject = $l10n->t('%s %s shared a new post', $subjectPrefix, $params['source_name']);
|
||||
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);
|
||||
$epreamble = $l10n->t('%1$s [url=%2$s]shared a post[/url].',
|
||||
'[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
|
||||
$params['link']
|
||||
);
|
||||
$preamble = $l10n->t('%1$s shared a new post at %2$s', $params['source_name'], $sitename);
|
||||
$epreamble = $l10n->t('%1$s [url=%2$s]shared a post[/url].',
|
||||
'[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;
|
||||
}
|
||||
}
|
||||
1043
library/OAuth1.php
|
|
@ -1,1043 +0,0 @@
|
|||
<?php
|
||||
// vim: foldmethod=marker
|
||||
|
||||
/* Generic exception class
|
||||
*/
|
||||
|
||||
use Friendica\Network\FKOAuthDataStore;
|
||||
|
||||
if (!class_exists('OAuthException', false)) {
|
||||
class OAuthException extends Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthConsumer
|
||||
{
|
||||
public $key;
|
||||
public $secret;
|
||||
public $callback_url;
|
||||
|
||||
function __construct($key, $secret, $callback_url = NULL)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->secret = $secret;
|
||||
$this->callback_url = $callback_url;
|
||||
}
|
||||
|
||||
function __toString()
|
||||
{
|
||||
return "OAuthConsumer[key=$this->key,secret=$this->secret]";
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthToken
|
||||
{
|
||||
// access tokens and request tokens
|
||||
public $key;
|
||||
public $secret;
|
||||
|
||||
public $expires;
|
||||
public $scope;
|
||||
public $uid;
|
||||
|
||||
/**
|
||||
* key = the token
|
||||
* secret = the token secret
|
||||
*
|
||||
* @param $key
|
||||
* @param $secret
|
||||
*/
|
||||
function __construct($key, $secret)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* generates the basic string serialization of a token that a server
|
||||
* would respond to request_token and access_token calls with
|
||||
*/
|
||||
function to_string()
|
||||
{
|
||||
return "oauth_token=" .
|
||||
OAuthUtil::urlencode_rfc3986($this->key) .
|
||||
"&oauth_token_secret=" .
|
||||
OAuthUtil::urlencode_rfc3986($this->secret);
|
||||
}
|
||||
|
||||
function __toString()
|
||||
{
|
||||
return $this->to_string();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for implementing a Signature Method
|
||||
* See section 9 ("Signing Requests") in the spec
|
||||
*/
|
||||
abstract class OAuthSignatureMethod
|
||||
{
|
||||
/**
|
||||
* Needs to return the name of the Signature Method (ie HMAC-SHA1)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_name();
|
||||
|
||||
/**
|
||||
* Build up the signature
|
||||
* NOTE: The output of this function MUST NOT be urlencoded.
|
||||
* the encoding is handled in OAuthRequest when the final
|
||||
* request is serialized
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @param OAuthConsumer $consumer
|
||||
* @param OAuthToken $token
|
||||
* @return string
|
||||
*/
|
||||
abstract public function build_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token = null);
|
||||
|
||||
/**
|
||||
* Verifies that a given signature is correct
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @param OAuthConsumer $consumer
|
||||
* @param OAuthToken $token
|
||||
* @param string $signature
|
||||
* @return bool
|
||||
*/
|
||||
public function check_signature(OAuthRequest $request, OAuthConsumer $consumer, $signature, OAuthToken $token = null)
|
||||
{
|
||||
$built = $this->build_signature($request, $consumer, $token);
|
||||
return ($built == $signature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
|
||||
* where the Signature Base String is the text and the key is the concatenated values (each first
|
||||
* encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
|
||||
* character (ASCII code 38) even if empty.
|
||||
* - Chapter 9.2 ("HMAC-SHA1")
|
||||
*/
|
||||
class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod
|
||||
{
|
||||
function get_name()
|
||||
{
|
||||
return "HMAC-SHA1";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OAuthRequest $request
|
||||
* @param OAuthConsumer $consumer
|
||||
* @param OAuthToken $token
|
||||
* @return string
|
||||
*/
|
||||
public function build_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token = null)
|
||||
{
|
||||
$base_string = $request->get_signature_base_string();
|
||||
$request->base_string = $base_string;
|
||||
|
||||
$key_parts = array(
|
||||
$consumer->secret,
|
||||
($token) ? $token->secret : ""
|
||||
);
|
||||
|
||||
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
|
||||
$key = implode('&', $key_parts);
|
||||
|
||||
|
||||
$r = base64_encode(hash_hmac('sha1', $base_string, $key, true));
|
||||
return $r;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The PLAINTEXT method does not provide any security protection and SHOULD only be used
|
||||
* over a secure channel such as HTTPS. It does not use the Signature Base String.
|
||||
* - Chapter 9.4 ("PLAINTEXT")
|
||||
*/
|
||||
class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod
|
||||
{
|
||||
public function get_name()
|
||||
{
|
||||
return "PLAINTEXT";
|
||||
}
|
||||
|
||||
/**
|
||||
* oauth_signature is set to the concatenated encoded values of the Consumer Secret and
|
||||
* Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
|
||||
* empty. The result MUST be encoded again.
|
||||
* - Chapter 9.4.1 ("Generating Signatures")
|
||||
*
|
||||
* Please note that the second encoding MUST NOT happen in the SignatureMethod, as
|
||||
* OAuthRequest handles this!
|
||||
*
|
||||
* @param $request
|
||||
* @param $consumer
|
||||
* @param $token
|
||||
* @return string
|
||||
*/
|
||||
public function build_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token = null)
|
||||
{
|
||||
$key_parts = array(
|
||||
$consumer->secret,
|
||||
($token) ? $token->secret : ""
|
||||
);
|
||||
|
||||
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
|
||||
$key = implode('&', $key_parts);
|
||||
$request->base_string = $key;
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
|
||||
* [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
|
||||
* EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
|
||||
* verified way to the Service Provider, in a manner which is beyond the scope of this
|
||||
* specification.
|
||||
* - Chapter 9.3 ("RSA-SHA1")
|
||||
*/
|
||||
abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod
|
||||
{
|
||||
public function get_name()
|
||||
{
|
||||
return "RSA-SHA1";
|
||||
}
|
||||
|
||||
// Up to the SP to implement this lookup of keys. Possible ideas are:
|
||||
// (1) do a lookup in a table of trusted certs keyed off of consumer
|
||||
// (2) fetch via http using a url provided by the requester
|
||||
// (3) some sort of specific discovery code based on request
|
||||
//
|
||||
// Either way should return a string representation of the certificate
|
||||
protected abstract function fetch_public_cert(&$request);
|
||||
|
||||
// Up to the SP to implement this lookup of keys. Possible ideas are:
|
||||
// (1) do a lookup in a table of trusted certs keyed off of consumer
|
||||
//
|
||||
// Either way should return a string representation of the certificate
|
||||
protected abstract function fetch_private_cert(&$request);
|
||||
|
||||
public function build_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token = null)
|
||||
{
|
||||
$base_string = $request->get_signature_base_string();
|
||||
$request->base_string = $base_string;
|
||||
|
||||
// Fetch the private key cert based on the request
|
||||
$cert = $this->fetch_private_cert($request);
|
||||
|
||||
// Pull the private key ID from the certificate
|
||||
$privatekeyid = openssl_get_privatekey($cert);
|
||||
|
||||
// Sign using the key
|
||||
openssl_sign($base_string, $signature, $privatekeyid);
|
||||
|
||||
// Release the key resource
|
||||
openssl_free_key($privatekeyid);
|
||||
|
||||
return base64_encode($signature);
|
||||
}
|
||||
|
||||
public function check_signature(OAuthRequest $request, OAuthConsumer $consumer, $signature, OAuthToken $token = null)
|
||||
{
|
||||
$decoded_sig = base64_decode($signature);
|
||||
|
||||
$base_string = $request->get_signature_base_string();
|
||||
|
||||
// Fetch the public key cert based on the request
|
||||
$cert = $this->fetch_public_cert($request);
|
||||
|
||||
// Pull the public key ID from the certificate
|
||||
$publickeyid = openssl_get_publickey($cert);
|
||||
|
||||
// Check the computed signature against the one passed in the query
|
||||
$ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
|
||||
|
||||
// Release the key resource
|
||||
openssl_free_key($publickeyid);
|
||||
|
||||
return $ok == 1;
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthRequest
|
||||
{
|
||||
private $parameters;
|
||||
private $http_method;
|
||||
private $http_url;
|
||||
// for debug purposes
|
||||
public $base_string;
|
||||
public static $version = '1.0';
|
||||
public static $POST_INPUT = 'php://input';
|
||||
|
||||
function __construct($http_method, $http_url, $parameters = NULL)
|
||||
{
|
||||
@$parameters or $parameters = array();
|
||||
$parameters = array_merge(OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
|
||||
$this->parameters = $parameters;
|
||||
$this->http_method = $http_method;
|
||||
$this->http_url = $http_url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* attempt to build up a request from what was passed to the server
|
||||
*
|
||||
* @param string|null $http_method
|
||||
* @param string|null $http_url
|
||||
* @param string|null $parameters
|
||||
* @return OAuthRequest
|
||||
*/
|
||||
public static function from_request($http_method = NULL, $http_url = NULL, $parameters = NULL)
|
||||
{
|
||||
$scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
|
||||
? 'http'
|
||||
: 'https';
|
||||
@$http_url or $http_url = $scheme .
|
||||
'://' . $_SERVER['HTTP_HOST'] .
|
||||
':' .
|
||||
$_SERVER['SERVER_PORT'] .
|
||||
$_SERVER['REQUEST_URI'];
|
||||
@$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// We weren't handed any parameters, so let's find the ones relevant to
|
||||
// this request.
|
||||
// If you run XML-RPC or similar you should use this to provide your own
|
||||
// parsed parameter-list
|
||||
if (!$parameters) {
|
||||
// Find request headers
|
||||
$request_headers = OAuthUtil::get_headers();
|
||||
|
||||
// Parse the query-string to find GET parameters
|
||||
$parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
|
||||
|
||||
// It's a POST request of the proper content-type, so parse POST
|
||||
// parameters and add those overriding any duplicates from GET
|
||||
if (
|
||||
$http_method == "POST"
|
||||
&& @strstr(
|
||||
$request_headers["Content-Type"],
|
||||
"application/x-www-form-urlencoded"
|
||||
)
|
||||
) {
|
||||
$post_data = OAuthUtil::parse_parameters(
|
||||
file_get_contents(self::$POST_INPUT)
|
||||
);
|
||||
$parameters = array_merge($parameters, $post_data);
|
||||
}
|
||||
|
||||
// We have a Authorization-header with OAuth data. Parse the header
|
||||
// and add those overriding any duplicates from GET or POST
|
||||
if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
|
||||
$header_parameters = OAuthUtil::split_header(
|
||||
$request_headers['Authorization']
|
||||
);
|
||||
$parameters = array_merge($parameters, $header_parameters);
|
||||
}
|
||||
}
|
||||
// fix for friendica redirect system
|
||||
|
||||
$http_url = substr($http_url, 0, strpos($http_url, $parameters['pagename']) + strlen($parameters['pagename']));
|
||||
unset($parameters['pagename']);
|
||||
|
||||
return new OAuthRequest($http_method, $http_url, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* pretty much a helper function to set up the request
|
||||
*
|
||||
* @param OAuthConsumer $consumer
|
||||
* @param OAuthToken $token
|
||||
* @param string $http_method
|
||||
* @param string $http_url
|
||||
* @param array|null $parameters
|
||||
* @return OAuthRequest
|
||||
*/
|
||||
public static function from_consumer_and_token(OAuthConsumer $consumer, $http_method, $http_url, array $parameters = null, OAuthToken $token = null)
|
||||
{
|
||||
@$parameters or $parameters = array();
|
||||
$defaults = array(
|
||||
"oauth_version" => OAuthRequest::$version,
|
||||
"oauth_nonce" => OAuthRequest::generate_nonce(),
|
||||
"oauth_timestamp" => OAuthRequest::generate_timestamp(),
|
||||
"oauth_consumer_key" => $consumer->key
|
||||
);
|
||||
if ($token)
|
||||
$defaults['oauth_token'] = $token->key;
|
||||
|
||||
$parameters = array_merge($defaults, $parameters);
|
||||
|
||||
return new OAuthRequest($http_method, $http_url, $parameters);
|
||||
}
|
||||
|
||||
public function set_parameter($name, $value, $allow_duplicates = true)
|
||||
{
|
||||
if ($allow_duplicates && isset($this->parameters[$name])) {
|
||||
// We have already added parameter(s) with this name, so add to the list
|
||||
if (is_scalar($this->parameters[$name])) {
|
||||
// This is the first duplicate, so transform scalar (string)
|
||||
// into an array so we can add the duplicates
|
||||
$this->parameters[$name] = array($this->parameters[$name]);
|
||||
}
|
||||
|
||||
$this->parameters[$name][] = $value;
|
||||
} else {
|
||||
$this->parameters[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function get_parameter($name)
|
||||
{
|
||||
return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
|
||||
}
|
||||
|
||||
public function get_parameters()
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function unset_parameter($name)
|
||||
{
|
||||
unset($this->parameters[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The request parameters, sorted and concatenated into a normalized string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_signable_parameters()
|
||||
{
|
||||
// Grab all parameters
|
||||
$params = $this->parameters;
|
||||
|
||||
// Remove oauth_signature if present
|
||||
// Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
|
||||
if (isset($params['oauth_signature'])) {
|
||||
unset($params['oauth_signature']);
|
||||
}
|
||||
|
||||
return OAuthUtil::build_http_query($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base string of this request
|
||||
*
|
||||
* The base string defined as the method, the url
|
||||
* and the parameters (normalized), each urlencoded
|
||||
* and the concated with &.
|
||||
*/
|
||||
public function get_signature_base_string()
|
||||
{
|
||||
$parts = array(
|
||||
$this->get_normalized_http_method(),
|
||||
$this->get_normalized_http_url(),
|
||||
$this->get_signable_parameters()
|
||||
);
|
||||
|
||||
$parts = OAuthUtil::urlencode_rfc3986($parts);
|
||||
|
||||
return implode('&', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* just uppercases the http method
|
||||
*/
|
||||
public function get_normalized_http_method()
|
||||
{
|
||||
return strtoupper($this->http_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* parses the url and rebuilds it to be
|
||||
* scheme://host/path
|
||||
*/
|
||||
public function get_normalized_http_url()
|
||||
{
|
||||
$parts = parse_url($this->http_url);
|
||||
|
||||
$port = @$parts['port'];
|
||||
$scheme = $parts['scheme'];
|
||||
$host = $parts['host'];
|
||||
$path = @$parts['path'];
|
||||
|
||||
$port or $port = ($scheme == 'https') ? '443' : '80';
|
||||
|
||||
if (($scheme == 'https' && $port != '443')
|
||||
|| ($scheme == 'http' && $port != '80')
|
||||
) {
|
||||
$host = "$host:$port";
|
||||
}
|
||||
return "$scheme://$host$path";
|
||||
}
|
||||
|
||||
/**
|
||||
* builds a url usable for a GET request
|
||||
*/
|
||||
public function to_url()
|
||||
{
|
||||
$post_data = $this->to_postdata();
|
||||
$out = $this->get_normalized_http_url();
|
||||
if ($post_data) {
|
||||
$out .= '?' . $post_data;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* builds the data one would send in a POST request
|
||||
*
|
||||
* @param bool $raw
|
||||
* @return array|string
|
||||
*/
|
||||
public function to_postdata(bool $raw = false)
|
||||
{
|
||||
if ($raw)
|
||||
return $this->parameters;
|
||||
else
|
||||
return OAuthUtil::build_http_query($this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* builds the Authorization: header
|
||||
*
|
||||
* @param string|null $realm
|
||||
* @return string
|
||||
* @throws OAuthException
|
||||
*/
|
||||
public function to_header($realm = null)
|
||||
{
|
||||
$first = true;
|
||||
if ($realm) {
|
||||
$out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
|
||||
$first = false;
|
||||
} else
|
||||
$out = 'Authorization: OAuth';
|
||||
|
||||
foreach ($this->parameters as $k => $v) {
|
||||
if (substr($k, 0, 5) != "oauth") continue;
|
||||
if (is_array($v)) {
|
||||
throw new OAuthException('Arrays not supported in headers');
|
||||
}
|
||||
$out .= ($first) ? ' ' : ',';
|
||||
$out .= OAuthUtil::urlencode_rfc3986($k) .
|
||||
'="' .
|
||||
OAuthUtil::urlencode_rfc3986($v) .
|
||||
'"';
|
||||
$first = false;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->to_url();
|
||||
}
|
||||
|
||||
|
||||
public function sign_request(OAuthSignatureMethod $signature_method, $consumer, $token)
|
||||
{
|
||||
$this->set_parameter(
|
||||
"oauth_signature_method",
|
||||
$signature_method->get_name(),
|
||||
false
|
||||
);
|
||||
$signature = $this->build_signature($signature_method, $consumer, $token);
|
||||
$this->set_parameter("oauth_signature", $signature, false);
|
||||
}
|
||||
|
||||
public function build_signature(OAuthSignatureMethod $signature_method, $consumer, $token)
|
||||
{
|
||||
$signature = $signature_method->build_signature($this, $consumer, $token);
|
||||
return $signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* util function: current timestamp
|
||||
*/
|
||||
private static function generate_timestamp()
|
||||
{
|
||||
return time();
|
||||
}
|
||||
|
||||
/**
|
||||
* util function: current nonce
|
||||
*/
|
||||
private static function generate_nonce()
|
||||
{
|
||||
return Friendica\Util\Strings::getRandomHex(32);
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthServer
|
||||
{
|
||||
protected $timestamp_threshold = 300; // in seconds, five minutes
|
||||
protected $version = '1.0'; // hi blaine
|
||||
/** @var OAuthSignatureMethod[] */
|
||||
protected $signature_methods = array();
|
||||
|
||||
/** @var FKOAuthDataStore */
|
||||
protected $data_store;
|
||||
|
||||
function __construct(FKOAuthDataStore $data_store)
|
||||
{
|
||||
$this->data_store = $data_store;
|
||||
}
|
||||
|
||||
public function add_signature_method(OAuthSignatureMethod $signature_method)
|
||||
{
|
||||
$this->signature_methods[$signature_method->get_name()] =
|
||||
$signature_method;
|
||||
}
|
||||
|
||||
// high level functions
|
||||
|
||||
/**
|
||||
* process a request_token request
|
||||
* returns the request token on success
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @return OAuthToken|null
|
||||
* @throws OAuthException
|
||||
*/
|
||||
public function fetch_request_token(OAuthRequest $request)
|
||||
{
|
||||
$this->get_version($request);
|
||||
|
||||
$consumer = $this->get_consumer($request);
|
||||
|
||||
// no token required for the initial token request
|
||||
$token = NULL;
|
||||
|
||||
$this->check_signature($request, $consumer, $token);
|
||||
|
||||
// Rev A change
|
||||
$callback = $request->get_parameter('oauth_callback');
|
||||
$new_token = $this->data_store->new_request_token($consumer, $callback);
|
||||
|
||||
return $new_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* process an access_token request
|
||||
* returns the access token on success
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @return object
|
||||
* @throws OAuthException
|
||||
*/
|
||||
public function fetch_access_token(OAuthRequest $request)
|
||||
{
|
||||
$this->get_version($request);
|
||||
|
||||
$consumer = $this->get_consumer($request);
|
||||
|
||||
// requires authorized request token
|
||||
$token = $this->get_token($request, $consumer, "request");
|
||||
|
||||
$this->check_signature($request, $consumer, $token);
|
||||
|
||||
// Rev A change
|
||||
$verifier = $request->get_parameter('oauth_verifier');
|
||||
$new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
|
||||
|
||||
return $new_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* verify an api call, checks all the parameters
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @return array
|
||||
* @throws OAuthException
|
||||
*/
|
||||
public function verify_request(OAuthRequest $request)
|
||||
{
|
||||
$this->get_version($request);
|
||||
$consumer = $this->get_consumer($request);
|
||||
$token = $this->get_token($request, $consumer, "access");
|
||||
$this->check_signature($request, $consumer, $token);
|
||||
return [$consumer, $token];
|
||||
}
|
||||
|
||||
// Internals from here
|
||||
|
||||
/**
|
||||
* version 1
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @return string
|
||||
* @throws OAuthException
|
||||
*/
|
||||
private function get_version(OAuthRequest $request)
|
||||
{
|
||||
$version = $request->get_parameter("oauth_version");
|
||||
if (!$version) {
|
||||
// Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
|
||||
// Chapter 7.0 ("Accessing Protected Ressources")
|
||||
$version = '1.0';
|
||||
}
|
||||
if ($version !== $this->version) {
|
||||
throw new OAuthException("OAuth version '$version' not supported");
|
||||
}
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* figure out the signature with some defaults
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @return OAuthSignatureMethod
|
||||
* @throws OAuthException
|
||||
*/
|
||||
private function get_signature_method(OAuthRequest $request)
|
||||
{
|
||||
$signature_method =
|
||||
@$request->get_parameter("oauth_signature_method");
|
||||
|
||||
if (!$signature_method) {
|
||||
// According to chapter 7 ("Accessing Protected Ressources") the signature-method
|
||||
// parameter is required, and we can't just fallback to PLAINTEXT
|
||||
throw new OAuthException('No signature method parameter. This parameter is required');
|
||||
}
|
||||
|
||||
if (!in_array(
|
||||
$signature_method,
|
||||
array_keys($this->signature_methods)
|
||||
)) {
|
||||
throw new OAuthException(
|
||||
"Signature method '$signature_method' not supported " .
|
||||
"try one of the following: " .
|
||||
implode(", ", array_keys($this->signature_methods))
|
||||
);
|
||||
}
|
||||
return $this->signature_methods[$signature_method];
|
||||
}
|
||||
|
||||
/**
|
||||
* try to find the consumer for the provided request's consumer key
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @return OAuthConsumer
|
||||
* @throws OAuthException
|
||||
*/
|
||||
private function get_consumer(OAuthRequest $request)
|
||||
{
|
||||
$consumer_key = @$request->get_parameter("oauth_consumer_key");
|
||||
if (!$consumer_key) {
|
||||
throw new OAuthException("Invalid consumer key");
|
||||
}
|
||||
|
||||
$consumer = $this->data_store->lookup_consumer($consumer_key);
|
||||
if (!$consumer) {
|
||||
throw new OAuthException("Invalid consumer");
|
||||
}
|
||||
|
||||
return $consumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* try to find the token for the provided request's token key
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @param $consumer
|
||||
* @param string $token_type
|
||||
* @return OAuthToken|null
|
||||
* @throws OAuthException
|
||||
*/
|
||||
private function get_token(OAuthRequest &$request, $consumer, $token_type = "access")
|
||||
{
|
||||
$token_field = @$request->get_parameter('oauth_token');
|
||||
$token = $this->data_store->lookup_token(
|
||||
$consumer,
|
||||
$token_type,
|
||||
$token_field
|
||||
);
|
||||
if (!$token) {
|
||||
throw new OAuthException("Invalid $token_type token: $token_field");
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* all-in-one function to check the signature on a request
|
||||
* should guess the signature method appropriately
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @param OAuthConsumer $consumer
|
||||
* @param OAuthToken|null $token
|
||||
* @throws OAuthException
|
||||
*/
|
||||
private function check_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token = null)
|
||||
{
|
||||
// this should probably be in a different method
|
||||
$timestamp = @$request->get_parameter('oauth_timestamp');
|
||||
$nonce = @$request->get_parameter('oauth_nonce');
|
||||
|
||||
$this->check_timestamp($timestamp);
|
||||
$this->check_nonce($consumer, $token, $nonce, $timestamp);
|
||||
|
||||
$signature_method = $this->get_signature_method($request);
|
||||
|
||||
$signature = $request->get_parameter('oauth_signature');
|
||||
$valid_sig = $signature_method->check_signature(
|
||||
$request,
|
||||
$consumer,
|
||||
$signature,
|
||||
$token
|
||||
);
|
||||
|
||||
if (!$valid_sig) {
|
||||
throw new OAuthException("Invalid signature");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check that the timestamp is new enough
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @throws OAuthException
|
||||
*/
|
||||
private function check_timestamp($timestamp)
|
||||
{
|
||||
if (!$timestamp)
|
||||
throw new OAuthException(
|
||||
'Missing timestamp parameter. The parameter is required'
|
||||
);
|
||||
|
||||
// verify that timestamp is recentish
|
||||
$now = time();
|
||||
if (abs($now - $timestamp) > $this->timestamp_threshold) {
|
||||
throw new OAuthException(
|
||||
"Expired timestamp, yours $timestamp, ours $now"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check that the nonce is not repeated
|
||||
*
|
||||
* @param OAuthConsumer $consumer
|
||||
* @param OAuthToken $token
|
||||
* @param string $nonce
|
||||
* @param int $timestamp
|
||||
* @throws OAuthException
|
||||
*/
|
||||
private function check_nonce(OAuthConsumer $consumer, OAuthToken $token, $nonce, int $timestamp)
|
||||
{
|
||||
if (!$nonce)
|
||||
throw new OAuthException(
|
||||
'Missing nonce parameter. The parameter is required'
|
||||
);
|
||||
|
||||
// verify that the nonce is uniqueish
|
||||
$found = $this->data_store->lookup_nonce(
|
||||
$consumer,
|
||||
$token,
|
||||
$nonce,
|
||||
$timestamp
|
||||
);
|
||||
if ($found) {
|
||||
throw new OAuthException("Nonce already used: $nonce");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthDataStore
|
||||
{
|
||||
function lookup_consumer($consumer_key)
|
||||
{
|
||||
// implement me
|
||||
}
|
||||
|
||||
function lookup_token(OAuthConsumer $consumer, $token_type, $token_id)
|
||||
{
|
||||
// implement me
|
||||
}
|
||||
|
||||
function lookup_nonce(OAuthConsumer $consumer, OAuthToken $token, $nonce, int $timestamp)
|
||||
{
|
||||
// implement me
|
||||
}
|
||||
|
||||
function new_request_token(OAuthConsumer $consumer, $callback = null)
|
||||
{
|
||||
// return a new token attached to this consumer
|
||||
}
|
||||
|
||||
function new_access_token(OAuthToken $token, OAuthConsumer $consumer, $verifier = null)
|
||||
{
|
||||
// return a new access token attached to this consumer
|
||||
// for the user associated with this token if the request token
|
||||
// is authorized
|
||||
// should also invalidate the request token
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthUtil
|
||||
{
|
||||
public static function urlencode_rfc3986($input)
|
||||
{
|
||||
if (is_array($input)) {
|
||||
return array_map(['OAuthUtil', 'urlencode_rfc3986'], $input);
|
||||
} else if (is_scalar($input)) {
|
||||
return str_replace(
|
||||
'+',
|
||||
' ',
|
||||
str_replace('%7E', '~', rawurlencode($input))
|
||||
);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This decode function isn't taking into consideration the above
|
||||
// modifications to the encoding process. However, this method doesn't
|
||||
// seem to be used anywhere so leaving it as is.
|
||||
public static function urldecode_rfc3986($string)
|
||||
{
|
||||
return urldecode($string);
|
||||
}
|
||||
|
||||
// Utility function for turning the Authorization: header into
|
||||
// parameters, has to do some unescaping
|
||||
// Can filter out any non-oauth parameters if needed (default behaviour)
|
||||
public static function split_header($header, $only_allow_oauth_parameters = true)
|
||||
{
|
||||
$pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
|
||||
$offset = 0;
|
||||
$params = [];
|
||||
while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
|
||||
$match = $matches[0];
|
||||
$header_name = $matches[2][0];
|
||||
$header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
|
||||
if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) {
|
||||
$params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content);
|
||||
}
|
||||
$offset = $match[1] + strlen($match[0]);
|
||||
}
|
||||
|
||||
if (isset($params['realm'])) {
|
||||
unset($params['realm']);
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
// helper to try to sort out headers for people who aren't running apache
|
||||
public static function get_headers()
|
||||
{
|
||||
if (function_exists('apache_request_headers')) {
|
||||
// we need this to get the actual Authorization: header
|
||||
// because apache tends to tell us it doesn't exist
|
||||
$headers = apache_request_headers();
|
||||
|
||||
// sanitize the output of apache_request_headers because
|
||||
// we always want the keys to be Cased-Like-This and arh()
|
||||
// returns the headers in the same case as they are in the
|
||||
// request
|
||||
$out = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$key = str_replace(
|
||||
" ",
|
||||
"-",
|
||||
ucwords(strtolower(str_replace("-", " ", $key)))
|
||||
);
|
||||
$out[$key] = $value;
|
||||
}
|
||||
} else {
|
||||
// otherwise we don't have apache and are just going to have to hope
|
||||
// that $_SERVER actually contains what we need
|
||||
$out = [];
|
||||
if (isset($_SERVER['CONTENT_TYPE']))
|
||||
$out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
|
||||
if (isset($_ENV['CONTENT_TYPE']))
|
||||
$out['Content-Type'] = $_ENV['CONTENT_TYPE'];
|
||||
|
||||
foreach ($_SERVER as $key => $value) {
|
||||
if (substr($key, 0, 5) == "HTTP_") {
|
||||
// this is chaos, basically it is just there to capitalize the first
|
||||
// letter of every word that is not an initial HTTP and strip HTTP
|
||||
// code from przemek
|
||||
$key = str_replace(
|
||||
" ",
|
||||
"-",
|
||||
ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
|
||||
);
|
||||
$out[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
// This function takes a input like a=b&a=c&d=e and returns the parsed
|
||||
// parameters like this
|
||||
// array('a' => array('b','c'), 'd' => 'e')
|
||||
public static function parse_parameters($input)
|
||||
{
|
||||
if (!isset($input) || !$input) return array();
|
||||
|
||||
$pairs = explode('&', $input);
|
||||
|
||||
$parsed_parameters = [];
|
||||
foreach ($pairs as $pair) {
|
||||
$split = explode('=', $pair, 2);
|
||||
$parameter = OAuthUtil::urldecode_rfc3986($split[0]);
|
||||
$value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
|
||||
|
||||
if (isset($parsed_parameters[$parameter])) {
|
||||
// We have already recieved parameter(s) with this name, so add to the list
|
||||
// of parameters with this name
|
||||
|
||||
if (is_scalar($parsed_parameters[$parameter])) {
|
||||
// This is the first duplicate, so transform scalar (string) into an array
|
||||
// so we can add the duplicates
|
||||
$parsed_parameters[$parameter] = [$parsed_parameters[$parameter]];
|
||||
}
|
||||
|
||||
$parsed_parameters[$parameter][] = $value;
|
||||
} else {
|
||||
$parsed_parameters[$parameter] = $value;
|
||||
}
|
||||
}
|
||||
return $parsed_parameters;
|
||||
}
|
||||
|
||||
public static function build_http_query($params)
|
||||
{
|
||||
if (!$params) return '';
|
||||
|
||||
// Urlencode both keys and values
|
||||
$keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
|
||||
$values = OAuthUtil::urlencode_rfc3986(array_values($params));
|
||||
$params = array_combine($keys, $values);
|
||||
|
||||
// Parameters are sorted by name, using lexicographical byte value ordering.
|
||||
// Ref: Spec: 9.1.1 (1)
|
||||
uksort($params, 'strcmp');
|
||||
|
||||
$pairs = [];
|
||||
foreach ($params as $parameter => $value) {
|
||||
if (is_array($value)) {
|
||||
// If two or more parameters share the same name, they are sorted by their value
|
||||
// Ref: Spec: 9.1.1 (1)
|
||||
natsort($value);
|
||||
foreach ($value as $duplicate_value) {
|
||||
$pairs[] = $parameter . '=' . $duplicate_value;
|
||||
}
|
||||
} else {
|
||||
$pairs[] = $parameter . '=' . $value;
|
||||
}
|
||||
}
|
||||
// For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
|
||||
// Each name-value pair is separated by an '&' character (ASCII code 38)
|
||||
return implode('&', $pairs);
|
||||
}
|
||||
}
|
||||
292
library/asn1.php
|
|
@ -1,292 +0,0 @@
|
|||
<?php
|
||||
|
||||
// ASN.1 parsing library
|
||||
// Attribution: http://www.krisbailey.com
|
||||
// license: unknown
|
||||
// modified: Mike Macgrivin mike@macgirvin.com 6-oct-2010 to support Salmon auto-discovery
|
||||
// modified: Tobias Diekershoff 28-jul-2016 adding an intval in line 162 to make PHP7 happy
|
||||
// from openssl public keys
|
||||
|
||||
|
||||
class ASN_BASE {
|
||||
public $asnData = null;
|
||||
private $cursor = 0;
|
||||
private $parent = null;
|
||||
|
||||
public static $ASN_MARKERS = array(
|
||||
'ASN_UNIVERSAL' => 0x00,
|
||||
'ASN_APPLICATION' => 0x40,
|
||||
'ASN_CONTEXT' => 0x80,
|
||||
'ASN_PRIVATE' => 0xC0,
|
||||
|
||||
'ASN_PRIMITIVE' => 0x00,
|
||||
'ASN_CONSTRUCTOR' => 0x20,
|
||||
|
||||
'ASN_LONG_LEN' => 0x80,
|
||||
'ASN_EXTENSION_ID' => 0x1F,
|
||||
'ASN_BIT' => 0x80,
|
||||
);
|
||||
|
||||
public static $ASN_TYPES = array(
|
||||
1 => 'ASN_BOOLEAN',
|
||||
2 => 'ASN_INTEGER',
|
||||
3 => 'ASN_BIT_STR',
|
||||
4 => 'ASN_OCTET_STR',
|
||||
5 => 'ASN_NULL',
|
||||
6 => 'ASN_OBJECT_ID',
|
||||
9 => 'ASN_REAL',
|
||||
10 => 'ASN_ENUMERATED',
|
||||
13 => 'ASN_RELATIVE_OID',
|
||||
48 => 'ASN_SEQUENCE',
|
||||
49 => 'ASN_SET',
|
||||
19 => 'ASN_PRINT_STR',
|
||||
22 => 'ASN_IA5_STR',
|
||||
23 => 'ASN_UTC_TIME',
|
||||
24 => 'ASN_GENERAL_TIME',
|
||||
);
|
||||
|
||||
function __construct($v = false)
|
||||
{
|
||||
if (false !== $v) {
|
||||
$this->asnData = $v;
|
||||
if (is_array($this->asnData)) {
|
||||
foreach ($this->asnData as $key => $value) {
|
||||
if (is_object($value)) {
|
||||
$this->asnData[$key]->setParent($this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_object($this->asnData)) {
|
||||
$this->asnData->setParent($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setParent($parent)
|
||||
{
|
||||
if (false !== $parent) {
|
||||
$this->parent = $parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will take the markers and types arrays and
|
||||
* dynamically generate classes that extend this class for each one,
|
||||
* and also define constants for them.
|
||||
*/
|
||||
public static function generateSubclasses()
|
||||
{
|
||||
define('ASN_BASE', 0);
|
||||
foreach (self::$ASN_MARKERS as $name => $bit)
|
||||
self::makeSubclass($name, $bit);
|
||||
foreach (self::$ASN_TYPES as $bit => $name)
|
||||
self::makeSubclass($name, $bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for generateSubclasses()
|
||||
*/
|
||||
public static function makeSubclass($name, $bit)
|
||||
{
|
||||
define($name, $bit);
|
||||
eval("class ".$name." extends ASN_BASE {}");
|
||||
}
|
||||
|
||||
/**
|
||||
* This function reset's the internal cursor used for value iteration.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->cursor = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function catches calls to get the value for the type, typeName, value, values, and data
|
||||
* from the object. For type calls we just return the class name or the value of the constant that
|
||||
* is named the same as the class.
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if ('type' == $name) {
|
||||
// int flag of the data type
|
||||
return constant(get_class($this));
|
||||
} elseif ('typeName' == $name) {
|
||||
// name of the data type
|
||||
return get_class($this);
|
||||
} elseif ('value' == $name) {
|
||||
// will always return one value and can be iterated over with:
|
||||
// while ($v = $obj->value) { ...
|
||||
// because $this->asnData["invalid key"] will return false
|
||||
return is_array($this->asnData) ? $this->asnData[$this->cursor++] : $this->asnData;
|
||||
} elseif ('values' == $name) {
|
||||
// will always return an array
|
||||
return is_array($this->asnData) ? $this->asnData : array($this->asnData);
|
||||
} elseif ('data' == $name) {
|
||||
// will always return the raw data
|
||||
return $this->asnData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ASN.1 binary string.
|
||||
*
|
||||
* This function takes a binary ASN.1 string and parses it into it's respective
|
||||
* pieces and returns it. It can optionally stop at any depth.
|
||||
*
|
||||
* @param string $string The binary ASN.1 String
|
||||
* @param int $level The current parsing depth level
|
||||
* @param int $maxLevel The max parsing depth level
|
||||
* @return ASN_BASE The array representation of the ASN.1 data contained in $string
|
||||
*/
|
||||
public static function parseASNString($string=false, $level=1, $maxLevels=false){
|
||||
if (!class_exists('ASN_UNIVERSAL'))
|
||||
self::generateSubclasses();
|
||||
if ($level>$maxLevels && $maxLevels)
|
||||
return array(new ASN_BASE($string));
|
||||
$parsed = array();
|
||||
$endLength = strlen($string);
|
||||
$bigLength = $length = $type = $dtype = $p = 0;
|
||||
while ($p<$endLength){
|
||||
$type = ord($string[$p++]);
|
||||
$dtype = ($type & 192) >> 6;
|
||||
if ($type==0){ // if we are type 0, just continue
|
||||
} else {
|
||||
$length = ord($string[$p++]);
|
||||
if (($length & ASN_LONG_LEN)==ASN_LONG_LEN){
|
||||
$tempLength = 0;
|
||||
for ($x=0; $x<($length & (ASN_LONG_LEN-1)); $x++){
|
||||
$tempLength = @ord($string[$p++]) + ($tempLength * 256);
|
||||
}
|
||||
$length = $tempLength;
|
||||
}
|
||||
$data = substr($string, $p, intval($length));
|
||||
$parsed[] = self::parseASNData($type, $data, $level, $maxLevels);
|
||||
$p = $p + $length;
|
||||
}
|
||||
}
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ASN.1 field value.
|
||||
*
|
||||
* This function takes a binary ASN.1 value and parses it according to it's specified type
|
||||
*
|
||||
* @param int $type The type of data being provided
|
||||
* @param string $data The raw binary data string
|
||||
* @param int $level The current parsing depth
|
||||
* @param int $maxLevels The max parsing depth
|
||||
* @return mixed The data that was parsed from the raw binary data string
|
||||
*/
|
||||
public static function parseASNData($type, $data, $level, $maxLevels){
|
||||
$type = $type%50; // strip out context
|
||||
switch ($type){
|
||||
default:
|
||||
return new ASN_BASE($data);
|
||||
case ASN_BOOLEAN:
|
||||
return new ASN_BOOLEAN((bool)$data);
|
||||
case ASN_INTEGER:
|
||||
return new ASN_INTEGER(strtr(base64_encode($data),'+/','-_'));
|
||||
case ASN_BIT_STR:
|
||||
return new ASN_BIT_STR(self::parseASNString($data, $level+1, $maxLevels));
|
||||
case ASN_OCTET_STR:
|
||||
return new ASN_OCTET_STR($data);
|
||||
case ASN_NULL:
|
||||
return new ASN_NULL(null);
|
||||
case ASN_REAL:
|
||||
return new ASN_REAL($data);
|
||||
case ASN_ENUMERATED:
|
||||
return new ASN_ENUMERATED(self::parseASNString($data, $level+1, $maxLevels));
|
||||
case ASN_RELATIVE_OID: // I don't really know how this works and don't have an example :-)
|
||||
// so, lets just return it ...
|
||||
return new ASN_RELATIVE_OID($data);
|
||||
case ASN_SEQUENCE:
|
||||
return new ASN_SEQUENCE(self::parseASNString($data, $level+1, $maxLevels));
|
||||
case ASN_SET:
|
||||
return new ASN_SET(self::parseASNString($data, $level+1, $maxLevels));
|
||||
case ASN_PRINT_STR:
|
||||
return new ASN_PRINT_STR($data);
|
||||
case ASN_IA5_STR:
|
||||
return new ASN_IA5_STR($data);
|
||||
case ASN_UTC_TIME:
|
||||
return new ASN_UTC_TIME($data);
|
||||
case ASN_GENERAL_TIME:
|
||||
return new ASN_GENERAL_TIME($data);
|
||||
case ASN_OBJECT_ID:
|
||||
return new ASN_OBJECT_ID(self::parseOID($data));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ASN.1 OID value.
|
||||
*
|
||||
* This takes the raw binary string that represents an OID value and parses it into its
|
||||
* dot notation form. example - 1.2.840.113549.1.1.5
|
||||
* look up OID's here: http://www.oid-info.com/
|
||||
* (the multi-byte OID section can be done in a more efficient way, I will fix it later)
|
||||
*
|
||||
* @param string $data The raw binary data string
|
||||
* @return string The OID contained in $data
|
||||
*/
|
||||
public static function parseOID($string){
|
||||
$ret = floor(ord($string[0])/40).".";
|
||||
$ret .= (ord($string[0]) % 40);
|
||||
$build = array();
|
||||
$cs = 0;
|
||||
|
||||
for ($i=1; $i<strlen($string); $i++){
|
||||
$v = ord($string[$i]);
|
||||
if ($v>127){
|
||||
$build[] = ord($string[$i])-ASN_BIT;
|
||||
} elseif ($build){
|
||||
// do the build here for multibyte values
|
||||
$build[] = ord($string[$i])-ASN_BIT;
|
||||
// you know, it seems there should be a better way to do this...
|
||||
$build = array_reverse($build);
|
||||
$num = 0;
|
||||
for ($x=0; $x<count($build); $x++){
|
||||
$mult = $x==0?1:pow(256, $x);
|
||||
if ($x+1==count($build)){
|
||||
$value = ((($build[$x] & (ASN_BIT-1)) >> $x)) * $mult;
|
||||
} else {
|
||||
$value = ((($build[$x] & (ASN_BIT-1)) >> $x) ^ ($build[$x+1] << (7 - $x) & 255)) * $mult;
|
||||
}
|
||||
$num += $value;
|
||||
}
|
||||
$ret .= ".".$num;
|
||||
$build = array(); // start over
|
||||
} else {
|
||||
$ret .= ".".$v;
|
||||
$build = array();
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function printASN($x, $indent=''){
|
||||
if (is_object($x)) {
|
||||
echo $indent.$x->typeName."\n";
|
||||
if (ASN_NULL == $x->type) return;
|
||||
if (is_array($x->data)) {
|
||||
while ($d = $x->value) {
|
||||
echo self::printASN($d, $indent.'. ');
|
||||
}
|
||||
$x->reset();
|
||||
} else {
|
||||
echo self::printASN($x->data, $indent.'. ');
|
||||
}
|
||||
} elseif (is_array($x)) {
|
||||
foreach ($x as $d) {
|
||||
echo self::printASN($d, $indent);
|
||||
}
|
||||
} else {
|
||||
if (preg_match('/[^[:print:]]/', $x)) // if we have non-printable characters that would
|
||||
$x = base64_encode($x); // mess up the console, then print the base64 of them...
|
||||
echo $indent.$x."\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -24,6 +24,8 @@ use Friendica\Core\Renderer;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Module\Security\Login;
|
||||
use Friendica\Security\OAuth1\OAuthRequest;
|
||||
use Friendica\Security\OAuth1\OAuthUtil;
|
||||
|
||||
require_once __DIR__ . '/../include/api.php';
|
||||
|
||||
|
|
@ -47,12 +49,12 @@ function oauth_get_client(OAuthRequest $request)
|
|||
function api_post(App $a)
|
||||
{
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($a->user) && !empty($a->user['uid']) && $a->user['uid'] != local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -107,7 +109,7 @@ function api_content(App $a)
|
|||
|
||||
if (!local_user()) {
|
||||
/// @TODO We need login form to redirect to this page
|
||||
notice(DI::l10n()->t('Please login to continue.') . EOL);
|
||||
notice(DI::l10n()->t('Please login to continue.'));
|
||||
return Login::form(DI::args()->getQueryString(), false, $request->get_parameters());
|
||||
}
|
||||
//FKOAuth1::loginUser(4);
|
||||
|
|
|
|||
40
mod/cal.php
|
|
@ -24,7 +24,6 @@
|
|||
*/
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Content\Feature;
|
||||
use Friendica\Content\Nav;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Widget;
|
||||
|
|
@ -37,17 +36,18 @@ use Friendica\Model\Event;
|
|||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Module\BaseProfile;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Temporal;
|
||||
|
||||
function cal_init(App $a)
|
||||
{
|
||||
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
|
||||
throw new \Friendica\Network\HTTPException\ForbiddenException(DI::l10n()->t('Access denied.'));
|
||||
throw new HTTPException\ForbiddenException(DI::l10n()->t('Access denied.'));
|
||||
}
|
||||
|
||||
if ($a->argc < 2) {
|
||||
throw new \Friendica\Network\HTTPException\ForbiddenException(DI::l10n()->t('Access denied.'));
|
||||
throw new HTTPException\ForbiddenException(DI::l10n()->t('Access denied.'));
|
||||
}
|
||||
|
||||
Nav::setSelected('events');
|
||||
|
|
@ -55,7 +55,7 @@ function cal_init(App $a)
|
|||
$nick = $a->argv[1];
|
||||
$user = DBA::selectFirst('user', [], ['nickname' => $nick, 'blocked' => false]);
|
||||
if (!DBA::isResult($user)) {
|
||||
throw new \Friendica\Network\HTTPException\NotFoundException();
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
$a->data['user'] = $user;
|
||||
|
|
@ -67,18 +67,21 @@ function cal_init(App $a)
|
|||
return;
|
||||
}
|
||||
|
||||
$profile = Profile::getByNickname($nick, $a->profile_uid);
|
||||
$a->profile = Profile::getByNickname($nick, $a->profile_uid);
|
||||
if (empty($a->profile)) {
|
||||
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
|
||||
}
|
||||
|
||||
$account_type = Contact::getAccountType($profile);
|
||||
$account_type = Contact::getAccountType($a->profile);
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('widget/vcard.tpl');
|
||||
|
||||
$vcard_widget = Renderer::replaceMacros($tpl, [
|
||||
'$name' => $profile['name'],
|
||||
'$photo' => $profile['photo'],
|
||||
'$addr' => $profile['addr'] ?: '',
|
||||
'$name' => $a->profile['name'],
|
||||
'$photo' => $a->profile['photo'],
|
||||
'$addr' => $a->profile['addr'] ?: '',
|
||||
'$account_type' => $account_type,
|
||||
'$about' => BBCode::convert($profile['about'] ?: ''),
|
||||
'$about' => BBCode::convert($a->profile['about']),
|
||||
]);
|
||||
|
||||
$cal_widget = Widget\CalendarExport::getHTML();
|
||||
|
|
@ -100,6 +103,11 @@ function cal_content(App $a)
|
|||
// get the translation strings for the callendar
|
||||
$i18n = Event::getStrings();
|
||||
|
||||
DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.min.css');
|
||||
DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.print.min.css', 'print');
|
||||
DI::page()->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js');
|
||||
DI::page()->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js');
|
||||
|
||||
$htpl = Renderer::getMarkupTemplate('event_head.tpl');
|
||||
DI::page()['htmlhead'] .= Renderer::replaceMacros($htpl, [
|
||||
'$module_url' => '/cal/' . $a->data['user']['nickname'],
|
||||
|
|
@ -121,6 +129,9 @@ function cal_content(App $a)
|
|||
// Setup permissions structures
|
||||
$owner_uid = intval($a->data['user']['uid']);
|
||||
$nick = $a->data['user']['nickname'];
|
||||
if (empty($a->profile)) {
|
||||
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
|
||||
}
|
||||
|
||||
$contact_id = Session::getRemoteContactID($a->profile['uid']);
|
||||
|
||||
|
|
@ -129,7 +140,7 @@ function cal_content(App $a)
|
|||
$is_owner = local_user() == $a->profile['uid'];
|
||||
|
||||
if ($a->profile['hidewall'] && !$is_owner && !$remote_contact) {
|
||||
notice(DI::l10n()->t('Access to this profile has been restricted.') . EOL);
|
||||
notice(DI::l10n()->t('Access to this profile has been restricted.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -287,13 +298,6 @@ function cal_content(App $a)
|
|||
return;
|
||||
}
|
||||
|
||||
// Test permissions
|
||||
// Respect the export feature setting for all other /cal pages if it's not the own profile
|
||||
if ((local_user() !== $owner_uid) && !Feature::isEnabled($owner_uid, "export_calendar")) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
DI::baseUrl()->redirect('cal/' . $nick);
|
||||
}
|
||||
|
||||
// Get the export data by uid
|
||||
$evexport = Event::exportListByUserId($owner_uid, $format);
|
||||
|
||||
|
|
|
|||
170
mod/common.php
|
|
@ -1,170 +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\App;
|
||||
use Friendica\Content\ContactSelector;
|
||||
use Friendica\Content\Pager;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model;
|
||||
use Friendica\Module;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
function common_content(App $a)
|
||||
{
|
||||
$o = '';
|
||||
|
||||
$cmd = $a->argv[1];
|
||||
$uid = intval($a->argv[2]);
|
||||
$cid = intval($a->argv[3]);
|
||||
$zcid = 0;
|
||||
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($cmd !== 'loc' && $cmd != 'rem') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$uid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($cmd === 'loc' && $cid) {
|
||||
$contact = DBA::selectFirst('contact', ['name', 'url', 'photo', 'uid', 'id'], ['id' => $cid, 'uid' => $uid]);
|
||||
|
||||
if (DBA::isResult($contact)) {
|
||||
DI::page()['aside'] = "";
|
||||
Model\Profile::load($a, "", Model\Contact::getDetailsByURL($contact["url"]));
|
||||
}
|
||||
} else {
|
||||
$contact = DBA::selectFirst('contact', ['name', 'url', 'photo', 'uid', 'id'], ['self' => true, 'uid' => $uid]);
|
||||
|
||||
if (DBA::isResult($contact)) {
|
||||
$vcard_widget = Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [
|
||||
'$name' => $contact['name'],
|
||||
'$photo' => $contact['photo'],
|
||||
'url' => 'contact/' . $cid
|
||||
]);
|
||||
|
||||
if (empty(DI::page()['aside'])) {
|
||||
DI::page()['aside'] = '';
|
||||
}
|
||||
DI::page()['aside'] .= $vcard_widget;
|
||||
}
|
||||
}
|
||||
|
||||
if (!DBA::isResult($contact)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$cid && Model\Profile::getMyURL()) {
|
||||
$contact = DBA::selectFirst('contact', ['id'], ['nurl' => Strings::normaliseLink(Model\Profile::getMyURL()), 'uid' => $uid]);
|
||||
if (DBA::isResult($contact)) {
|
||||
$cid = $contact['id'];
|
||||
} else {
|
||||
$gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => Strings::normaliseLink(Model\Profile::getMyURL())]);
|
||||
if (DBA::isResult($gcontact)) {
|
||||
$zcid = $gcontact['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($cid == 0 && $zcid == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($cid) {
|
||||
$total = Model\GContact::countCommonFriends($uid, $cid);
|
||||
} else {
|
||||
$total = Model\GContact::countCommonFriendsZcid($uid, $zcid);
|
||||
}
|
||||
|
||||
if ($total < 1) {
|
||||
notice(DI::l10n()->t('No contacts in common.') . EOL);
|
||||
return $o;
|
||||
}
|
||||
|
||||
$pager = new Pager(DI::l10n(), DI::args()->getQueryString());
|
||||
|
||||
if ($cid) {
|
||||
$common_friends = Model\GContact::commonFriends($uid, $cid, $pager->getStart(), $pager->getItemsPerPage());
|
||||
} else {
|
||||
$common_friends = Model\GContact::commonFriendsZcid($uid, $zcid, $pager->getStart(), $pager->getItemsPerPage());
|
||||
}
|
||||
|
||||
if (!DBA::isResult($common_friends)) {
|
||||
return $o;
|
||||
}
|
||||
|
||||
$id = 0;
|
||||
|
||||
$entries = [];
|
||||
foreach ($common_friends as $common_friend) {
|
||||
//get further details of the contact
|
||||
$contact_details = Model\Contact::getDetailsByURL($common_friend['url'], $uid);
|
||||
|
||||
// $rr['id'] is needed to use contact_photo_menu()
|
||||
/// @TODO Adding '/" here avoids E_NOTICE on missing constants
|
||||
$common_friend['id'] = $common_friend['cid'];
|
||||
|
||||
$photo_menu = Model\Contact::photoMenu($common_friend);
|
||||
|
||||
$entry = [
|
||||
'url' => Model\Contact::magicLink($common_friend['url']),
|
||||
'itemurl' => ($contact_details['addr'] ?? '') ?: $common_friend['url'],
|
||||
'name' => $contact_details['name'],
|
||||
'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB),
|
||||
'img_hover' => $contact_details['name'],
|
||||
'details' => $contact_details['location'],
|
||||
'tags' => $contact_details['keywords'],
|
||||
'about' => $contact_details['about'],
|
||||
'account_type' => Model\Contact::getAccountType($contact_details),
|
||||
'network' => ContactSelector::networkToName($contact_details['network'], $contact_details['url']),
|
||||
'photo_menu' => $photo_menu,
|
||||
'id' => ++$id,
|
||||
];
|
||||
$entries[] = $entry;
|
||||
}
|
||||
|
||||
$title = '';
|
||||
$tab_str = '';
|
||||
if ($cmd === 'loc' && $cid && local_user() == $uid) {
|
||||
$tab_str = Module\Contact::getTabsHTML($a, $contact, 5);
|
||||
} else {
|
||||
$title = DI::l10n()->t('Common Friends');
|
||||
}
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl');
|
||||
|
||||
$o .= Renderer::replaceMacros($tpl, [
|
||||
'$title' => $title,
|
||||
'$tab_str' => $tab_str,
|
||||
'$contacts' => $entries,
|
||||
'$paginate' => $pager->renderFull($total),
|
||||
]);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
|
@ -27,9 +27,9 @@
|
|||
* 2. We may be the target or other side of the conversation to scenario 1, and will
|
||||
* interact with that process on our own user's behalf.
|
||||
*
|
||||
* @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf
|
||||
* @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf
|
||||
* You also find a graphic which describes the confirmation process at
|
||||
* https://github.com/friendica/friendica/blob/master/spec/dfrn2_contact_confirmation.png
|
||||
* https://github.com/friendica/friendica/blob/stable/spec/dfrn2_contact_confirmation.png
|
||||
*/
|
||||
|
||||
use Friendica\App;
|
||||
|
|
@ -40,12 +40,12 @@ use Friendica\Database\DBA;
|
|||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Notify;
|
||||
use Friendica\Model\Notify\Type;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Util\Crypto;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
|
|
@ -76,13 +76,13 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
if (empty($_POST['source_url'])) {
|
||||
$uid = ($handsfree['uid'] ?? 0) ?: local_user();
|
||||
if (!$uid) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$user = DBA::selectFirst('user', [], ['uid' => $uid]);
|
||||
if (!DBA::isResult($user)) {
|
||||
notice(DI::l10n()->t('Profile not found.') . EOL);
|
||||
notice(DI::l10n()->t('Profile not found.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -137,8 +137,8 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
);
|
||||
if (!DBA::isResult($r)) {
|
||||
Logger::log('Contact not found in DB.');
|
||||
notice(DI::l10n()->t('Contact not found.') . EOL);
|
||||
notice(DI::l10n()->t('This may occasionally happen if contact was requested by both persons and it has already been approved.') . EOL);
|
||||
notice(DI::l10n()->t('Contact not found.'));
|
||||
notice(DI::l10n()->t('This may occasionally happen if contact was requested by both persons and it has already been approved.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +214,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
$params['page'] = 2;
|
||||
}
|
||||
|
||||
Logger::log('Confirm: posting data to ' . $dfrn_confirm . ': ' . print_r($params, true), Logger::DATA);
|
||||
Logger::debug('Confirm: posting data', ['confirm' => $dfrn_confirm, 'parameter' => $params]);
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
@ -224,7 +224,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
*
|
||||
*/
|
||||
|
||||
$res = Network::post($dfrn_confirm, $params, [], 120)->getBody();
|
||||
$res = DI::httpRequest()->post($dfrn_confirm, $params, [], 120)->getBody();
|
||||
|
||||
Logger::log(' Confirm: received data: ' . $res, Logger::DATA);
|
||||
|
||||
|
|
@ -239,20 +239,20 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
// We shouldn't proceed, because the xml parser might choke,
|
||||
// and $status is going to be zero, which indicates success.
|
||||
// We can hardly call this a success.
|
||||
notice(DI::l10n()->t('Response from remote site was not understood.') . EOL);
|
||||
notice(DI::l10n()->t('Response from remote site was not understood.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen($leading_junk) && DI::config()->get('system', 'debugging')) {
|
||||
// This might be more common. Mixed error text and some XML.
|
||||
// If we're configured for debugging, show the text. Proceed in either case.
|
||||
notice(DI::l10n()->t('Unexpected response from remote site: ') . EOL . $leading_junk . EOL);
|
||||
notice(DI::l10n()->t('Unexpected response from remote site: ') . $leading_junk);
|
||||
}
|
||||
|
||||
if (stristr($res, "<status") === false) {
|
||||
// wrong xml! stop here!
|
||||
Logger::log('Unexpected response posting to ' . $dfrn_confirm);
|
||||
notice(DI::l10n()->t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res) . EOL);
|
||||
notice(DI::l10n()->t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -261,7 +261,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
$message = XML::unescape($xml->message); // human readable text of what may have gone wrong.
|
||||
switch ($status) {
|
||||
case 0:
|
||||
info(DI::l10n()->t("Confirmation completed successfully.") . EOL);
|
||||
info(DI::l10n()->t("Confirmation completed successfully."));
|
||||
break;
|
||||
case 1:
|
||||
// birthday paradox - generate new dfrn-id and fall through.
|
||||
|
|
@ -273,15 +273,15 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
);
|
||||
|
||||
case 2:
|
||||
notice(DI::l10n()->t("Temporary failure. Please wait and try again.") . EOL);
|
||||
notice(DI::l10n()->t("Temporary failure. Please wait and try again."));
|
||||
break;
|
||||
case 3:
|
||||
notice(DI::l10n()->t("Introduction failed or was revoked.") . EOL);
|
||||
notice(DI::l10n()->t("Introduction failed or was revoked."));
|
||||
break;
|
||||
}
|
||||
|
||||
if (strlen($message)) {
|
||||
notice(DI::l10n()->t('Remote site reported: ') . $message . EOL);
|
||||
notice(DI::l10n()->t('Remote site reported: ') . $message);
|
||||
}
|
||||
|
||||
if (($status == 0) && $intro_id) {
|
||||
|
|
@ -305,7 +305,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
*
|
||||
* We will also update the contact record with the nature and scope of the relationship.
|
||||
*/
|
||||
Contact::updateAvatar($contact['photo'], $uid, $contact_id);
|
||||
Contact::updateAvatar($contact_id, $contact['photo']);
|
||||
|
||||
Logger::log('dfrn_confirm: confirm - imported photos');
|
||||
|
||||
|
|
@ -372,9 +372,9 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
$forum = (($page == 1) ? 1 : 0);
|
||||
$prv = (($page == 2) ? 1 : 0);
|
||||
|
||||
Logger::log('dfrn_confirm: requestee contacted: ' . $node);
|
||||
Logger::notice('requestee contacted', ['node' => $node]);
|
||||
|
||||
Logger::log('dfrn_confirm: request: POST=' . print_r($_POST, true), Logger::DATA);
|
||||
Logger::debug('request', ['POST' => $_POST]);
|
||||
|
||||
// If $aes_key is set, both of these items require unpacking from the hex transport encoding.
|
||||
|
||||
|
|
@ -482,10 +482,10 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
if (DBA::isResult($contact)) {
|
||||
$photo = $contact['photo'];
|
||||
} else {
|
||||
$photo = DI::baseUrl() . '/images/person-300.jpg';
|
||||
$photo = DI::baseUrl() . Contact::DEFAULT_AVATAR_PHOTO;
|
||||
}
|
||||
|
||||
Contact::updateAvatar($photo, $local_uid, $dfrn_record);
|
||||
Contact::updateAvatar($dfrn_record, $photo);
|
||||
|
||||
Logger::log('dfrn_confirm: request - photos imported');
|
||||
|
||||
|
|
@ -543,18 +543,12 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
|||
if ($combined['notify-flags'] & Type::CONFIRM) {
|
||||
$mutual = ($new_relation == Contact::FRIEND);
|
||||
notification([
|
||||
'type' => Type::CONFIRM,
|
||||
'notify_flags' => $combined['notify-flags'],
|
||||
'language' => $combined['language'],
|
||||
'to_name' => $combined['username'],
|
||||
'to_email' => $combined['email'],
|
||||
'uid' => $combined['uid'],
|
||||
'link' => DI::baseUrl() . '/contact/' . $dfrn_record,
|
||||
'source_name' => ((strlen(stripslashes($combined['name']))) ? stripslashes($combined['name']) : DI::l10n()->t('[Name Withheld]')),
|
||||
'source_link' => $combined['url'],
|
||||
'source_photo' => $combined['photo'],
|
||||
'verb' => ($mutual ? Activity::FRIEND : Activity::FOLLOW),
|
||||
'otype' => 'intro'
|
||||
'type' => Type::CONFIRM,
|
||||
'otype' => Notify\ObjectType::INTRO,
|
||||
'verb' => ($mutual ? Activity::FRIEND : Activity::FOLLOW),
|
||||
'uid' => $combined['uid'],
|
||||
'cid' => $combined['id'],
|
||||
'link' => DI::baseUrl() . '/contact/' . $dfrn_record,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
*
|
||||
* The dfrn notify endpoint
|
||||
*
|
||||
* @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf
|
||||
* @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf
|
||||
*/
|
||||
|
||||
use Friendica\App;
|
||||
|
|
@ -28,6 +28,7 @@ use Friendica\Core\System;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Conversation;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Protocol\DFRN;
|
||||
use Friendica\Protocol\Diaspora;
|
||||
|
|
@ -35,8 +36,6 @@ use Friendica\Util\Network;
|
|||
use Friendica\Util\Strings;
|
||||
|
||||
function dfrn_notify_post(App $a) {
|
||||
Logger::log(__function__, Logger::TRACE);
|
||||
|
||||
$postdata = Network::postdata();
|
||||
|
||||
if (empty($_POST) || !empty($postdata)) {
|
||||
|
|
@ -192,7 +191,7 @@ function dfrn_notify_post(App $a) {
|
|||
|
||||
Logger::log('Importing post from ' . $importer['addr'] . ' to ' . $importer['nickname'] . ' with the RINO ' . $rino_remote . ' encryption.', Logger::DEBUG);
|
||||
|
||||
$ret = DFRN::import($data, $importer);
|
||||
$ret = DFRN::import($data, $importer, Conversation::PARCEL_LEGACY_DFRN, Conversation::PUSH);
|
||||
System::xmlExit($ret, 'Processed');
|
||||
|
||||
// NOTREACHED
|
||||
|
|
@ -224,7 +223,7 @@ function dfrn_dispatch_public($postdata)
|
|||
Logger::log('Importing post from ' . $msg['author'] . ' with the public envelope.', Logger::DEBUG);
|
||||
|
||||
// Now we should be able to import it
|
||||
$ret = DFRN::import($msg['message'], $importer);
|
||||
$ret = DFRN::import($msg['message'], $importer, Conversation::PARCEL_DIASPORA_DFRN, Conversation::RELAY);
|
||||
System::xmlExit($ret, 'Done');
|
||||
}
|
||||
|
||||
|
|
@ -257,7 +256,7 @@ function dfrn_dispatch_private($user, $postdata)
|
|||
Logger::log('Importing post from ' . $msg['author'] . ' to ' . $user['nickname'] . ' with the private envelope.', Logger::DEBUG);
|
||||
|
||||
// Now we should be able to import it
|
||||
$ret = DFRN::import($msg['message'], $importer);
|
||||
$ret = DFRN::import($msg['message'], $importer, Conversation::PARCEL_DIASPORA_DFRN, Conversation::PUSH);
|
||||
System::xmlExit($ret, 'Done');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,13 +21,12 @@
|
|||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Protocol\DFRN;
|
||||
use Friendica\Protocol\OStatus;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
|
|
@ -115,7 +114,7 @@ function dfrn_poll_init(App $a)
|
|||
);
|
||||
|
||||
if (DBA::isResult($r)) {
|
||||
$s = Network::fetchUrl($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check');
|
||||
$s = DI::httpRequest()->fetch($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check');
|
||||
|
||||
Logger::log("dfrn_poll: old profile returns " . $s, Logger::DATA);
|
||||
|
||||
|
|
@ -133,7 +132,7 @@ function dfrn_poll_init(App $a)
|
|||
Session::setVisitorsContacts();
|
||||
|
||||
if (!$quiet) {
|
||||
info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']) . EOL);
|
||||
info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']));
|
||||
}
|
||||
|
||||
// Visitors get 1 day session.
|
||||
|
|
@ -240,7 +239,6 @@ function dfrn_poll_post(App $a)
|
|||
{
|
||||
$dfrn_id = $_POST['dfrn_id'] ?? '';
|
||||
$challenge = $_POST['challenge'] ?? '';
|
||||
$url = $_POST['url'] ?? '';
|
||||
$sec = $_POST['sec'] ?? '';
|
||||
$ptype = $_POST['type'] ?? '';
|
||||
$perm = ($_POST['perm'] ?? '') ?: 'r';
|
||||
|
|
@ -320,7 +318,6 @@ function dfrn_poll_post(App $a)
|
|||
exit();
|
||||
}
|
||||
|
||||
$type = $r[0]['type'];
|
||||
$last_update = $r[0]['last_update'];
|
||||
|
||||
DBA::delete('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge]);
|
||||
|
|
@ -347,59 +344,29 @@ function dfrn_poll_post(App $a)
|
|||
}
|
||||
|
||||
$contact = $r[0];
|
||||
$owner_uid = $r[0]['uid'];
|
||||
$contact_id = $r[0]['id'];
|
||||
|
||||
if ($type === 'reputation' && strlen($url)) {
|
||||
$r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
|
||||
DBA::escape($url),
|
||||
intval($owner_uid)
|
||||
);
|
||||
$reputation = 0;
|
||||
$text = '';
|
||||
|
||||
if (DBA::isResult($r)) {
|
||||
$reputation = $r[0]['rating'];
|
||||
$text = $r[0]['reason'];
|
||||
|
||||
if ($r[0]['id'] == $contact_id) { // inquiring about own reputation not allowed
|
||||
$reputation = 0;
|
||||
$text = '';
|
||||
}
|
||||
// Update the writable flag if it changed
|
||||
Logger::debug('post request feed', ['post' => $_POST]);
|
||||
if ($dfrn_version >= 2.21) {
|
||||
if ($perm === 'rw') {
|
||||
$writable = 1;
|
||||
} else {
|
||||
$writable = 0;
|
||||
}
|
||||
|
||||
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||
<reputation>
|
||||
<url>$url</url>
|
||||
<rating>$reputation</rating>
|
||||
<description>$text</description>
|
||||
</reputation>
|
||||
";
|
||||
exit();
|
||||
// NOTREACHED
|
||||
} else {
|
||||
// Update the writable flag if it changed
|
||||
Logger::log('dfrn_poll: post request feed: ' . print_r($_POST, true), Logger::DATA);
|
||||
if ($dfrn_version >= 2.21) {
|
||||
if ($perm === 'rw') {
|
||||
$writable = 1;
|
||||
} else {
|
||||
$writable = 0;
|
||||
}
|
||||
|
||||
if ($writable != $contact['writable']) {
|
||||
q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d",
|
||||
intval($writable),
|
||||
intval($contact_id)
|
||||
);
|
||||
}
|
||||
if ($writable != $contact['writable']) {
|
||||
q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d",
|
||||
intval($writable),
|
||||
intval($contact_id)
|
||||
);
|
||||
}
|
||||
|
||||
header("Content-type: application/atom+xml");
|
||||
$o = DFRN::feed($dfrn_id, $a->argv[1], $last_update, $direction);
|
||||
echo $o;
|
||||
exit();
|
||||
}
|
||||
|
||||
header("Content-type: application/atom+xml");
|
||||
$o = DFRN::feed($dfrn_id, $a->argv[1], $last_update, $direction);
|
||||
echo $o;
|
||||
exit();
|
||||
}
|
||||
|
||||
function dfrn_poll_content(App $a)
|
||||
|
|
@ -499,20 +466,20 @@ function dfrn_poll_content(App $a)
|
|||
|
||||
// URL reply
|
||||
if ($dfrn_version < 2.2) {
|
||||
$s = Network::fetchUrl($r[0]['poll']
|
||||
. '?dfrn_id=' . $encrypted_id
|
||||
. '&type=profile-check'
|
||||
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION
|
||||
. '&challenge=' . $challenge
|
||||
. '&sec=' . $sec
|
||||
$s = DI::httpRequest()->fetch($r[0]['poll']
|
||||
. '?dfrn_id=' . $encrypted_id
|
||||
. '&type=profile-check'
|
||||
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION
|
||||
. '&challenge=' . $challenge
|
||||
. '&sec=' . $sec
|
||||
);
|
||||
} else {
|
||||
$s = Network::post($r[0]['poll'], [
|
||||
'dfrn_id' => $encrypted_id,
|
||||
'type' => 'profile-check',
|
||||
$s = DI::httpRequest()->post($r[0]['poll'], [
|
||||
'dfrn_id' => $encrypted_id,
|
||||
'type' => 'profile-check',
|
||||
'dfrn_version' => DFRN_PROTOCOL_VERSION,
|
||||
'challenge' => $challenge,
|
||||
'sec' => $sec
|
||||
'challenge' => $challenge,
|
||||
'sec' => $sec
|
||||
])->getBody();
|
||||
}
|
||||
|
||||
|
|
@ -521,7 +488,7 @@ function dfrn_poll_content(App $a)
|
|||
if (strlen($s) && strstr($s, '<?xml')) {
|
||||
$xml = XML::parseString($s);
|
||||
|
||||
Logger::log('dfrn_poll: profile: parsed xml: ' . print_r($xml, true), Logger::DATA);
|
||||
Logger::debug(' profile: parsed', ['xml' => $xml]);
|
||||
|
||||
Logger::log('dfrn_poll: secure profile: challenge: ' . $xml->challenge . ' expecting ' . $hash);
|
||||
Logger::log('dfrn_poll: secure profile: sec: ' . $xml->sec . ' expecting ' . $sec);
|
||||
|
|
@ -536,7 +503,7 @@ function dfrn_poll_content(App $a)
|
|||
Session::setVisitorsContacts();
|
||||
|
||||
if (!$quiet) {
|
||||
info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']) . EOL);
|
||||
info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']));
|
||||
}
|
||||
|
||||
// Visitors get 1 day session.
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@
|
|||
*
|
||||
*Handles communication associated with the issuance of friend requests.
|
||||
*
|
||||
* @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf
|
||||
* @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf
|
||||
* You also find a graphic which describes the confirmation process at
|
||||
* https://github.com/friendica/friendica/blob/master/spec/dfrn2_contact_request.png
|
||||
* https://github.com/friendica/friendica/blob/stable/spec/dfrn2_contact_request.png
|
||||
*/
|
||||
|
||||
use Friendica\App;
|
||||
|
|
@ -29,12 +29,13 @@ use Friendica\Core\Logger;
|
|||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Search;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Notify;
|
||||
use Friendica\Model\Notify\Type;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\User;
|
||||
|
|
@ -110,7 +111,7 @@ function dfrn_request_post(App $a)
|
|||
if (DBA::isResult($r)) {
|
||||
if (strlen($r[0]['dfrn-id'])) {
|
||||
// We don't need to be here. It has already happened.
|
||||
notice(DI::l10n()->t("This introduction has already been accepted.") . EOL);
|
||||
notice(DI::l10n()->t("This introduction has already been accepted."));
|
||||
return;
|
||||
} else {
|
||||
$contact_record = $r[0];
|
||||
|
|
@ -128,18 +129,18 @@ function dfrn_request_post(App $a)
|
|||
$parms = Probe::profile($dfrn_url);
|
||||
|
||||
if (!count($parms)) {
|
||||
notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.') . EOL);
|
||||
notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.'));
|
||||
return;
|
||||
} else {
|
||||
if (empty($parms['fn'])) {
|
||||
notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.') . EOL);
|
||||
notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.'));
|
||||
}
|
||||
if (empty($parms['photo'])) {
|
||||
notice(DI::l10n()->t('Warning: profile location has no profile photo.') . EOL);
|
||||
notice(DI::l10n()->t('Warning: profile location has no profile photo.'));
|
||||
}
|
||||
$invalid = Probe::validDfrn($parms);
|
||||
if ($invalid) {
|
||||
notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid) . EOL);
|
||||
notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +178,7 @@ function dfrn_request_post(App $a)
|
|||
}
|
||||
|
||||
if ($r) {
|
||||
info(DI::l10n()->t("Introduction complete.") . EOL);
|
||||
info(DI::l10n()->t("Introduction complete."));
|
||||
}
|
||||
|
||||
$r = q("SELECT `id`, `network` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `site-pubkey` = '%s' LIMIT 1",
|
||||
|
|
@ -189,7 +190,7 @@ function dfrn_request_post(App $a)
|
|||
Group::addMember(User::getDefaultGroup(local_user(), $r[0]["network"]), $r[0]['id']);
|
||||
|
||||
if (isset($photo)) {
|
||||
Contact::updateAvatar($photo, local_user(), $r[0]["id"], true);
|
||||
Contact::updateAvatar($r[0]["id"], $photo, true);
|
||||
}
|
||||
|
||||
$forward_path = "contact/" . $r[0]['id'];
|
||||
|
|
@ -203,7 +204,7 @@ function dfrn_request_post(App $a)
|
|||
}
|
||||
|
||||
if (!empty($dfrn_request) && strlen($confirm_key)) {
|
||||
Network::fetchUrl($dfrn_request . '?confirm_key=' . $confirm_key);
|
||||
DI::httpRequest()->fetch($dfrn_request . '?confirm_key=' . $confirm_key);
|
||||
}
|
||||
|
||||
// (ignore reply, nothing we can do it failed)
|
||||
|
|
@ -213,7 +214,7 @@ function dfrn_request_post(App $a)
|
|||
}
|
||||
|
||||
// invalid/bogus request
|
||||
notice(DI::l10n()->t('Unrecoverable protocol error.') . EOL);
|
||||
notice(DI::l10n()->t('Unrecoverable protocol error.'));
|
||||
DI::baseUrl()->redirect();
|
||||
return; // NOTREACHED
|
||||
}
|
||||
|
|
@ -240,7 +241,7 @@ function dfrn_request_post(App $a)
|
|||
*
|
||||
*/
|
||||
if (empty($a->profile['uid'])) {
|
||||
notice(DI::l10n()->t('Profile unavailable.') . EOL);
|
||||
notice(DI::l10n()->t('Profile unavailable.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -261,9 +262,9 @@ function dfrn_request_post(App $a)
|
|||
intval($uid)
|
||||
);
|
||||
if (DBA::isResult($r) && count($r) > $maxreq) {
|
||||
notice(DI::l10n()->t('%s has received too many connection requests today.', $a->profile['name']) . EOL);
|
||||
notice(DI::l10n()->t('Spam protection measures have been invoked.') . EOL);
|
||||
notice(DI::l10n()->t('Friends are advised to please try again in 24 hours.') . EOL);
|
||||
notice(DI::l10n()->t('%s has received too many connection requests today.', $a->profile['name']));
|
||||
notice(DI::l10n()->t('Spam protection measures have been invoked.'));
|
||||
notice(DI::l10n()->t('Friends are advised to please try again in 24 hours.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -287,18 +288,18 @@ function dfrn_request_post(App $a)
|
|||
|
||||
$url = trim($_POST['dfrn_url']);
|
||||
if (!strlen($url)) {
|
||||
notice(DI::l10n()->t("Invalid locator") . EOL);
|
||||
notice(DI::l10n()->t("Invalid locator"));
|
||||
return;
|
||||
}
|
||||
|
||||
$hcard = '';
|
||||
|
||||
// Detect the network
|
||||
$data = Probe::uri($url);
|
||||
$data = Contact::getByURL($url);
|
||||
$network = $data["network"];
|
||||
|
||||
// Canonicalise email-style profile locator
|
||||
$url = Probe::webfingerDfrn($url, $hcard);
|
||||
// Canonicalize email-style profile locator
|
||||
$url = Probe::webfingerDfrn($data['url'] ?? $url, $hcard);
|
||||
|
||||
if (substr($url, 0, 5) === 'stat:') {
|
||||
// Every time we detect the remote subscription we define this as OStatus.
|
||||
|
|
@ -323,10 +324,10 @@ function dfrn_request_post(App $a)
|
|||
|
||||
if (DBA::isResult($ret)) {
|
||||
if (strlen($ret[0]['issued-id'])) {
|
||||
notice(DI::l10n()->t('You have already introduced yourself here.') . EOL);
|
||||
notice(DI::l10n()->t('You have already introduced yourself here.'));
|
||||
return;
|
||||
} elseif ($ret[0]['rel'] == Contact::FRIEND) {
|
||||
notice(DI::l10n()->t('Apparently you are already friends with %s.', $a->profile['name']) . EOL);
|
||||
notice(DI::l10n()->t('Apparently you are already friends with %s.', $a->profile['name']));
|
||||
return;
|
||||
} else {
|
||||
$contact_record = $ret[0];
|
||||
|
|
@ -346,19 +347,19 @@ function dfrn_request_post(App $a)
|
|||
} else {
|
||||
$url = Network::isUrlValid($url);
|
||||
if (!$url) {
|
||||
notice(DI::l10n()->t('Invalid profile URL.') . EOL);
|
||||
notice(DI::l10n()->t('Invalid profile URL.'));
|
||||
DI::baseUrl()->redirect(DI::args()->getCommand());
|
||||
return; // NOTREACHED
|
||||
}
|
||||
|
||||
if (!Network::isUrlAllowed($url)) {
|
||||
notice(DI::l10n()->t('Disallowed profile URL.') . EOL);
|
||||
notice(DI::l10n()->t('Disallowed profile URL.'));
|
||||
DI::baseUrl()->redirect(DI::args()->getCommand());
|
||||
return; // NOTREACHED
|
||||
}
|
||||
|
||||
if (Network::isUrlBlocked($url)) {
|
||||
notice(DI::l10n()->t('Blocked domain') . EOL);
|
||||
notice(DI::l10n()->t('Blocked domain'));
|
||||
DI::baseUrl()->redirect(DI::args()->getCommand());
|
||||
return; // NOTREACHED
|
||||
}
|
||||
|
|
@ -366,18 +367,18 @@ function dfrn_request_post(App $a)
|
|||
$parms = Probe::profile(($hcard) ? $hcard : $url);
|
||||
|
||||
if (!count($parms)) {
|
||||
notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.') . EOL);
|
||||
notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.'));
|
||||
DI::baseUrl()->redirect(DI::args()->getCommand());
|
||||
} else {
|
||||
if (empty($parms['fn'])) {
|
||||
notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.') . EOL);
|
||||
notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.'));
|
||||
}
|
||||
if (empty($parms['photo'])) {
|
||||
notice(DI::l10n()->t('Warning: profile location has no profile photo.') . EOL);
|
||||
notice(DI::l10n()->t('Warning: profile location has no profile photo.'));
|
||||
}
|
||||
$invalid = Probe::validDfrn($parms);
|
||||
if ($invalid) {
|
||||
notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid) . EOL);
|
||||
notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid));
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -420,12 +421,12 @@ function dfrn_request_post(App $a)
|
|||
);
|
||||
if (DBA::isResult($r)) {
|
||||
$contact_record = $r[0];
|
||||
Contact::updateAvatar($photo, $uid, $contact_record["id"], true);
|
||||
Contact::updateAvatar($contact_record["id"], $photo, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($r === false) {
|
||||
notice(DI::l10n()->t('Failed to update contact record.') . EOL);
|
||||
notice(DI::l10n()->t('Failed to update contact record.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -445,7 +446,7 @@ function dfrn_request_post(App $a)
|
|||
|
||||
// This notice will only be seen by the requestor if the requestor and requestee are on the same server.
|
||||
if (!$failed) {
|
||||
info(DI::l10n()->t('Your introduction has been sent.') . EOL);
|
||||
info(DI::l10n()->t('Your introduction has been sent.'));
|
||||
}
|
||||
|
||||
// "Homecoming" - send the requestor back to their site to record the introduction.
|
||||
|
|
@ -477,7 +478,7 @@ function dfrn_request_post(App $a)
|
|||
// NOTREACHED
|
||||
// END $network != Protocol::PHANTOM
|
||||
} else {
|
||||
notice(DI::l10n()->t("Remote subscription can't be done for your network. Please subscribe directly on your system.") . EOL);
|
||||
notice(DI::l10n()->t("Remote subscription can't be done for your network. Please subscribe directly on your system."));
|
||||
return;
|
||||
}
|
||||
} return;
|
||||
|
|
@ -493,7 +494,7 @@ function dfrn_request_content(App $a)
|
|||
// to send us to the post section to record the introduction.
|
||||
if (!empty($_GET['dfrn_url'])) {
|
||||
if (!local_user()) {
|
||||
info(DI::l10n()->t("Please login to confirm introduction.") . EOL);
|
||||
info(DI::l10n()->t("Please login to confirm introduction."));
|
||||
/* setup the return URL to come back to this page if they use openid */
|
||||
return Login::form();
|
||||
}
|
||||
|
|
@ -501,7 +502,7 @@ function dfrn_request_content(App $a)
|
|||
// Edge case, but can easily happen in the wild. This person is authenticated,
|
||||
// but not as the person who needs to deal with this request.
|
||||
if ($a->user['nickname'] != $a->argv[1]) {
|
||||
notice(DI::l10n()->t("Incorrect identity currently logged in. Please login to <strong>this</strong> profile.") . EOL);
|
||||
notice(DI::l10n()->t("Incorrect identity currently logged in. Please login to <strong>this</strong> profile."));
|
||||
return Login::form();
|
||||
}
|
||||
|
||||
|
|
@ -559,18 +560,12 @@ function dfrn_request_content(App $a)
|
|||
|
||||
if (!$auto_confirm) {
|
||||
notification([
|
||||
'type' => Type::INTRO,
|
||||
'notify_flags' => $r[0]['notify-flags'],
|
||||
'language' => $r[0]['language'],
|
||||
'to_name' => $r[0]['username'],
|
||||
'to_email' => $r[0]['email'],
|
||||
'uid' => $r[0]['uid'],
|
||||
'link' => DI::baseUrl() . '/notifications/intros',
|
||||
'source_name' => ((strlen(stripslashes($r[0]['name']))) ? stripslashes($r[0]['name']) : DI::l10n()->t('[Name Withheld]')),
|
||||
'source_link' => $r[0]['url'],
|
||||
'source_photo' => $r[0]['photo'],
|
||||
'verb' => Activity::REQ_FRIEND,
|
||||
'otype' => 'intro'
|
||||
'type' => Type::INTRO,
|
||||
'otype' => Notify\ObjectType::INTRO,
|
||||
'verb' => Activity::REQ_FRIEND,
|
||||
'uid' => $r[0]['uid'],
|
||||
'cid' => $r[0]['id'],
|
||||
'link' => DI::baseUrl() . '/notifications/intros',
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -603,7 +598,7 @@ function dfrn_request_content(App $a)
|
|||
// Normal web request. Display our user's introduction form.
|
||||
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
|
||||
if (!DI::config()->get('system', 'local_block')) {
|
||||
notice(DI::l10n()->t('Public access denied.') . EOL);
|
||||
notice(DI::l10n()->t('Public access denied.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ function display_init(App $a)
|
|||
$item = null;
|
||||
$item_user = local_user();
|
||||
|
||||
$fields = ['id', 'parent', 'author-id', 'body', 'uid', 'guid'];
|
||||
$fields = ['id', 'parent', 'author-id', 'body', 'uid', 'guid', 'gravity'];
|
||||
|
||||
// If there is only one parameter, then check if this parameter could be a guid
|
||||
if ($a->argc == 2) {
|
||||
|
|
@ -101,12 +101,12 @@ function display_init(App $a)
|
|||
}
|
||||
|
||||
if (!empty($_SERVER['HTTP_ACCEPT']) && strstr($_SERVER['HTTP_ACCEPT'], 'application/atom+xml')) {
|
||||
Logger::log('Directly serving XML for id '.$item["id"], Logger::DEBUG);
|
||||
displayShowFeed($item["id"], false);
|
||||
Logger::log('Directly serving XML for id '.$item['id'], Logger::DEBUG);
|
||||
displayShowFeed($item['id'], false);
|
||||
}
|
||||
|
||||
if ($item["id"] != $item["parent"]) {
|
||||
$parent = Item::selectFirstForUser($item_user, $fields, ['id' => $item["parent"]]);
|
||||
if ($item['gravity'] != GRAVITY_PARENT) {
|
||||
$parent = Item::selectFirstForUser($item_user, $fields, ['id' => $item['parent']]);
|
||||
$item = $parent ?: $item;
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +164,7 @@ function display_fetchauthor($a, $item)
|
|||
$profiledata["about"] = "";
|
||||
}
|
||||
|
||||
$profiledata = Contact::getDetailsByURL($profiledata["url"], local_user(), $profiledata);
|
||||
$profiledata = Contact::getByURLForUser($profiledata["url"], local_user()) ?: $profiledata;
|
||||
|
||||
if (!empty($profiledata["photo"])) {
|
||||
$profiledata["photo"] = DI::baseUrl()->remove($profiledata["photo"]);
|
||||
|
|
@ -183,9 +183,11 @@ function display_content(App $a, $update = false, $update_uid = 0)
|
|||
|
||||
$item = null;
|
||||
|
||||
$force = (bool)($_REQUEST['force'] ?? false);
|
||||
|
||||
if ($update) {
|
||||
$item_id = $_REQUEST['item_id'];
|
||||
$item = Item::selectFirst(['uid', 'parent', 'parent-uri'], ['id' => $item_id]);
|
||||
$item = Item::selectFirst(['uid', 'parent', 'parent-uri', 'parent-uri-id'], ['id' => $item_id]);
|
||||
if ($item['uid'] != 0) {
|
||||
$a->profile = ['uid' => intval($item['uid'])];
|
||||
} else {
|
||||
|
|
@ -199,14 +201,14 @@ function display_content(App $a, $update = false, $update_uid = 0)
|
|||
|
||||
if ($a->argc == 2) {
|
||||
$item_parent = 0;
|
||||
$fields = ['id', 'parent', 'parent-uri', 'uid'];
|
||||
$fields = ['id', 'parent', 'parent-uri', 'parent-uri-id', 'uid'];
|
||||
|
||||
if (local_user()) {
|
||||
$condition = ['guid' => $a->argv[1], 'uid' => local_user()];
|
||||
$item = Item::selectFirstForUser(local_user(), $fields, $condition);
|
||||
if (DBA::isResult($item)) {
|
||||
$item_id = $item["id"];
|
||||
$item_parent = $item["parent"];
|
||||
$item_id = $item['id'];
|
||||
$item_parent = $item['parent'];
|
||||
$item_parent_uri = $item['parent-uri'];
|
||||
}
|
||||
}
|
||||
|
|
@ -214,8 +216,8 @@ function display_content(App $a, $update = false, $update_uid = 0)
|
|||
if (($item_parent == 0) && remote_user()) {
|
||||
$item = Item::selectFirst($fields, ['guid' => $a->argv[1], 'private' => Item::PRIVATE, 'origin' => true]);
|
||||
if (DBA::isResult($item) && Contact::isFollower(remote_user(), $item['uid'])) {
|
||||
$item_id = $item["id"];
|
||||
$item_parent = $item["parent"];
|
||||
$item_id = $item['id'];
|
||||
$item_parent = $item['parent'];
|
||||
$item_parent_uri = $item['parent-uri'];
|
||||
}
|
||||
}
|
||||
|
|
@ -224,8 +226,8 @@ function display_content(App $a, $update = false, $update_uid = 0)
|
|||
$condition = ['private' => [Item::PUBLIC, Item::UNLISTED], 'guid' => $a->argv[1], 'uid' => 0];
|
||||
$item = Item::selectFirstForUser(local_user(), $fields, $condition);
|
||||
if (DBA::isResult($item)) {
|
||||
$item_id = $item["id"];
|
||||
$item_parent = $item["parent"];
|
||||
$item_id = $item['id'];
|
||||
$item_parent = $item['parent'];
|
||||
$item_parent_uri = $item['parent-uri'];
|
||||
}
|
||||
}
|
||||
|
|
@ -236,6 +238,10 @@ function display_content(App $a, $update = false, $update_uid = 0)
|
|||
throw new HTTPException\NotFoundException(DI::l10n()->t('The requested item doesn\'t exist or has been deleted.'));
|
||||
}
|
||||
|
||||
if (!DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
DBA::update('notify', ['seen' => true], ['parent-uri-id' => $item['parent-uri-id'], 'uid' => local_user()]);
|
||||
}
|
||||
|
||||
// We are displaying an "alternate" link if that post was public. See issue 2864
|
||||
$is_public = Item::exists(['id' => $item_id, 'private' => [Item::PUBLIC, Item::UNLISTED]]);
|
||||
if ($is_public) {
|
||||
|
|
@ -281,7 +287,7 @@ function display_content(App $a, $update = false, $update_uid = 0)
|
|||
}
|
||||
|
||||
// We need the editor here to be able to reshare an item.
|
||||
if ($is_owner) {
|
||||
if ($is_owner && !$update) {
|
||||
$x = [
|
||||
'is_owner' => true,
|
||||
'allow_location' => $a->user['allow_location'],
|
||||
|
|
@ -304,7 +310,7 @@ function display_content(App $a, $update = false, $update_uid = 0)
|
|||
$unseen = false;
|
||||
}
|
||||
|
||||
if ($update && !$unseen) {
|
||||
if ($update && !$unseen && !$force) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,14 +35,14 @@ function editpost_content(App $a)
|
|||
$o = '';
|
||||
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = (($a->argc > 1) ? intval($a->argv[1]) : 0);
|
||||
|
||||
if (!$post_id) {
|
||||
notice(DI::l10n()->t('Item not found') . EOL);
|
||||
notice(DI::l10n()->t('Item not found'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ function editpost_content(App $a)
|
|||
$item = Item::selectFirstForUser(local_user(), $fields, ['id' => $post_id, 'uid' => local_user()]);
|
||||
|
||||
if (!DBA::isResult($item)) {
|
||||
notice(DI::l10n()->t('Item not found') . EOL);
|
||||
notice(DI::l10n()->t('Item not found'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +66,8 @@ function editpost_content(App $a)
|
|||
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
|
||||
'$ispublic' => ' ', // DI::l10n()->t('Visible to <strong>everybody</strong>'),
|
||||
'$geotag' => $geotag,
|
||||
'$nickname' => $a->user['nickname']
|
||||
'$nickname' => $a->user['nickname'],
|
||||
'$is_mobile' => DI::mode()->isMobile(),
|
||||
]);
|
||||
|
||||
if (strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) {
|
||||
|
|
@ -131,7 +132,7 @@ function editpost_content(App $a)
|
|||
//jot nav tab (used in some themes)
|
||||
'$message' => DI::l10n()->t('Message'),
|
||||
'$browser' => DI::l10n()->t('Browser'),
|
||||
'$shortpermset' => DI::l10n()->t('permissions'),
|
||||
'$shortpermset' => DI::l10n()->t('Permissions'),
|
||||
|
||||
'$compose_link_title' => DI::l10n()->t('Open Compose page'),
|
||||
]);
|
||||
|
|
@ -145,7 +146,7 @@ function undo_post_tagging($s) {
|
|||
if ($cnt) {
|
||||
foreach ($matches as $mtch) {
|
||||
if (in_array($mtch[1], ['!', '@'])) {
|
||||
$contact = Contact::getDetailsByURL($mtch[2]);
|
||||
$contact = Contact::getByURL($mtch[2], false, ['addr']);
|
||||
$mtch[3] = empty($contact['addr']) ? $mtch[2] : $contact['addr'];
|
||||
}
|
||||
$s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);
|
||||
|
|
|
|||
|
|
@ -25,11 +25,13 @@ use Friendica\Content\Nav;
|
|||
use Friendica\Content\Widget\CalendarExport;
|
||||
use Friendica\Core\ACL;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Theme;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Conversation;
|
||||
use Friendica\Model\Event;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\User;
|
||||
|
|
@ -65,9 +67,7 @@ function events_init(App $a)
|
|||
|
||||
function events_post(App $a)
|
||||
{
|
||||
|
||||
Logger::log('post: ' . print_r($_REQUEST, true), Logger::DATA);
|
||||
|
||||
Logger::debug('post', ['request' => $_REQUEST]);
|
||||
if (!local_user()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -82,6 +82,8 @@ function events_post(App $a)
|
|||
$adjust = intval($_POST['adjust'] ?? 0);
|
||||
$nofinish = intval($_POST['nofinish'] ?? 0);
|
||||
|
||||
$share = intval($_POST['share'] ?? 0);
|
||||
|
||||
// The default setting for the `private` field in event_store() is false, so mirror that
|
||||
$private_event = false;
|
||||
|
||||
|
|
@ -129,10 +131,10 @@ function events_post(App $a)
|
|||
];
|
||||
|
||||
$action = ($event_id == '') ? 'new' : 'event/' . $event_id;
|
||||
$onerror_path = 'events/' . $action . '?' . http_build_query($params, null, null, PHP_QUERY_RFC3986);
|
||||
$onerror_path = 'events/' . $action . '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
|
||||
|
||||
if (strcmp($finish, $start) < 0 && !$nofinish) {
|
||||
notice(DI::l10n()->t('Event can not end before it has started.') . EOL);
|
||||
notice(DI::l10n()->t('Event can not end before it has started.'));
|
||||
if (intval($_REQUEST['preview'])) {
|
||||
echo DI::l10n()->t('Event can not end before it has started.');
|
||||
exit();
|
||||
|
|
@ -141,7 +143,7 @@ function events_post(App $a)
|
|||
}
|
||||
|
||||
if (!$summary || ($start === DBA::NULL_DATETIME)) {
|
||||
notice(DI::l10n()->t('Event title and start time are required.') . EOL);
|
||||
notice(DI::l10n()->t('Event title and start time are required.'));
|
||||
if (intval($_REQUEST['preview'])) {
|
||||
echo DI::l10n()->t('Event title and start time are required.');
|
||||
exit();
|
||||
|
|
@ -149,45 +151,42 @@ function events_post(App $a)
|
|||
DI::baseUrl()->redirect($onerror_path);
|
||||
}
|
||||
|
||||
$share = intval($_POST['share'] ?? 0);
|
||||
|
||||
$c = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
|
||||
intval(local_user())
|
||||
);
|
||||
|
||||
if (DBA::isResult($c)) {
|
||||
$self = $c[0]['id'];
|
||||
} else {
|
||||
$self = 0;
|
||||
}
|
||||
$self = \Friendica\Model\Contact::getPublicIdByUserId($uid);
|
||||
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
|
||||
if ($share) {
|
||||
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
|
||||
$str_group_allow = $aclFormatter->toString($_POST['group_allow'] ?? '');
|
||||
$str_contact_allow = $aclFormatter->toString($_POST['contact_allow'] ?? '');
|
||||
$str_group_deny = $aclFormatter->toString($_POST['group_deny'] ?? '');
|
||||
$str_contact_deny = $aclFormatter->toString($_POST['contact_deny'] ?? '');
|
||||
|
||||
// Undo the pseudo-contact of self, since there are real contacts now
|
||||
if (strpos($str_contact_allow, '<' . $self . '>') !== false) {
|
||||
$str_contact_allow = str_replace('<' . $self . '>', '', $str_contact_allow);
|
||||
$user = User::getById($uid, ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']);
|
||||
if (!DBA::isResult($user)) {
|
||||
return;
|
||||
}
|
||||
// Make sure to set the `private` field as true. This is necessary to
|
||||
// have the posts show up correctly in Diaspora if an event is created
|
||||
// as visible only to self at first, but then edited to display to others.
|
||||
if (strlen($str_group_allow) || strlen($str_contact_allow) || strlen($str_group_deny) || strlen($str_contact_deny)) {
|
||||
$private_event = true;
|
||||
|
||||
$str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $user['allow_cid'] ?? '';
|
||||
$str_group_allow = isset($_REQUEST['group_allow']) ? $aclFormatter->toString($_REQUEST['group_allow']) : $user['allow_gid'] ?? '';
|
||||
$str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $user['deny_cid'] ?? '';
|
||||
$str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $user['deny_gid'] ?? '';
|
||||
|
||||
$visibility = $_REQUEST['visibility'] ?? '';
|
||||
if ($visibility === 'public') {
|
||||
// The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected
|
||||
$str_contact_allow = $str_group_allow = $str_contact_deny = $str_group_deny = '';
|
||||
} else if ($visibility === 'custom') {
|
||||
// Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL
|
||||
// case that would make it public. So we always append the author's contact id to the allowed contacts.
|
||||
// See https://github.com/friendica/friendica/issues/9672
|
||||
$str_contact_allow .= $aclFormatter->toString($self);
|
||||
}
|
||||
} else {
|
||||
// Note: do not set `private` field for self-only events. It will
|
||||
// keep even you from seeing them!
|
||||
$str_contact_allow = '<' . $self . '>';
|
||||
$str_contact_allow = $aclFormatter->toString($self);
|
||||
$str_group_allow = $str_contact_deny = $str_group_deny = '';
|
||||
}
|
||||
|
||||
// Make sure to set the `private` field as true. This is necessary to
|
||||
// have the posts show up correctly in Diaspora if an event is created
|
||||
// as visible only to self at first, but then edited to display to others.
|
||||
if (strlen($str_group_allow) || strlen($str_contact_allow) || strlen($str_group_deny) || strlen($str_contact_deny)) {
|
||||
$private_event = true;
|
||||
}
|
||||
|
||||
$datarray = [];
|
||||
$datarray['start'] = $start;
|
||||
|
|
@ -206,6 +205,9 @@ function events_post(App $a)
|
|||
$datarray['deny_gid'] = $str_group_deny;
|
||||
$datarray['private'] = $private_event;
|
||||
$datarray['id'] = $event_id;
|
||||
$datarray['network'] = Protocol::DFRN;
|
||||
$datarray['protocol'] = Conversation::PARCEL_DIRECT;
|
||||
$datarray['direction'] = Conversation::PUSH;
|
||||
|
||||
if (intval($_REQUEST['preview'])) {
|
||||
$html = Event::getHTML($datarray);
|
||||
|
|
@ -225,7 +227,7 @@ function events_post(App $a)
|
|||
function events_content(App $a)
|
||||
{
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return Login::form();
|
||||
}
|
||||
|
||||
|
|
@ -256,6 +258,11 @@ function events_content(App $a)
|
|||
// get the translation strings for the callendar
|
||||
$i18n = Event::getStrings();
|
||||
|
||||
DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.min.css');
|
||||
DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.print.min.css', 'print');
|
||||
DI::page()->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js');
|
||||
DI::page()->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js');
|
||||
|
||||
$htpl = Renderer::getMarkupTemplate('event_head.tpl');
|
||||
DI::page()['htmlhead'] .= Renderer::replaceMacros($htpl, [
|
||||
'$module_url' => '/events',
|
||||
|
|
@ -469,16 +476,16 @@ function events_content(App $a)
|
|||
$t_orig = $orig_event['summary'] ?? '';
|
||||
$d_orig = $orig_event['desc'] ?? '';
|
||||
$l_orig = $orig_event['location'] ?? '';
|
||||
$eid = !empty($orig_event) ? $orig_event['id'] : 0;
|
||||
$cid = !empty($orig_event) ? $orig_event['cid'] : 0;
|
||||
$uri = !empty($orig_event) ? $orig_event['uri'] : '';
|
||||
$eid = $orig_event['id'] ?? 0;
|
||||
$cid = $orig_event['cid'] ?? 0;
|
||||
$uri = $orig_event['uri'] ?? '';
|
||||
|
||||
if ($cid || $mode === 'edit') {
|
||||
$share_disabled = 'disabled="disabled"';
|
||||
}
|
||||
|
||||
$sdt = !empty($orig_event) ? $orig_event['start'] : 'now';
|
||||
$fdt = !empty($orig_event) ? $orig_event['finish'] : 'now';
|
||||
$sdt = $orig_event['start'] ?? 'now';
|
||||
$fdt = $orig_event['finish'] ?? 'now';
|
||||
|
||||
$tz = date_default_timezone_get();
|
||||
if (!empty($orig_event)) {
|
||||
|
|
@ -583,9 +590,7 @@ function events_content(App $a)
|
|||
}
|
||||
|
||||
if (Item::exists(['id' => $ev[0]['itemid']])) {
|
||||
notice(DI::l10n()->t('Failed to remove event') . EOL);
|
||||
} else {
|
||||
info(DI::l10n()->t('Event removed') . EOL);
|
||||
notice(DI::l10n()->t('Failed to remove event'));
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect('events');
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use Friendica\App;
|
|||
use Friendica\Core\Renderer;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Photo;
|
||||
use Friendica\Util\Images;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
|
|
@ -47,8 +48,8 @@ function fbrowser_content(App $a)
|
|||
if ($a->argc==2) {
|
||||
$photos = q("SELECT distinct(`album`) AS `album` FROM `photo` WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' ",
|
||||
intval(local_user()),
|
||||
DBA::escape('Contact Photos'),
|
||||
DBA::escape(DI::l10n()->t('Contact Photos'))
|
||||
DBA::escape(Photo::CONTACT_PHOTOS),
|
||||
DBA::escape(DI::l10n()->t(Photo::CONTACT_PHOTOS))
|
||||
);
|
||||
|
||||
$albums = array_column($photos, 'album');
|
||||
|
|
@ -66,8 +67,8 @@ function fbrowser_content(App $a)
|
|||
FROM `photo` WHERE `uid` = %d $sql_extra AND `album` != '%s' AND `album` != '%s'
|
||||
GROUP BY `resource-id` $sql_extra2",
|
||||
intval(local_user()),
|
||||
DBA::escape('Contact Photos'),
|
||||
DBA::escape(DI::l10n()->t('Contact Photos'))
|
||||
DBA::escape(Photo::CONTACT_PHOTOS),
|
||||
DBA::escape(DI::l10n()->t(Photo::CONTACT_PHOTOS))
|
||||
);
|
||||
|
||||
function _map_files1($rr)
|
||||
|
|
|
|||
157
mod/follow.php
|
|
@ -28,6 +28,7 @@ use Friendica\Model\Profile;
|
|||
use Friendica\Model\Item;
|
||||
use Friendica\Network\Probe;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
function follow_post(App $a)
|
||||
|
|
@ -40,32 +41,9 @@ function follow_post(App $a)
|
|||
DI::baseUrl()->redirect('contact');
|
||||
}
|
||||
|
||||
$uid = local_user();
|
||||
$url = Probe::cleanURI($_REQUEST['url']);
|
||||
$return_path = 'follow?url=' . urlencode($url);
|
||||
|
||||
// Makes the connection request for friendica contacts easier
|
||||
// This is just a precaution if maybe this page is called somewhere directly via POST
|
||||
$_SESSION['fastlane'] = $url;
|
||||
|
||||
$result = Contact::createFromProbe($uid, $url, true);
|
||||
|
||||
if ($result['success'] == false) {
|
||||
// Possibly it is a remote item and not an account
|
||||
follow_remote_item($url);
|
||||
|
||||
if ($result['message']) {
|
||||
notice($result['message']);
|
||||
}
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
} elseif ($result['cid']) {
|
||||
DI::baseUrl()->redirect('contact/' . $result['cid']);
|
||||
}
|
||||
|
||||
info(DI::l10n()->t('The contact could not be added.'));
|
||||
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
// NOTREACHED
|
||||
follow_process($a, $url);
|
||||
}
|
||||
|
||||
function follow_content(App $a)
|
||||
|
|
@ -95,88 +73,73 @@ function follow_content(App $a)
|
|||
$submit = DI::l10n()->t('Submit Request');
|
||||
|
||||
// Don't try to add a pending contact
|
||||
$r = q("SELECT `pending` FROM `contact` WHERE `uid` = %d AND ((`rel` != %d) OR (`network` = '%s')) AND
|
||||
(`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s') AND
|
||||
`network` != '%s' LIMIT 1",
|
||||
intval(local_user()), DBA::escape(Contact::FOLLOWER), DBA::escape(Protocol::DFRN), DBA::escape(Strings::normaliseLink($url)),
|
||||
DBA::escape(Strings::normaliseLink($url)), DBA::escape($url), DBA::escape(Protocol::STATUSNET));
|
||||
$user_contact = DBA::selectFirst('contact', ['pending'], ["`uid` = ? AND ((`rel` != ?) OR (`network` = ?)) AND
|
||||
(`nurl` = ? OR `alias` = ? OR `alias` = ?) AND `network` != ?",
|
||||
$uid, Contact::FOLLOWER, Protocol::DFRN, Strings::normaliseLink($url),
|
||||
Strings::normaliseLink($url), $url, Protocol::STATUSNET]);
|
||||
|
||||
if ($r) {
|
||||
if ($r[0]['pending']) {
|
||||
if (DBA::isResult($user_contact)) {
|
||||
if ($user_contact['pending']) {
|
||||
notice(DI::l10n()->t('You already added this contact.'));
|
||||
$submit = '';
|
||||
//$a->internalRedirect($_SESSION['return_path']);
|
||||
// NOTREACHED
|
||||
}
|
||||
}
|
||||
|
||||
$ret = Probe::uri($url);
|
||||
$contact = Contact::getByURL($url, true);
|
||||
|
||||
$protocol = Contact::getProtocol($ret['url'], $ret['network']);
|
||||
|
||||
if (($protocol == Protocol::DIASPORA) && !DI::config()->get('system', 'diaspora_enabled')) {
|
||||
notice(DI::l10n()->t("Diaspora support isn't enabled. Contact can't be added."));
|
||||
$submit = '';
|
||||
//$a->internalRedirect($_SESSION['return_path']);
|
||||
// NOTREACHED
|
||||
// Possibly it is a mail contact
|
||||
if (empty($contact)) {
|
||||
$contact = Probe::uri($url, Protocol::MAIL, $uid);
|
||||
}
|
||||
|
||||
if (($protocol == Protocol::OSTATUS) && DI::config()->get('system', 'ostatus_disabled')) {
|
||||
notice(DI::l10n()->t("OStatus support is disabled. Contact can't be added."));
|
||||
$submit = '';
|
||||
//$a->internalRedirect($_SESSION['return_path']);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
if ($protocol == Protocol::PHANTOM) {
|
||||
if (empty($contact) || ($contact['network'] == Protocol::PHANTOM)) {
|
||||
// Possibly it is a remote item and not an account
|
||||
follow_remote_item($url);
|
||||
|
||||
notice(DI::l10n()->t("The network type couldn't be detected. Contact can't be added."));
|
||||
$submit = '';
|
||||
//$a->internalRedirect($_SESSION['return_path']);
|
||||
// NOTREACHED
|
||||
$contact = ['url' => $url, 'network' => Protocol::PHANTOM, 'name' => $url, 'keywords' => ''];
|
||||
}
|
||||
|
||||
$protocol = Contact::getProtocol($contact['url'], $contact['network']);
|
||||
|
||||
if (($protocol == Protocol::DIASPORA) && !DI::config()->get('system', 'diaspora_enabled')) {
|
||||
notice(DI::l10n()->t("Diaspora support isn't enabled. Contact can't be added."));
|
||||
$submit = '';
|
||||
}
|
||||
|
||||
if (($protocol == Protocol::OSTATUS) && DI::config()->get('system', 'ostatus_disabled')) {
|
||||
notice(DI::l10n()->t("OStatus support is disabled. Contact can't be added."));
|
||||
$submit = '';
|
||||
}
|
||||
|
||||
if ($protocol == Protocol::MAIL) {
|
||||
$ret['url'] = $ret['addr'];
|
||||
$contact['url'] = $contact['addr'];
|
||||
}
|
||||
|
||||
if (($protocol === Protocol::DFRN) && !DBA::isResult($r)) {
|
||||
$request = $ret['request'];
|
||||
if (($protocol === Protocol::DFRN) && !DBA::isResult($contact)) {
|
||||
$request = $contact['request'];
|
||||
$tpl = Renderer::getMarkupTemplate('dfrn_request.tpl');
|
||||
} else {
|
||||
if (!empty($_REQUEST['auto'])) {
|
||||
follow_process($a, $contact['url']);
|
||||
}
|
||||
|
||||
$request = DI::baseUrl() . '/follow';
|
||||
$tpl = Renderer::getMarkupTemplate('auto_request.tpl');
|
||||
}
|
||||
|
||||
$r = q("SELECT `url` FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1", intval($uid));
|
||||
|
||||
if (!$r) {
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if (empty($owner)) {
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
$myaddr = $r[0]['url'];
|
||||
$gcontact_id = 0;
|
||||
$myaddr = $owner['url'];
|
||||
|
||||
// Makes the connection request for friendica contacts easier
|
||||
$_SESSION['fastlane'] = $ret['url'];
|
||||
|
||||
$r = q("SELECT `id`, `location`, `about`, `keywords` FROM `gcontact` WHERE `nurl` = '%s'",
|
||||
Strings::normaliseLink($ret['url']));
|
||||
|
||||
if (!$r) {
|
||||
$r = [['location' => '', 'about' => '', 'keywords' => '']];
|
||||
} else {
|
||||
$gcontact_id = $r[0]['id'];
|
||||
}
|
||||
|
||||
if ($protocol === Protocol::DIASPORA) {
|
||||
$r[0]['location'] = '';
|
||||
$r[0]['about'] = '';
|
||||
}
|
||||
$_SESSION['fastlane'] = $contact['url'];
|
||||
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$header' => DI::l10n()->t('Connect/Follow'),
|
||||
|
|
@ -188,35 +151,59 @@ function follow_content(App $a)
|
|||
'$cancel' => DI::l10n()->t('Cancel'),
|
||||
|
||||
'$request' => $request,
|
||||
'$name' => $ret['name'],
|
||||
'$url' => $ret['url'],
|
||||
'$zrl' => Profile::zrl($ret['url']),
|
||||
'$name' => $contact['name'],
|
||||
'$url' => $contact['url'],
|
||||
'$zrl' => Profile::zrl($contact['url']),
|
||||
'$myaddr' => $myaddr,
|
||||
'$keywords' => $r[0]['keywords'],
|
||||
'$keywords' => $contact['keywords'],
|
||||
|
||||
'$does_know_you' => ['knowyou', DI::l10n()->t('%s knows you', $ret['name'])],
|
||||
'$does_know_you' => ['knowyou', DI::l10n()->t('%s knows you', $contact['name'])],
|
||||
'$addnote_field' => ['dfrn-request-message', DI::l10n()->t('Add a personal note:')],
|
||||
]);
|
||||
|
||||
DI::page()['aside'] = '';
|
||||
|
||||
$profiledata = Contact::getDetailsByURL($ret['url']);
|
||||
if ($profiledata) {
|
||||
Profile::load($a, '', $profiledata, false);
|
||||
}
|
||||
if ($protocol != Protocol::PHANTOM) {
|
||||
Profile::load($a, '', $contact, false);
|
||||
|
||||
if ($gcontact_id <> 0) {
|
||||
$o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'),
|
||||
['$title' => DI::l10n()->t('Status Messages and Posts')]
|
||||
);
|
||||
|
||||
// Show last public posts
|
||||
$o .= Contact::getPostsFromUrl($ret['url']);
|
||||
$o .= Contact::getPostsFromUrl($contact['url']);
|
||||
}
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
function follow_process(App $a, string $url)
|
||||
{
|
||||
$return_path = 'follow?url=' . urlencode($url);
|
||||
|
||||
// Makes the connection request for friendica contacts easier
|
||||
// This is just a precaution if maybe this page is called somewhere directly via POST
|
||||
$_SESSION['fastlane'] = $url;
|
||||
|
||||
$result = Contact::createFromProbe($a->user, $url, true);
|
||||
|
||||
if ($result['success'] == false) {
|
||||
// Possibly it is a remote item and not an account
|
||||
follow_remote_item($url);
|
||||
|
||||
if ($result['message']) {
|
||||
notice($result['message']);
|
||||
}
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
} elseif ($result['cid']) {
|
||||
DI::baseUrl()->redirect('contact/' . $result['cid']);
|
||||
}
|
||||
|
||||
notice(DI::l10n()->t('The contact could not be added.'));
|
||||
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
}
|
||||
|
||||
function follow_remote_item($url)
|
||||
{
|
||||
$item_id = Item::fetchByLink($url, local_user());
|
||||
|
|
|
|||
449
mod/item.php
|
|
@ -29,6 +29,8 @@
|
|||
*/
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Content\Item as ItemHelper;
|
||||
use Friendica\Content\PageInfo;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
|
|
@ -43,20 +45,20 @@ use Friendica\Model\Contact;
|
|||
use Friendica\Model\Conversation;
|
||||
use Friendica\Model\FileTag;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Notify;
|
||||
use Friendica\Model\Notify\Type;
|
||||
use Friendica\Model\Photo;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Object\EMail\ItemCCEMail;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Protocol\Diaspora;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Security;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Security\Security;
|
||||
use Friendica\Worker\Delivery;
|
||||
|
||||
require_once __DIR__ . '/../include/items.php';
|
||||
|
||||
function item_post(App $a) {
|
||||
if (!Session::isAuthenticated()) {
|
||||
throw new HTTPException\ForbiddenException();
|
||||
|
|
@ -66,7 +68,10 @@ function item_post(App $a) {
|
|||
|
||||
if (!empty($_REQUEST['dropitems'])) {
|
||||
$arr_drop = explode(',', $_REQUEST['dropitems']);
|
||||
drop_items($arr_drop);
|
||||
foreach ($arr_drop as $item) {
|
||||
Item::deleteForUser(['id' => $item], $uid);
|
||||
}
|
||||
|
||||
$json = ['success' => 1];
|
||||
System::jsonExit($json);
|
||||
}
|
||||
|
|
@ -77,8 +82,6 @@ function item_post(App $a) {
|
|||
|
||||
$api_source = $_REQUEST['api_source'] ?? false;
|
||||
|
||||
$message_id = ((!empty($_REQUEST['message_id']) && $api_source) ? strip_tags($_REQUEST['message_id']) : '');
|
||||
|
||||
$return_path = $_REQUEST['return'] ?? '';
|
||||
$preview = intval($_REQUEST['preview'] ?? 0);
|
||||
|
||||
|
|
@ -97,36 +100,32 @@ function item_post(App $a) {
|
|||
}
|
||||
|
||||
// Is this a reply to something?
|
||||
$toplevel_item_id = intval($_REQUEST['parent'] ?? 0);
|
||||
$parent_item_id = intval($_REQUEST['parent'] ?? 0);
|
||||
$thr_parent_uri = trim($_REQUEST['parent_uri'] ?? '');
|
||||
|
||||
$thread_parent_uriid = 0;
|
||||
$thread_parent_contact = null;
|
||||
|
||||
$parent_item = null;
|
||||
$toplevel_item = null;
|
||||
$parent_user = null;
|
||||
|
||||
$parent_contact = null;
|
||||
$toplevel_item_id = 0;
|
||||
$toplevel_user_id = null;
|
||||
|
||||
$objecttype = null;
|
||||
$profile_uid = ($_REQUEST['profile_uid'] ?? 0) ?: local_user();
|
||||
$posttype = ($_REQUEST['post_type'] ?? '') ?: Item::PT_ARTICLE;
|
||||
|
||||
if ($toplevel_item_id || $thr_parent_uri) {
|
||||
if ($toplevel_item_id) {
|
||||
$toplevel_item = Item::selectFirst([], ['id' => $toplevel_item_id]);
|
||||
if ($parent_item_id || $thr_parent_uri) {
|
||||
if ($parent_item_id) {
|
||||
$parent_item = Item::selectFirst([], ['id' => $parent_item_id]);
|
||||
} elseif ($thr_parent_uri) {
|
||||
$toplevel_item = Item::selectFirst([], ['uri' => $thr_parent_uri, 'uid' => $profile_uid]);
|
||||
$parent_item = Item::selectFirst([], ['uri' => $thr_parent_uri, 'uid' => $profile_uid]);
|
||||
}
|
||||
|
||||
// if this isn't the top-level parent of the conversation, find it
|
||||
if (DBA::isResult($toplevel_item)) {
|
||||
if (DBA::isResult($parent_item)) {
|
||||
// The URI and the contact is taken from the direct parent which needn't to be the top parent
|
||||
$thread_parent_uriid = $toplevel_item['uri-id'];
|
||||
$thr_parent_uri = $toplevel_item['uri'];
|
||||
$thread_parent_contact = Contact::getDetailsByURL($toplevel_item["author-link"]);
|
||||
$thr_parent_uri = $parent_item['uri'];
|
||||
$toplevel_item = $parent_item;
|
||||
|
||||
if ($toplevel_item['id'] != $toplevel_item['parent']) {
|
||||
if ($parent_item['gravity'] != GRAVITY_PARENT) {
|
||||
$toplevel_item = Item::selectFirst([], ['id' => $toplevel_item['parent']]);
|
||||
}
|
||||
}
|
||||
|
|
@ -139,8 +138,18 @@ function item_post(App $a) {
|
|||
throw new HTTPException\NotFoundException(DI::l10n()->t('Unable to locate original post.'));
|
||||
}
|
||||
|
||||
// When commenting on a public post then store the post for the current user
|
||||
// This enables interaction like starring and saving into folders
|
||||
if ($toplevel_item['uid'] == 0) {
|
||||
$stored = Item::storeForUserByUriId($toplevel_item['uri-id'], local_user());
|
||||
Logger::info('Public item stored for user', ['uri-id' => $toplevel_item['uri-id'], 'uid' => $uid, 'stored' => $stored]);
|
||||
if ($stored) {
|
||||
$toplevel_item = Item::selectFirst([], ['id' => $stored]);
|
||||
}
|
||||
}
|
||||
|
||||
$toplevel_item_id = $toplevel_item['id'];
|
||||
$parent_user = $toplevel_item['uid'];
|
||||
$toplevel_user_id = $toplevel_item['uid'];
|
||||
|
||||
$objecttype = Activity\ObjectType::COMMENT;
|
||||
}
|
||||
|
|
@ -162,16 +171,8 @@ function item_post(App $a) {
|
|||
}
|
||||
|
||||
// Ensure that the user id in a thread always stay the same
|
||||
if (!is_null($parent_user) && in_array($parent_user, [local_user(), 0])) {
|
||||
$profile_uid = $parent_user;
|
||||
}
|
||||
|
||||
// Check for multiple posts with the same message id (when the post was created via API)
|
||||
if (($message_id != '') && ($profile_uid != 0)) {
|
||||
if (Item::exists(['uri' => $message_id, 'uid' => $profile_uid])) {
|
||||
Logger::info('Message already exists for user', ['uri' => $message_id, 'uid' => $profile_uid]);
|
||||
return 0;
|
||||
}
|
||||
if (!is_null($toplevel_user_id) && in_array($toplevel_user_id, [local_user(), 0])) {
|
||||
$profile_uid = $toplevel_user_id;
|
||||
}
|
||||
|
||||
// Allow commenting if it is an answer to a public post
|
||||
|
|
@ -195,8 +196,7 @@ function item_post(App $a) {
|
|||
$orig_post = Item::selectFirst(Item::ITEM_FIELDLIST, ['id' => $post_id]);
|
||||
}
|
||||
|
||||
$user = DBA::selectFirst('user', [], ['uid' => $profile_uid]);
|
||||
|
||||
$user = User::getById($profile_uid, ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']);
|
||||
if (!DBA::isResult($user) && !$toplevel_item_id) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -235,7 +235,7 @@ function item_post(App $a) {
|
|||
];
|
||||
}
|
||||
|
||||
$att_bbcode = add_page_info_data($attachment);
|
||||
$att_bbcode = "\n" . PageInfo::getFooterFromData($attachment);
|
||||
$body .= $att_bbcode;
|
||||
}
|
||||
|
||||
|
|
@ -252,8 +252,8 @@ function item_post(App $a) {
|
|||
$verb = $orig_post['verb'];
|
||||
$objecttype = $orig_post['object-type'];
|
||||
$app = $orig_post['app'];
|
||||
$categories = $orig_post['file'];
|
||||
$title = Strings::escapeTags(trim($_REQUEST['title']));
|
||||
$categories = $orig_post['file'] ?? '';
|
||||
$title = trim($_REQUEST['title'] ?? '');
|
||||
$body = trim($body);
|
||||
$private = $orig_post['private'];
|
||||
$pubmail_enabled = $orig_post['pubmail'];
|
||||
|
|
@ -261,26 +261,30 @@ function item_post(App $a) {
|
|||
$guid = $orig_post['guid'];
|
||||
$extid = $orig_post['extid'];
|
||||
} else {
|
||||
$str_contact_allow = '';
|
||||
$str_group_allow = '';
|
||||
$str_contact_deny = '';
|
||||
$str_group_deny = '';
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
$str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $user['allow_cid'] ?? '';
|
||||
$str_group_allow = isset($_REQUEST['group_allow']) ? $aclFormatter->toString($_REQUEST['group_allow']) : $user['allow_gid'] ?? '';
|
||||
$str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $user['deny_cid'] ?? '';
|
||||
$str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $user['deny_gid'] ?? '';
|
||||
|
||||
if (($_REQUEST['visibility'] ?? '') !== 'public') {
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
$str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $user['allow_cid'] ?? '';
|
||||
$str_group_allow = isset($_REQUEST['group_allow']) ? $aclFormatter->toString($_REQUEST['group_allow']) : $user['allow_gid'] ?? '';
|
||||
$str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $user['deny_cid'] ?? '';
|
||||
$str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $user['deny_gid'] ?? '';
|
||||
$visibility = $_REQUEST['visibility'] ?? '';
|
||||
if ($visibility === 'public') {
|
||||
// The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected
|
||||
$str_contact_allow = $str_group_allow = $str_contact_deny = $str_group_deny = '';
|
||||
} else if ($visibility === 'custom') {
|
||||
// Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL
|
||||
// case that would make it public. So we always append the author's contact id to the allowed contacts.
|
||||
// See https://github.com/friendica/friendica/issues/9672
|
||||
$str_contact_allow .= $aclFormatter->toString(Contact::getPublicIdByUserId($uid));
|
||||
}
|
||||
|
||||
$title = Strings::escapeTags(trim($_REQUEST['title'] ?? ''));
|
||||
$location = Strings::escapeTags(trim($_REQUEST['location'] ?? ''));
|
||||
$coord = Strings::escapeTags(trim($_REQUEST['coord'] ?? ''));
|
||||
$verb = Strings::escapeTags(trim($_REQUEST['verb'] ?? ''));
|
||||
$emailcc = Strings::escapeTags(trim($_REQUEST['emailcc'] ?? ''));
|
||||
$title = trim($_REQUEST['title'] ?? '');
|
||||
$location = trim($_REQUEST['location'] ?? '');
|
||||
$coord = trim($_REQUEST['coord'] ?? '');
|
||||
$verb = trim($_REQUEST['verb'] ?? '');
|
||||
$emailcc = trim($_REQUEST['emailcc'] ?? '');
|
||||
$body = trim($body);
|
||||
$network = Strings::escapeTags(trim(($_REQUEST['network'] ?? '') ?: Protocol::DFRN));
|
||||
$network = trim(($_REQUEST['network'] ?? '') ?: Protocol::DFRN);
|
||||
$guid = System::createUUID();
|
||||
|
||||
$postopts = $_REQUEST['postopts'] ?? '';
|
||||
|
|
@ -326,7 +330,7 @@ function item_post(App $a) {
|
|||
System::jsonExit(['preview' => '']);
|
||||
}
|
||||
|
||||
info(DI::l10n()->t('Empty post discarded.'));
|
||||
notice(DI::l10n()->t('Empty post discarded.'));
|
||||
if ($return_path) {
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
}
|
||||
|
|
@ -369,27 +373,23 @@ function item_post(App $a) {
|
|||
|
||||
// get contact info for owner
|
||||
if ($profile_uid == local_user() || $allow_comment) {
|
||||
$contact_record = $author;
|
||||
$contact_record = $author ?: [];
|
||||
} else {
|
||||
$contact_record = DBA::selectFirst('contact', [], ['uid' => $profile_uid, 'self' => true]);
|
||||
$contact_record = DBA::selectFirst('contact', [], ['uid' => $profile_uid, 'self' => true]) ?: [];
|
||||
}
|
||||
|
||||
// Look for any tags and linkify them
|
||||
$inform = '';
|
||||
|
||||
$tags = BBCode::getTags($body);
|
||||
|
||||
if ($thread_parent_uriid && !\Friendica\Content\Feature::isEnabled($uid, 'explicit_mentions')) {
|
||||
$tags = item_add_implicit_mentions($tags, $thread_parent_contact, $thread_parent_uriid);
|
||||
}
|
||||
|
||||
$tagged = [];
|
||||
|
||||
$private_forum = false;
|
||||
$private_id = null;
|
||||
$only_to_forum = false;
|
||||
$forum_contact = [];
|
||||
|
||||
if (count($tags)) {
|
||||
$body = BBCode::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network, $str_contact_allow, &$inform, &$private_forum, &$private_id, &$only_to_forum, &$forum_contact) {
|
||||
$tags = BBCode::getTags($body);
|
||||
|
||||
$tagged = [];
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$tag_type = substr($tag, 0, 1);
|
||||
|
||||
|
|
@ -397,45 +397,39 @@ function item_post(App $a) {
|
|||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we already tagged 'Robert Johnson', don't try and tag 'Robert'.
|
||||
/* If we already tagged 'Robert Johnson', don't try and tag 'Robert'.
|
||||
* Robert Johnson should be first in the $tags array
|
||||
*/
|
||||
$fullnametagged = false;
|
||||
/// @TODO $tagged is initialized above if () block and is not filled, maybe old-lost code?
|
||||
foreach ($tagged as $nextTag) {
|
||||
if (stristr($nextTag, $tag . ' ')) {
|
||||
$fullnametagged = true;
|
||||
break;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if ($fullnametagged) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$success = handle_tag($body, $inform, local_user() ? local_user() : $profile_uid, $tag, $network);
|
||||
$success = ItemHelper::replaceTag($body, $inform, local_user() ? local_user() : $profile_uid, $tag, $network);
|
||||
if ($success['replaced']) {
|
||||
$tagged[] = $tag;
|
||||
}
|
||||
// When the forum is private or the forum is addressed with a "!" make the post private
|
||||
if (is_array($success['contact']) && (!empty($success['contact']['prv']) || ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]))) {
|
||||
if (!empty($success['contact']['prv']) || ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) {
|
||||
$private_forum = $success['contact']['prv'];
|
||||
$only_to_forum = ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
|
||||
$private_id = $success['contact']['id'];
|
||||
$forum_contact = $success['contact'];
|
||||
} elseif (is_array($success['contact']) && !empty($success['contact']['forum']) &&
|
||||
($str_contact_allow == '<' . $success['contact']['id'] . '>')) {
|
||||
} elseif (!empty($success['contact']['forum']) && ($str_contact_allow == '<' . $success['contact']['id'] . '>')) {
|
||||
$private_forum = false;
|
||||
$only_to_forum = true;
|
||||
$private_id = $success['contact']['id'];
|
||||
$forum_contact = $success['contact'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$original_contact_id = $contact_id;
|
||||
|
||||
if (!$toplevel_item_id && count($forum_contact) && ($private_forum || $only_to_forum)) {
|
||||
if (!$toplevel_item_id && !empty($forum_contact) && ($private_forum || $only_to_forum)) {
|
||||
// we tagged a forum in a top level post. Now we change the post
|
||||
$private = $private_forum;
|
||||
|
||||
|
|
@ -535,9 +529,8 @@ function item_post(App $a) {
|
|||
if (strlen($attachments)) {
|
||||
$attachments .= ',';
|
||||
}
|
||||
$attachments .= '[attach]href="' . DI::baseUrl() . '/attach/' . $attachment['id'] .
|
||||
'" length="' . $attachment['filesize'] . '" type="' . $attachment['filetype'] .
|
||||
'" title="' . ($attachment['filename'] ? $attachment['filename'] : '') . '"[/attach]';
|
||||
$attachments .= Post\Media::getAttachElement(DI::baseUrl() . '/attach/' . $attachment['id'],
|
||||
$attachment['filesize'], $attachment['filetype'], $attachment['filename'] ?? '');
|
||||
}
|
||||
$body = str_replace($match[1],'',$body);
|
||||
}
|
||||
|
|
@ -563,7 +556,7 @@ function item_post(App $a) {
|
|||
$origin = $_REQUEST['origin'];
|
||||
}
|
||||
|
||||
$uri = ($message_id ? $message_id : Item::newURI($api_source ? $profile_uid : $uid, $guid));
|
||||
$uri = Item::newURI($api_source ? $profile_uid : $uid, $guid);
|
||||
|
||||
// Fallback so that we alway have a parent uri
|
||||
if (!$thr_parent_uri || !$toplevel_item_id) {
|
||||
|
|
@ -576,9 +569,9 @@ function item_post(App $a) {
|
|||
$datarray['gravity'] = $gravity;
|
||||
$datarray['network'] = $network;
|
||||
$datarray['contact-id'] = $contact_id;
|
||||
$datarray['owner-name'] = $contact_record['name'];
|
||||
$datarray['owner-link'] = $contact_record['url'];
|
||||
$datarray['owner-avatar'] = $contact_record['thumb'];
|
||||
$datarray['owner-name'] = $contact_record['name'] ?? '';
|
||||
$datarray['owner-link'] = $contact_record['url'] ?? '';
|
||||
$datarray['owner-avatar'] = $contact_record['thumb'] ?? '';
|
||||
$datarray['owner-id'] = Contact::getIdForURL($datarray['owner-link']);
|
||||
$datarray['author-name'] = $author['name'];
|
||||
$datarray['author-link'] = $author['url'];
|
||||
|
|
@ -610,8 +603,7 @@ function item_post(App $a) {
|
|||
$datarray['pubmail'] = $pubmail_enabled;
|
||||
$datarray['attach'] = $attachments;
|
||||
|
||||
// This is not a bug. The item store function changes 'parent-uri' to 'thr-parent' and fetches 'parent-uri' new. (We should change this)
|
||||
$datarray['parent-uri'] = $thr_parent_uri;
|
||||
$datarray['thr-parent'] = $thr_parent_uri;
|
||||
|
||||
$datarray['postopts'] = $postopts;
|
||||
$datarray['origin'] = $origin;
|
||||
|
|
@ -630,9 +622,10 @@ function item_post(App $a) {
|
|||
$datarray['api_source'] = $api_source;
|
||||
|
||||
// This field is for storing the raw conversation data
|
||||
$datarray['protocol'] = Conversation::PARCEL_DFRN;
|
||||
$datarray['protocol'] = Conversation::PARCEL_DIRECT;
|
||||
$datarray['direction'] = Conversation::PUSH;
|
||||
|
||||
$conversation = DBA::selectFirst('conversation', ['conversation-uri', 'conversation-href'], ['item-uri' => $datarray['parent-uri']]);
|
||||
$conversation = DBA::selectFirst('conversation', ['conversation-uri', 'conversation-href'], ['item-uri' => $datarray['thr-parent']]);
|
||||
if (DBA::isResult($conversation)) {
|
||||
if ($conversation['conversation-uri'] != '') {
|
||||
$datarray['conversation-uri'] = $conversation['conversation-uri'];
|
||||
|
|
@ -653,7 +646,7 @@ function item_post(App $a) {
|
|||
|
||||
// Check for hashtags in the body and repair or add hashtag links
|
||||
if ($preview || $orig_post) {
|
||||
Item::setHashtags($datarray);
|
||||
$datarray['body'] = Item::setHashtags($datarray['body']);
|
||||
}
|
||||
|
||||
// preview mode - prepare the body for display and send it via json
|
||||
|
|
@ -661,6 +654,7 @@ function item_post(App $a) {
|
|||
// We set the datarray ID to -1 because in preview mode the dataray
|
||||
// doesn't have an ID.
|
||||
$datarray["id"] = -1;
|
||||
$datarray["uri-id"] = -1;
|
||||
$datarray["item_id"] = -1;
|
||||
$datarray["author-network"] = Protocol::DFRN;
|
||||
|
||||
|
|
@ -705,7 +699,6 @@ function item_post(App $a) {
|
|||
// update filetags in pconfig
|
||||
FileTag::updatePconfig($uid, $categories_old, $categories_new, 'category');
|
||||
|
||||
info(DI::l10n()->t('Post updated.'));
|
||||
if ($return_path) {
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
}
|
||||
|
|
@ -727,7 +720,7 @@ function item_post(App $a) {
|
|||
$post_id = Item::insert($datarray);
|
||||
|
||||
if (!$post_id) {
|
||||
info(DI::l10n()->t('Item wasn\'t stored.'));
|
||||
notice(DI::l10n()->t('Item wasn\'t stored.'));
|
||||
if ($return_path) {
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
}
|
||||
|
|
@ -748,46 +741,34 @@ function item_post(App $a) {
|
|||
|
||||
Tag::storeFromBody($datarray['uri-id'], $datarray['body']);
|
||||
|
||||
if (!\Friendica\Content\Feature::isEnabled($uid, 'explicit_mentions') && ($datarray['gravity'] == GRAVITY_COMMENT)) {
|
||||
Tag::createImplicitMentions($datarray['uri-id'], $datarray['thr-parent-id']);
|
||||
}
|
||||
|
||||
// update filetags in pconfig
|
||||
FileTag::updatePconfig($uid, $categories_old, $categories_new, 'category');
|
||||
|
||||
// These notifications are sent if someone else is commenting other your wall
|
||||
if ($toplevel_item_id) {
|
||||
if ($contact_record != $author) {
|
||||
if ($contact_record != $author) {
|
||||
if ($toplevel_item_id) {
|
||||
notification([
|
||||
'type' => Type::COMMENT,
|
||||
'notify_flags' => $user['notify-flags'],
|
||||
'language' => $user['language'],
|
||||
'to_name' => $user['username'],
|
||||
'to_email' => $user['email'],
|
||||
'uid' => $user['uid'],
|
||||
'item' => $datarray,
|
||||
'link' => DI::baseUrl().'/display/'.urlencode($datarray['guid']),
|
||||
'source_name' => $datarray['author-name'],
|
||||
'source_link' => $datarray['author-link'],
|
||||
'source_photo' => $datarray['author-avatar'],
|
||||
'verb' => Activity::POST,
|
||||
'otype' => 'item',
|
||||
'parent' => $toplevel_item_id,
|
||||
'parent_uri' => $toplevel_item['uri']
|
||||
'type' => Type::COMMENT,
|
||||
'otype' => Notify\ObjectType::ITEM,
|
||||
'verb' => Activity::POST,
|
||||
'uid' => $profile_uid,
|
||||
'cid' => $datarray['author-id'],
|
||||
'item' => $datarray,
|
||||
'link' => DI::baseUrl() . '/display/' . urlencode($datarray['guid']),
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if (($contact_record != $author) && !count($forum_contact)) {
|
||||
} elseif (empty($forum_contact)) {
|
||||
notification([
|
||||
'type' => Type::WALL,
|
||||
'notify_flags' => $user['notify-flags'],
|
||||
'language' => $user['language'],
|
||||
'to_name' => $user['username'],
|
||||
'to_email' => $user['email'],
|
||||
'uid' => $user['uid'],
|
||||
'item' => $datarray,
|
||||
'link' => DI::baseUrl().'/display/'.urlencode($datarray['guid']),
|
||||
'source_name' => $datarray['author-name'],
|
||||
'source_link' => $datarray['author-link'],
|
||||
'source_photo' => $datarray['author-avatar'],
|
||||
'verb' => Activity::POST,
|
||||
'otype' => 'item'
|
||||
'type' => Type::WALL,
|
||||
'otype' => Notify\ObjectType::ITEM,
|
||||
'verb' => Activity::POST,
|
||||
'uid' => $profile_uid,
|
||||
'cid' => $datarray['author-id'],
|
||||
'item' => $datarray,
|
||||
'link' => DI::baseUrl() . '/display/' . urlencode($datarray['guid']),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -808,12 +789,6 @@ function item_post(App $a) {
|
|||
}
|
||||
}
|
||||
|
||||
// Insert an item entry for UID=0 for global entries.
|
||||
// We now do it in the background to save some time.
|
||||
// This is important in interactive environments like the frontend or the API.
|
||||
// We don't fork a new process since this is done anyway with the following command
|
||||
Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], "CreateShadowEntry", $post_id);
|
||||
|
||||
// When we are doing some forum posting via ! we have to start the notifier manually.
|
||||
// These kind of posts don't initiate the notifier call in the item class.
|
||||
if ($only_to_forum) {
|
||||
|
|
@ -826,7 +801,6 @@ function item_post(App $a) {
|
|||
return $post_id;
|
||||
}
|
||||
|
||||
info(DI::l10n()->t('Post published.'));
|
||||
item_post_return(DI::baseUrl(), $api_source, $return_path);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
|
@ -861,7 +835,9 @@ function item_content(App $a)
|
|||
|
||||
if (($a->argc >= 3) && ($a->argv[1] === 'drop') && intval($a->argv[2])) {
|
||||
if (DI::mode()->isAjax()) {
|
||||
$o = Item::deleteForUser(['id' => $a->argv[2]], local_user());
|
||||
Item::deleteForUser(['id' => $a->argv[2]], local_user());
|
||||
// ajax return: [<item id>, 0 (no perm) | <owner id>]
|
||||
System::jsonExit([intval($a->argv[2]), local_user()]);
|
||||
} else {
|
||||
if (!empty($a->argv[3])) {
|
||||
$o = drop_item($a->argv[2], $a->argv[3]);
|
||||
|
|
@ -870,163 +846,78 @@ function item_content(App $a)
|
|||
$o = drop_item($a->argv[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (DI::mode()->isAjax()) {
|
||||
// ajax return: [<item id>, 0 (no perm) | <owner id>]
|
||||
System::jsonExit([intval($a->argv[2]), intval($o)]);
|
||||
}
|
||||
}
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function removes the tag $tag from the text $body and replaces it with
|
||||
* the appropriate link.
|
||||
*
|
||||
* @param App $a
|
||||
* @param string $body the text to replace the tag in
|
||||
* @param string $inform a comma-seperated string containing everybody to inform
|
||||
* @param integer $profile_uid
|
||||
* @param string $tag the tag to replace
|
||||
* @param string $network The network of the post
|
||||
*
|
||||
* @return array|bool ['replaced' => $replaced, 'contact' => $contact];
|
||||
* @throws ImagickException
|
||||
* @param int $id
|
||||
* @param string $return
|
||||
* @return string
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function handle_tag(&$body, &$inform, $profile_uid, $tag, $network = "")
|
||||
function drop_item(int $id, string $return = '')
|
||||
{
|
||||
$replaced = false;
|
||||
// locate item to be deleted
|
||||
$fields = ['id', 'uid', 'guid', 'contact-id', 'deleted', 'gravity', 'parent'];
|
||||
$item = Item::selectFirstForUser(local_user(), $fields, ['id' => $id]);
|
||||
|
||||
//is it a person tag?
|
||||
if (Tag::isType($tag, Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION)) {
|
||||
$tag_type = substr($tag, 0, 1);
|
||||
//is it already replaced?
|
||||
if (strpos($tag, '[url=')) {
|
||||
// Checking for the alias that is used for OStatus
|
||||
$pattern = "/[@!]\[url\=(.*?)\](.*?)\[\/url\]/ism";
|
||||
if (preg_match($pattern, $tag, $matches)) {
|
||||
$data = Contact::getDetailsByURL($matches[1]);
|
||||
|
||||
if ($data["alias"] != "") {
|
||||
$newtag = '@[url=' . $data["alias"] . ']' . $data["nick"] . '[/url]';
|
||||
}
|
||||
}
|
||||
|
||||
return $replaced;
|
||||
}
|
||||
|
||||
//get the person's name
|
||||
$name = substr($tag, 1);
|
||||
|
||||
// Sometimes the tag detection doesn't seem to work right
|
||||
// This is some workaround
|
||||
$nameparts = explode(" ", $name);
|
||||
$name = $nameparts[0];
|
||||
|
||||
// Try to detect the contact in various ways
|
||||
if (strpos($name, 'http://')) {
|
||||
// At first we have to ensure that the contact exists
|
||||
Contact::getIdForURL($name);
|
||||
|
||||
// Now we should have something
|
||||
$contact = Contact::getDetailsByURL($name);
|
||||
} elseif (strpos($name, '@')) {
|
||||
// This function automatically probes when no entry was found
|
||||
$contact = Contact::getDetailsByAddr($name);
|
||||
} else {
|
||||
$contact = false;
|
||||
$fields = ['id', 'url', 'nick', 'name', 'alias', 'network', 'forum', 'prv'];
|
||||
|
||||
if (strrpos($name, '+')) {
|
||||
// Is it in format @nick+number?
|
||||
$tagcid = intval(substr($name, strrpos($name, '+') + 1));
|
||||
$contact = DBA::selectFirst('contact', $fields, ['id' => $tagcid, 'uid' => $profile_uid]);
|
||||
}
|
||||
|
||||
// select someone by nick or attag in the current network
|
||||
if (!DBA::isResult($contact) && ($network != "")) {
|
||||
$condition = ["(`nick` = ? OR `attag` = ?) AND `network` = ? AND `uid` = ?",
|
||||
$name, $name, $network, $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
|
||||
//select someone by name in the current network
|
||||
if (!DBA::isResult($contact) && ($network != "")) {
|
||||
$condition = ['name' => $name, 'network' => $network, 'uid' => $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
|
||||
// select someone by nick or attag in any network
|
||||
if (!DBA::isResult($contact)) {
|
||||
$condition = ["(`nick` = ? OR `attag` = ?) AND `uid` = ?", $name, $name, $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
|
||||
// select someone by name in any network
|
||||
if (!DBA::isResult($contact)) {
|
||||
$condition = ['name' => $name, 'uid' => $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if $contact has been successfully loaded
|
||||
if (DBA::isResult($contact)) {
|
||||
if (strlen($inform) && (isset($contact["notify"]) || isset($contact["id"]))) {
|
||||
$inform .= ',';
|
||||
}
|
||||
|
||||
if (isset($contact["id"])) {
|
||||
$inform .= 'cid:' . $contact["id"];
|
||||
} elseif (isset($contact["notify"])) {
|
||||
$inform .= $contact["notify"];
|
||||
}
|
||||
|
||||
$profile = $contact["url"];
|
||||
$newname = ($contact["name"] ?? '') ?: $contact["nick"];
|
||||
}
|
||||
|
||||
//if there is an url for this persons profile
|
||||
if (isset($profile) && ($newname != "")) {
|
||||
$replaced = true;
|
||||
// create profile link
|
||||
$profile = str_replace(',', '%2c', $profile);
|
||||
$newtag = $tag_type.'[url=' . $profile . ']' . $newname . '[/url]';
|
||||
$body = str_replace($tag_type . $name, $newtag, $body);
|
||||
}
|
||||
if (!DBA::isResult($item)) {
|
||||
notice(DI::l10n()->t('Item not found.'));
|
||||
DI::baseUrl()->redirect('network');
|
||||
}
|
||||
|
||||
return ['replaced' => $replaced, 'contact' => $contact];
|
||||
}
|
||||
if ($item['deleted']) {
|
||||
return '';
|
||||
}
|
||||
|
||||
function item_add_implicit_mentions(array $tags, array $thread_parent_contact, $thread_parent_uriid)
|
||||
{
|
||||
if (DI::config()->get('system', 'disable_implicit_mentions')) {
|
||||
// Add a tag if the parent contact is from ActivityPub or OStatus (This will notify them)
|
||||
if (in_array($thread_parent_contact['network'], [Protocol::OSTATUS, Protocol::ACTIVITYPUB])) {
|
||||
$contact = Tag::TAG_CHARACTER[Tag::MENTION] . '[url=' . $thread_parent_contact['url'] . ']' . $thread_parent_contact['nick'] . '[/url]';
|
||||
if (!stripos(implode($tags), '[url=' . $thread_parent_contact['url'] . ']')) {
|
||||
$tags[] = $contact;
|
||||
$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) {
|
||||
if (!empty($item['parent'])) {
|
||||
$parentitem = Item::selectFirstForUser(local_user(), ['guid'], ['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 ($item['gravity'] == GRAVITY_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 {
|
||||
$implicit_mentions = [
|
||||
$thread_parent_contact['url'] => $thread_parent_contact['nick']
|
||||
];
|
||||
|
||||
$parent_terms = Tag::getByURIId($thread_parent_uriid, [Tag::MENTION, Tag::IMPLICIT_MENTION]);
|
||||
|
||||
foreach ($parent_terms as $parent_term) {
|
||||
$implicit_mentions[$parent_term['url']] = $parent_term['name'];
|
||||
}
|
||||
|
||||
foreach ($implicit_mentions as $url => $label) {
|
||||
if ($url != \Friendica\Model\Profile::getMyURL() && !stripos(implode($tags), '[url=' . $url . ']')) {
|
||||
$tags[] = Tag::TAG_CHARACTER[Tag::IMPLICIT_MENTION] . '[url=' . $url . ']' . $label . '[/url]';
|
||||
}
|
||||
}
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
DI::baseUrl()->redirect('display/' . $item['guid']);
|
||||
//NOTREACHED
|
||||
}
|
||||
|
||||
return $tags;
|
||||
return '';
|
||||
}
|
||||
|
|
|
|||
161
mod/lockview.php
|
|
@ -1,161 +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\App;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
|
||||
function lockview_content(App $a)
|
||||
{
|
||||
$type = (($a->argc > 1) ? $a->argv[1] : 0);
|
||||
if (is_numeric($type)) {
|
||||
$item_id = intval($type);
|
||||
$type = 'item';
|
||||
} else {
|
||||
$item_id = (($a->argc > 2) ? intval($a->argv[2]) : 0);
|
||||
}
|
||||
|
||||
if (!$item_id) {
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!in_array($type, ['item','photo','event'])) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$fields = ['uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'];
|
||||
$condition = ['id' => $item_id];
|
||||
|
||||
if ($type != 'item') {
|
||||
$item = DBA::selectFirst($type, $fields, $condition);
|
||||
} else {
|
||||
$fields[] = 'private';
|
||||
$item = Item::selectFirst($fields, $condition);
|
||||
}
|
||||
|
||||
if (!DBA::isResult($item)) {
|
||||
exit();
|
||||
}
|
||||
|
||||
Hook::callAll('lockview_content', $item);
|
||||
|
||||
if ($item['uid'] != local_user()) {
|
||||
echo DI::l10n()->t('Remote privacy information not available.') . '<br />';
|
||||
exit();
|
||||
}
|
||||
|
||||
if (isset($item['private'])
|
||||
&& $item['private'] == Item::PRIVATE
|
||||
&& empty($item['allow_cid'])
|
||||
&& empty($item['allow_gid'])
|
||||
&& empty($item['deny_cid'])
|
||||
&& empty($item['deny_gid']))
|
||||
{
|
||||
echo DI::l10n()->t('Remote privacy information not available.') . '<br />';
|
||||
exit();
|
||||
}
|
||||
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
|
||||
$allowed_users = $aclFormatter->expand($item['allow_cid']);
|
||||
$allowed_groups = $aclFormatter->expand($item['allow_gid']);
|
||||
$deny_users = $aclFormatter->expand($item['deny_cid']);
|
||||
$deny_groups = $aclFormatter->expand($item['deny_gid']);
|
||||
|
||||
$o = DI::l10n()->t('Visible to:') . '<br />';
|
||||
$l = [];
|
||||
|
||||
if (count($allowed_groups)) {
|
||||
$key = array_search(Group::FOLLOWERS, $allowed_groups);
|
||||
if ($key !== false) {
|
||||
$l[] = '<b>' . DI::l10n()->t('Followers') . '</b>';
|
||||
unset($allowed_groups[$key]);
|
||||
}
|
||||
|
||||
$key = array_search(Group::MUTUALS, $allowed_groups);
|
||||
if ($key !== false) {
|
||||
$l[] = '<b>' . DI::l10n()->t('Mutuals') . '</b>';
|
||||
unset($allowed_groups[$key]);
|
||||
}
|
||||
|
||||
|
||||
$r = q("SELECT `name` FROM `group` WHERE `id` IN ( %s )",
|
||||
DBA::escape(implode(', ', $allowed_groups))
|
||||
);
|
||||
if (DBA::isResult($r)) {
|
||||
foreach ($r as $rr) {
|
||||
$l[] = '<b>' . $rr['name'] . '</b>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($allowed_users)) {
|
||||
$r = q("SELECT `name` FROM `contact` WHERE `id` IN ( %s )",
|
||||
DBA::escape(implode(', ', $allowed_users))
|
||||
);
|
||||
if (DBA::isResult($r)) {
|
||||
foreach ($r as $rr) {
|
||||
$l[] = $rr['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($deny_groups)) {
|
||||
$key = array_search(Group::FOLLOWERS, $deny_groups);
|
||||
if ($key !== false) {
|
||||
$l[] = '<b><strike>' . DI::l10n()->t('Followers') . '</strike></b>';
|
||||
unset($deny_groups[$key]);
|
||||
}
|
||||
|
||||
$key = array_search(Group::MUTUALS, $deny_groups);
|
||||
if ($key !== false) {
|
||||
$l[] = '<b><strike>' . DI::l10n()->t('Mutuals') . '</strike></b>';
|
||||
unset($deny_groups[$key]);
|
||||
}
|
||||
|
||||
$r = q("SELECT `name` FROM `group` WHERE `id` IN ( %s )",
|
||||
DBA::escape(implode(', ', $deny_groups))
|
||||
);
|
||||
if (DBA::isResult($r)) {
|
||||
foreach ($r as $rr) {
|
||||
$l[] = '<b><strike>' . $rr['name'] . '</strike></b>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($deny_users)) {
|
||||
$r = q("SELECT `name` FROM `contact` WHERE `id` IN ( %s )",
|
||||
DBA::escape(implode(', ', $deny_users))
|
||||
);
|
||||
if (DBA::isResult($r)) {
|
||||
foreach ($r as $rr) {
|
||||
$l[] = '<strike>' . $rr['name'] . '</strike>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo $o . implode(', ', $l);
|
||||
exit();
|
||||
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ function lostpass_post(App $a)
|
|||
$condition = ['(`email` = ? OR `nickname` = ?) AND `verified` = 1 AND `blocked` = 0', $loginame, $loginame];
|
||||
$user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'language'], $condition);
|
||||
if (!DBA::isResult($user)) {
|
||||
notice(DI::l10n()->t('No valid account found.') . EOL);
|
||||
notice(DI::l10n()->t('No valid account found.'));
|
||||
DI::baseUrl()->redirect();
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ function lostpass_post(App $a)
|
|||
];
|
||||
$result = DBA::update('user', $fields, ['uid' => $user['uid']]);
|
||||
if ($result) {
|
||||
info(DI::l10n()->t('Password reset request issued. Check your email.') . EOL);
|
||||
info(DI::l10n()->t('Password reset request issued. Check your email.'));
|
||||
}
|
||||
|
||||
$sitename = DI::config()->get('config', 'sitename');
|
||||
|
|
@ -152,7 +152,7 @@ function lostpass_generate_password($user)
|
|||
'$newpass' => $new_password,
|
||||
]);
|
||||
|
||||
info("Your password has been reset." . EOL);
|
||||
info(DI::l10n()->t("Your password has been reset."));
|
||||
|
||||
$sitename = DI::config()->get('config', 'sitename');
|
||||
$preamble = Strings::deindent(DI::l10n()->t('
|
||||
|
|
|
|||
|
|
@ -27,8 +27,7 @@ use Friendica\Database\DBA;
|
|||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Module\Contact as ModuleContact;
|
||||
|
||||
/**
|
||||
* Controller for /match.
|
||||
|
|
@ -60,7 +59,7 @@ function match_content(App $a)
|
|||
return '';
|
||||
}
|
||||
if (!$profile['pub_keywords'] && (!$profile['prv_keywords'])) {
|
||||
notice(DI::l10n()->t('No keywords to match. Please add keywords to your profile.') . EOL);
|
||||
notice(DI::l10n()->t('No keywords to match. Please add keywords to your profile.'));
|
||||
return '';
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +75,7 @@ function match_content(App $a)
|
|||
$host = DI::baseUrl();
|
||||
}
|
||||
|
||||
$msearch_json = Network::post($host . '/msearch', $params)->getBody();
|
||||
$msearch_json = DI::httpRequest()->post($host . '/msearch', $params)->getBody();
|
||||
|
||||
$msearch = json_decode($msearch_json);
|
||||
|
||||
|
|
@ -89,37 +88,14 @@ function match_content(App $a)
|
|||
$profile = $msearch->results[$i];
|
||||
|
||||
// Already known contact
|
||||
if (!$profile || Contact::getIdForURL($profile->url, local_user(), true)) {
|
||||
if (!$profile || Contact::getIdForURL($profile->url, local_user())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Workaround for wrong directory photo URL
|
||||
$profile->photo = str_replace('http:///photo/', Search::getGlobalDirectory() . '/photo/', $profile->photo);
|
||||
|
||||
$connlnk = DI::baseUrl() . '/follow/?url=' . $profile->url;
|
||||
$photo_menu = [
|
||||
'profile' => [DI::l10n()->t("View Profile"), Contact::magicLink($profile->url)],
|
||||
'follow' => [DI::l10n()->t("Connect/Follow"), $connlnk]
|
||||
];
|
||||
|
||||
$contact_details = Contact::getDetailsByURL($profile->url, 0);
|
||||
|
||||
$entry = [
|
||||
'url' => Contact::magicLink($profile->url),
|
||||
'itemurl' => $contact_details['addr'] ?? $profile->url,
|
||||
'name' => $profile->name,
|
||||
'details' => $contact_details['location'] ?? '',
|
||||
'tags' => $contact_details['keywords'] ?? '',
|
||||
'about' => $contact_details['about'] ?? '',
|
||||
'account_type' => Contact::getAccountType($contact_details),
|
||||
'thumb' => ProxyUtils::proxifyUrl($profile->photo, false, ProxyUtils::SIZE_THUMB),
|
||||
'conntxt' => DI::l10n()->t('Connect'),
|
||||
'connlnk' => $connlnk,
|
||||
'img_hover' => $profile->tags,
|
||||
'photo_menu' => $photo_menu,
|
||||
'id' => $i,
|
||||
];
|
||||
$entries[] = $entry;
|
||||
$contact = Contact::getByURLForUser($profile->url, local_user());
|
||||
if (!empty($contact)) {
|
||||
$entries[] = ModuleContact::getContactTemplateVars($contact);
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
|
|
@ -141,7 +117,7 @@ function match_content(App $a)
|
|||
}
|
||||
|
||||
if (empty($entries)) {
|
||||
info(DI::l10n()->t('No matches') . EOL);
|
||||
info(DI::l10n()->t('No matches'));
|
||||
}
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl');
|
||||
|
|
|
|||
130
mod/message.php
|
|
@ -32,7 +32,6 @@ use Friendica\Model\Mail;
|
|||
use Friendica\Model\Notify\Type;
|
||||
use Friendica\Module\Security\Login;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Temporal;
|
||||
|
||||
|
|
@ -68,34 +67,32 @@ function message_init(App $a)
|
|||
function message_post(App $a)
|
||||
{
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$replyto = !empty($_REQUEST['replyto']) ? Strings::escapeTags(trim($_REQUEST['replyto'])) : '';
|
||||
$subject = !empty($_REQUEST['subject']) ? Strings::escapeTags(trim($_REQUEST['subject'])) : '';
|
||||
$body = !empty($_REQUEST['body']) ? Strings::escapeHtml(trim($_REQUEST['body'])) : '';
|
||||
$recipient = !empty($_REQUEST['messageto']) ? intval($_REQUEST['messageto']) : 0;
|
||||
$recipient = !empty($_REQUEST['recipient']) ? intval($_REQUEST['recipient']) : 0;
|
||||
|
||||
$ret = Mail::send($recipient, $body, $subject, $replyto);
|
||||
$norecip = false;
|
||||
|
||||
switch ($ret) {
|
||||
case -1:
|
||||
notice(DI::l10n()->t('No recipient selected.') . EOL);
|
||||
notice(DI::l10n()->t('No recipient selected.'));
|
||||
$norecip = true;
|
||||
break;
|
||||
case -2:
|
||||
notice(DI::l10n()->t('Unable to locate contact information.') . EOL);
|
||||
notice(DI::l10n()->t('Unable to locate contact information.'));
|
||||
break;
|
||||
case -3:
|
||||
notice(DI::l10n()->t('Message could not be sent.') . EOL);
|
||||
notice(DI::l10n()->t('Message could not be sent.'));
|
||||
break;
|
||||
case -4:
|
||||
notice(DI::l10n()->t('Message collection failure.') . EOL);
|
||||
notice(DI::l10n()->t('Message collection failure.'));
|
||||
break;
|
||||
default:
|
||||
info(DI::l10n()->t('Message sent.') . EOL);
|
||||
}
|
||||
|
||||
// fake it to go back to the input form if no recipient listed
|
||||
|
|
@ -113,7 +110,7 @@ function message_content(App $a)
|
|||
Nav::setSelected('messages');
|
||||
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return Login::form();
|
||||
}
|
||||
|
||||
|
|
@ -144,51 +141,20 @@ function message_content(App $a)
|
|||
return;
|
||||
}
|
||||
|
||||
// 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]];
|
||||
}
|
||||
}
|
||||
|
||||
//DI::page()['aside'] = '';
|
||||
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
|
||||
'$method' => 'get',
|
||||
'$message' => DI::l10n()->t('Do you really want to delete this message?'),
|
||||
'$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('message');
|
||||
}
|
||||
|
||||
$cmd = $a->argv[1];
|
||||
if ($cmd === 'drop') {
|
||||
$message = DBA::selectFirst('mail', ['convid'], ['id' => $a->argv[2], 'uid' => local_user()]);
|
||||
if(!DBA::isResult($message)){
|
||||
info(DI::l10n()->t('Conversation not found.') . EOL);
|
||||
notice(DI::l10n()->t('Conversation not found.'));
|
||||
DI::baseUrl()->redirect('message');
|
||||
}
|
||||
|
||||
if (DBA::delete('mail', ['id' => $a->argv[2], 'uid' => local_user()])) {
|
||||
info(DI::l10n()->t('Message deleted.') . EOL);
|
||||
if (!DBA::delete('mail', ['id' => $a->argv[2], 'uid' => local_user()])) {
|
||||
notice(DI::l10n()->t('Message was not deleted.'));
|
||||
}
|
||||
|
||||
$conversation = DBA::selectFirst('mail', ['id'], ['convid' => $message['convid'], 'uid' => local_user()]);
|
||||
if(!DBA::isResult($conversation)){
|
||||
info(DI::l10n()->t('Conversation removed.') . EOL);
|
||||
DI::baseUrl()->redirect('message');
|
||||
}
|
||||
|
||||
|
|
@ -201,8 +167,8 @@ function message_content(App $a)
|
|||
if (DBA::isResult($r)) {
|
||||
$parent = $r[0]['parent-uri'];
|
||||
|
||||
if (DBA::delete('mail', ['parent-uri' => $parent, 'uid' => local_user()])) {
|
||||
info(DI::l10n()->t('Conversation removed.') . EOL);
|
||||
if (!DBA::delete('mail', ['parent-uri' => $parent, 'uid' => local_user()])) {
|
||||
notice(DI::l10n()->t('Conversation was not removed.'));
|
||||
}
|
||||
}
|
||||
DI::baseUrl()->redirect('message');
|
||||
|
|
@ -219,50 +185,14 @@ function message_content(App $a)
|
|||
'$linkurl' => DI::l10n()->t('Please enter a link URL:')
|
||||
]);
|
||||
|
||||
$preselect = isset($a->argv[2]) ? [$a->argv[2]] : [];
|
||||
$recipientId = $a->argv[2] ?? null;
|
||||
|
||||
$prename = $preurl = $preid = '';
|
||||
|
||||
if ($preselect) {
|
||||
$r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `id` = %d LIMIT 1",
|
||||
intval(local_user()),
|
||||
intval($a->argv[2])
|
||||
);
|
||||
if (!DBA::isResult($r)) {
|
||||
$r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' LIMIT 1",
|
||||
intval(local_user()),
|
||||
DBA::escape(Strings::normaliseLink(base64_decode($a->argv[2])))
|
||||
);
|
||||
}
|
||||
|
||||
if (!DBA::isResult($r)) {
|
||||
$r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `addr` = '%s' LIMIT 1",
|
||||
intval(local_user()),
|
||||
DBA::escape(base64_decode($a->argv[2]))
|
||||
);
|
||||
}
|
||||
|
||||
if (DBA::isResult($r)) {
|
||||
$prename = $r[0]['name'];
|
||||
$preid = $r[0]['id'];
|
||||
$preselect = [$preid];
|
||||
} else {
|
||||
$preselect = [];
|
||||
}
|
||||
}
|
||||
|
||||
$prefill = $preselect ? $prename : '';
|
||||
|
||||
// the ugly select box
|
||||
$select = ACL::getMessageContactSelectHTML('messageto', 'message-to-select', $preselect, 4, 10);
|
||||
$select = ACL::getMessageContactSelectHTML($recipientId);
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('prv_message.tpl');
|
||||
$o .= Renderer::replaceMacros($tpl, [
|
||||
'$header' => DI::l10n()->t('Send Private Message'),
|
||||
'$to' => DI::l10n()->t('To:'),
|
||||
'$showinputs' => 'true',
|
||||
'$prefill' => $prefill,
|
||||
'$preid' => $preid,
|
||||
'$subject' => DI::l10n()->t('Subject:'),
|
||||
'$subjtxt' => $_REQUEST['subject'] ?? '',
|
||||
'$text' => $_REQUEST['body'] ?? '',
|
||||
|
|
@ -301,7 +231,7 @@ function message_content(App $a)
|
|||
$r = get_messages(local_user(), $pager->getStart(), $pager->getItemsPerPage());
|
||||
|
||||
if (!DBA::isResult($r)) {
|
||||
info(DI::l10n()->t('No messages.') . EOL);
|
||||
notice(DI::l10n()->t('No messages.'));
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
|
@ -352,13 +282,12 @@ function message_content(App $a)
|
|||
$messages = DBA::toArray($messages_stmt);
|
||||
|
||||
DBA::update('mail', ['seen' => 1], ['parent-uri' => $message['parent-uri'], 'uid' => local_user()]);
|
||||
DBA::update('notify', ['seen' => 1], ['type' => Type::MAIL, 'parent' => $message['id'], 'uid' => local_user()]);
|
||||
} else {
|
||||
$messages = false;
|
||||
}
|
||||
|
||||
if (!DBA::isResult($messages)) {
|
||||
notice(DI::l10n()->t('Message not available.') . EOL);
|
||||
notice(DI::l10n()->t('Message not available.'));
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
|
@ -396,20 +325,16 @@ function message_content(App $a)
|
|||
$body_e = BBCode::convert($message['body']);
|
||||
$to_name_e = $message['name'];
|
||||
|
||||
$contact = Contact::getDetailsByURL($message['from-url']);
|
||||
if (isset($contact["thumb"])) {
|
||||
$from_photo = $contact["thumb"];
|
||||
} else {
|
||||
$from_photo = $message['from-photo'];
|
||||
}
|
||||
$contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar']);
|
||||
$from_photo = Contact::getThumb($contact, $message['from-photo']);
|
||||
|
||||
$mails[] = [
|
||||
'id' => $message['id'],
|
||||
'from_name' => $from_name_e,
|
||||
'from_url' => $from_url,
|
||||
'from_addr' => $contact['addr'],
|
||||
'from_addr' => $contact['addr'] ?? $from_url,
|
||||
'sparkle' => $sparkle,
|
||||
'from_photo' => ProxyUtils::proxifyUrl($from_photo, false, ProxyUtils::SIZE_THUMB),
|
||||
'from_photo' => $from_photo,
|
||||
'subject' => $subject_e,
|
||||
'body' => $body_e,
|
||||
'delete' => DI::l10n()->t('Delete message'),
|
||||
|
|
@ -421,7 +346,7 @@ function message_content(App $a)
|
|||
$seen = $message['seen'];
|
||||
}
|
||||
|
||||
$select = $message['name'] . '<input type="hidden" name="messageto" value="' . $contact_id . '" />';
|
||||
$select = $message['name'] . '<input type="hidden" name="recipient" value="' . $contact_id . '" />';
|
||||
$parent = '<input type="hidden" name="replyto" value="' . $message['parent-uri'] . '" />';
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('mail_display.tpl');
|
||||
|
|
@ -437,7 +362,6 @@ function message_content(App $a)
|
|||
// reply
|
||||
'$header' => DI::l10n()->t('Send Reply'),
|
||||
'$to' => DI::l10n()->t('To:'),
|
||||
'$showinputs' => '',
|
||||
'$subject' => DI::l10n()->t('Subject:'),
|
||||
'$subjtxt' => $message['title'],
|
||||
'$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ',
|
||||
|
|
@ -461,7 +385,7 @@ function message_content(App $a)
|
|||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
function get_messages($uid, $start, $limit)
|
||||
function get_messages(int $uid, int $start, int $limit)
|
||||
{
|
||||
return DBA::toArray(DBA::p('SELECT
|
||||
m.`id`,
|
||||
|
|
@ -528,12 +452,8 @@ function render_messages(array $msg, $t)
|
|||
$body_e = $rr['body'];
|
||||
$to_name_e = $rr['name'];
|
||||
|
||||
$contact = Contact::getDetailsByURL($rr['url']);
|
||||
if (isset($contact["thumb"])) {
|
||||
$from_photo = $contact["thumb"];
|
||||
} else {
|
||||
$from_photo = (($rr['thumb']) ? $rr['thumb'] : $rr['from-photo']);
|
||||
}
|
||||
$contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar']);
|
||||
$from_photo = Contact::getThumb($contact, $rr['thumb'] ?: $rr['from-photo']);
|
||||
|
||||
$rslt .= Renderer::replaceMacros($tpl, [
|
||||
'$id' => $rr['id'],
|
||||
|
|
@ -541,7 +461,7 @@ function render_messages(array $msg, $t)
|
|||
'$from_url' => Contact::magicLink($rr['url']),
|
||||
'$from_addr' => $contact['addr'] ?? '',
|
||||
'$sparkle' => ' sparkle',
|
||||
'$from_photo' => ProxyUtils::proxifyUrl($from_photo, false, ProxyUtils::SIZE_THUMB),
|
||||
'$from_photo' => $from_photo,
|
||||
'$subject' => $rr['title'],
|
||||
'$delete' => DI::l10n()->t('Delete conversation'),
|
||||
'$body' => $body_e,
|
||||
|
|
|
|||
1004
mod/network.php
|
|
@ -1,1004 +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\App;
|
||||
use Friendica\Content\Feature;
|
||||
use Friendica\Content\ForumManager;
|
||||
use Friendica\Content\Nav;
|
||||
use Friendica\Content\Pager;
|
||||
use Friendica\Content\Widget;
|
||||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Core\ACL;
|
||||
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\Contact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post\Category;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Module\Security\Login;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
function network_init(App $a)
|
||||
{
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
Hook::add('head', __FILE__, 'network_infinite_scroll_head');
|
||||
|
||||
$is_a_date_query = false;
|
||||
|
||||
$group_id = (($a->argc > 1 && is_numeric($a->argv[1])) ? intval($a->argv[1]) : 0);
|
||||
|
||||
$cid = 0;
|
||||
if (!empty($_GET['cid'])) {
|
||||
$cid = $_GET['cid'];
|
||||
$_GET['nets'] = '';
|
||||
$group_id = 0;
|
||||
}
|
||||
|
||||
if ($a->argc > 1) {
|
||||
for ($x = 1; $x < $a->argc; $x ++) {
|
||||
if (DI::dtFormat()->isYearMonthDay($a->argv[$x])) {
|
||||
$is_a_date_query = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert query string to array. remove friendica args
|
||||
$query_array = [];
|
||||
parse_str(parse_url(DI::args()->getQueryString(), PHP_URL_QUERY), $query_array);
|
||||
|
||||
// fetch last used network view and redirect if needed
|
||||
if (!$is_a_date_query) {
|
||||
$sel_nets = $_GET['nets'] ?? '';
|
||||
$sel_tabs = network_query_get_sel_tab($a);
|
||||
$sel_groups = network_query_get_sel_group($a);
|
||||
$last_sel_tabs = DI::pConfig()->get(local_user(), 'network.view', 'tab.selected');
|
||||
|
||||
$remember_tab = ($sel_tabs[0] === 'active' && is_array($last_sel_tabs) && $last_sel_tabs[0] !== 'active');
|
||||
|
||||
$net_baseurl = '/network';
|
||||
$net_args = [];
|
||||
|
||||
if ($sel_groups !== false) {
|
||||
$net_baseurl .= '/' . $sel_groups;
|
||||
}
|
||||
|
||||
if ($remember_tab) {
|
||||
// redirect if current selected tab is '/network' and
|
||||
// last selected tab is _not_ '/network?order=activity'.
|
||||
// and this isn't a date query
|
||||
|
||||
$tab_args = [
|
||||
'order=activity', //all
|
||||
'order=post', //postord
|
||||
'conv=1', //conv
|
||||
'new=1', //new
|
||||
'star=1', //starred
|
||||
'bmark=1', //bookmarked
|
||||
];
|
||||
|
||||
$k = array_search('active', $last_sel_tabs);
|
||||
|
||||
if ($k != 3) {
|
||||
// parse out tab queries
|
||||
$dest_qa = [];
|
||||
$dest_qs = $tab_args[$k];
|
||||
parse_str($dest_qs, $dest_qa);
|
||||
$net_args = array_merge($net_args, $dest_qa);
|
||||
} else {
|
||||
$remember_tab = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($sel_nets) {
|
||||
$net_args['nets'] = $sel_nets;
|
||||
}
|
||||
|
||||
if ($remember_tab) {
|
||||
$net_args = array_merge($query_array, $net_args);
|
||||
$net_queries = http_build_query($net_args);
|
||||
|
||||
$redir_url = ($net_queries ? $net_baseurl . '?' . $net_queries : $net_baseurl);
|
||||
|
||||
DI::baseUrl()->redirect($redir_url);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty(DI::page()['aside'])) {
|
||||
DI::page()['aside'] = '';
|
||||
}
|
||||
|
||||
DI::page()['aside'] .= Group::sidebarWidget('network/0', 'network', 'standard', $group_id);
|
||||
DI::page()['aside'] .= ForumManager::widget(local_user(), $cid);
|
||||
DI::page()['aside'] .= Widget::postedByYear('network', local_user(), false);
|
||||
DI::page()['aside'] .= Widget::networks('network', $_GET['nets'] ?? '');
|
||||
DI::page()['aside'] .= Widget\SavedSearches::getHTML(DI::args()->getQueryString());
|
||||
DI::page()['aside'] .= Widget::fileAs('network', $_GET['file'] ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return selected tab from query
|
||||
*
|
||||
* urls -> returns
|
||||
* '/network' => $no_active = 'active'
|
||||
* '/network?order=activity' => $activity_active = 'active'
|
||||
* '/network?order=post' => $postord_active = 'active'
|
||||
* '/network?conv=1', => $conv_active = 'active'
|
||||
* '/network?new=1', => $new_active = 'active'
|
||||
* '/network?star=1', => $starred_active = 'active'
|
||||
* '/network?bmark=1', => $bookmarked_active = 'active'
|
||||
*
|
||||
* @param App $a
|
||||
* @return array ($no_active, $activity_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active);
|
||||
*/
|
||||
function network_query_get_sel_tab(App $a)
|
||||
{
|
||||
$no_active = '';
|
||||
$starred_active = '';
|
||||
$new_active = '';
|
||||
$bookmarked_active = '';
|
||||
$all_active = '';
|
||||
$conv_active = '';
|
||||
$postord_active = '';
|
||||
|
||||
if (!empty($_GET['new'])) {
|
||||
$new_active = 'active';
|
||||
}
|
||||
|
||||
if (!empty($_GET['star'])) {
|
||||
$starred_active = 'active';
|
||||
}
|
||||
|
||||
if (!empty($_GET['bmark'])) {
|
||||
$bookmarked_active = 'active';
|
||||
}
|
||||
|
||||
if (!empty($_GET['conv'])) {
|
||||
$conv_active = 'active';
|
||||
}
|
||||
|
||||
if (($new_active == '') && ($starred_active == '') && ($bookmarked_active == '') && ($conv_active == '')) {
|
||||
$no_active = 'active';
|
||||
}
|
||||
|
||||
if ($no_active == 'active' && !empty($_GET['order'])) {
|
||||
switch($_GET['order']) {
|
||||
case 'post' : $postord_active = 'active'; $no_active=''; break;
|
||||
case 'activity' : $all_active = 'active'; $no_active=''; break;
|
||||
}
|
||||
}
|
||||
|
||||
return [$no_active, $all_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active];
|
||||
}
|
||||
|
||||
function network_query_get_sel_group(App $a)
|
||||
{
|
||||
$group = false;
|
||||
|
||||
if ($a->argc >= 2 && is_numeric($a->argv[1])) {
|
||||
$group = $a->argv[1];
|
||||
}
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pager data and returns SQL
|
||||
*
|
||||
* @param App $a The global App
|
||||
* @param Pager $pager
|
||||
* @param integer $update Used for the automatic reloading
|
||||
* @return string SQL with the appropriate LIMIT clause
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function networkPager(App $a, Pager $pager, $update)
|
||||
{
|
||||
if ($update) {
|
||||
// only setup pagination on initial page view
|
||||
return ' LIMIT 100';
|
||||
}
|
||||
|
||||
if (DI::mode()->isMobile()) {
|
||||
$itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network',
|
||||
DI::config()->get('system', 'itemspage_network_mobile'));
|
||||
} else {
|
||||
$itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_network',
|
||||
DI::config()->get('system', 'itemspage_network'));
|
||||
}
|
||||
|
||||
// now that we have the user settings, see if the theme forces
|
||||
// a maximum item number which is lower then the user choice
|
||||
if (($a->force_max_items > 0) && ($a->force_max_items < $itemspage_network)) {
|
||||
$itemspage_network = $a->force_max_items;
|
||||
}
|
||||
|
||||
$pager->setItemsPerPage($itemspage_network);
|
||||
|
||||
return sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets items as seen
|
||||
*
|
||||
* @param array $condition The array with the SQL condition
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function networkSetSeen($condition)
|
||||
{
|
||||
if (empty($condition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$unseen = Item::exists($condition);
|
||||
|
||||
if ($unseen) {
|
||||
Item::update(['unseen' => false], $condition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the conversation HTML
|
||||
*
|
||||
* @param App $a The global App
|
||||
* @param array $items Items of the conversation
|
||||
* @param Pager $pager
|
||||
* @param string $mode Display mode for the conversation
|
||||
* @param integer $update Used for the automatic reloading
|
||||
* @param string $ordering
|
||||
* @return string HTML of the conversation
|
||||
* @throws ImagickException
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function networkConversation(App $a, $items, Pager $pager, $mode, $update, $ordering = '')
|
||||
{
|
||||
// Set this so that the conversation function can find out contact info for our wall-wall items
|
||||
$a->page_contact = $a->contact;
|
||||
|
||||
if (!is_array($items)) {
|
||||
Logger::log("Expecting items to be an array. Got " . print_r($items, true));
|
||||
$items = [];
|
||||
}
|
||||
|
||||
$o = conversation($a, $items, $mode, $update, false, $ordering, local_user());
|
||||
|
||||
if (!$update) {
|
||||
if (DI::pConfig()->get(local_user(), 'system', 'infinite_scroll')) {
|
||||
$o .= HTML::scrollLoader();
|
||||
} else {
|
||||
$o .= $pager->renderMinimal(count($items));
|
||||
}
|
||||
}
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
function network_content(App $a, $update = 0, $parent = 0)
|
||||
{
|
||||
if (!local_user()) {
|
||||
return Login::form();
|
||||
}
|
||||
|
||||
/// @TODO Is this really necessary? $a is already available to hooks
|
||||
$arr = ['query' => DI::args()->getQueryString()];
|
||||
Hook::callAll('network_content_init', $arr);
|
||||
|
||||
if (!empty($_GET['new']) || !empty($_GET['file'])) {
|
||||
$o = networkFlatView($a, $update);
|
||||
} else {
|
||||
$o = networkThreadedView($a, $update, $parent);
|
||||
}
|
||||
|
||||
if ($o === '') {
|
||||
info("No items found");
|
||||
}
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the network content in flat view
|
||||
*
|
||||
* @param App $a The global App
|
||||
* @param integer $update Used for the automatic reloading
|
||||
* @return string HTML of the network content in flat view
|
||||
* @throws ImagickException
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @global Pager $pager
|
||||
*/
|
||||
function networkFlatView(App $a, $update = 0)
|
||||
{
|
||||
global $pager;
|
||||
// Rawmode is used for fetching new content at the end of the page
|
||||
$rawmode = (isset($_GET['mode']) && ($_GET['mode'] == 'raw'));
|
||||
|
||||
$o = '';
|
||||
|
||||
$file = $_GET['file'] ?? '';
|
||||
|
||||
if (!$update && !$rawmode) {
|
||||
$tabs = network_tabs($a);
|
||||
$o .= $tabs;
|
||||
|
||||
Nav::setSelected('network');
|
||||
|
||||
$x = [
|
||||
'is_owner' => true,
|
||||
'allow_location' => $a->user['allow_location'],
|
||||
'default_location' => $a->user['default-location'],
|
||||
'nickname' => $a->user['nickname'],
|
||||
'lockstate' => (is_array($a->user) &&
|
||||
(strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) ||
|
||||
strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
|
||||
'default_perms' => ACL::getDefaultUserPermissions($a->user),
|
||||
'acl' => ACL::getFullSelectorHTML(DI::page(), $a->user, true),
|
||||
'bang' => '',
|
||||
'visitor' => 'block',
|
||||
'profile_uid' => local_user(),
|
||||
'content' => '',
|
||||
];
|
||||
|
||||
$o .= status_editor($a, $x);
|
||||
|
||||
if (!DI::config()->get('theme', 'hide_eventlist')) {
|
||||
$o .= Profile::getBirthdays();
|
||||
$o .= Profile::getEventsReminderHTML();
|
||||
}
|
||||
}
|
||||
|
||||
$pager = new Pager(DI::l10n(), DI::args()->getQueryString());
|
||||
|
||||
networkPager($a, $pager, $update);
|
||||
|
||||
|
||||
if (strlen($file)) {
|
||||
$item_params = ['order' => ['uri-id' => true]];
|
||||
$term_condition = ['name' => $file, 'type' => Category::FILE, 'uid' => local_user()];
|
||||
$term_params = ['order' => ['tid' => true], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
|
||||
$result = DBA::select('category-view', ['uri-id'], $term_condition, $term_params);
|
||||
|
||||
$posts = [];
|
||||
while ($term = DBA::fetch($result)) {
|
||||
$posts[] = $term['uri-id'];
|
||||
}
|
||||
DBA::close($result);
|
||||
|
||||
if (count($posts) == 0) {
|
||||
return '';
|
||||
}
|
||||
$item_condition = ['uid' => local_user(), 'uri-id' => $posts];
|
||||
} else {
|
||||
$item_params = ['order' => ['id' => true]];
|
||||
$item_condition = ['uid' => local_user()];
|
||||
$item_params['limit'] = [$pager->getStart(), $pager->getItemsPerPage()];
|
||||
|
||||
networkSetSeen(['unseen' => true, 'uid' => local_user()]);
|
||||
}
|
||||
|
||||
$result = Item::selectForUser(local_user(), [], $item_condition, $item_params);
|
||||
$items = Item::inArray($result);
|
||||
$o .= networkConversation($a, $items, $pager, 'network-new', $update);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the network content in threaded view
|
||||
*
|
||||
* @param App $a The global App
|
||||
* @param integer $update Used for the automatic reloading
|
||||
* @param integer $parent
|
||||
* @return string HTML of the network content in flat view
|
||||
* @throws ImagickException
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @global Pager $pager
|
||||
*/
|
||||
function networkThreadedView(App $a, $update, $parent)
|
||||
{
|
||||
/// @TODO this will have to be converted to a static property of the converted Module\Network class
|
||||
global $pager;
|
||||
|
||||
// Rawmode is used for fetching new content at the end of the page
|
||||
$rawmode = (isset($_GET['mode']) AND ( $_GET['mode'] == 'raw'));
|
||||
|
||||
if (isset($_GET['last_received']) && isset($_GET['last_commented']) && isset($_GET['last_created']) && isset($_GET['last_id'])) {
|
||||
$last_received = DateTimeFormat::utc($_GET['last_received']);
|
||||
$last_commented = DateTimeFormat::utc($_GET['last_commented']);
|
||||
$last_created = DateTimeFormat::utc($_GET['last_created']);
|
||||
$last_id = intval($_GET['last_id']);
|
||||
} else {
|
||||
$last_received = '';
|
||||
$last_commented = '';
|
||||
$last_created = '';
|
||||
$last_id = 0;
|
||||
}
|
||||
|
||||
$datequery = $datequery2 = '';
|
||||
|
||||
$gid = 0;
|
||||
|
||||
$default_permissions = [];
|
||||
|
||||
if ($a->argc > 1) {
|
||||
for ($x = 1; $x < $a->argc; $x ++) {
|
||||
if (DI::dtFormat()->isYearMonthDay($a->argv[$x])) {
|
||||
if ($datequery) {
|
||||
$datequery2 = Strings::escapeHtml($a->argv[$x]);
|
||||
} else {
|
||||
$datequery = Strings::escapeHtml($a->argv[$x]);
|
||||
$_GET['order'] = 'post';
|
||||
}
|
||||
} elseif (intval($a->argv[$x])) {
|
||||
$gid = intval($a->argv[$x]);
|
||||
$default_permissions['allow_gid'] = [$gid];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$o = '';
|
||||
|
||||
$cid = intval($_GET['cid'] ?? 0);
|
||||
$star = intval($_GET['star'] ?? 0);
|
||||
$bmark = intval($_GET['bmark'] ?? 0);
|
||||
$conv = intval($_GET['conv'] ?? 0);
|
||||
$order = Strings::escapeTags(($_GET['order'] ?? '') ?: 'activity');
|
||||
$nets = $_GET['nets'] ?? '';
|
||||
|
||||
$allowedCids = [];
|
||||
if ($cid) {
|
||||
$allowedCids[] = (int) $cid;
|
||||
} elseif ($nets) {
|
||||
$condition = [
|
||||
'uid' => local_user(),
|
||||
'network' => $nets,
|
||||
'self' => false,
|
||||
'blocked' => false,
|
||||
'pending' => false,
|
||||
'archive' => false,
|
||||
'rel' => [Contact::SHARING, Contact::FRIEND],
|
||||
];
|
||||
$contactStmt = DBA::select('contact', ['id'], $condition);
|
||||
while ($contact = DBA::fetch($contactStmt)) {
|
||||
$allowedCids[] = (int) $contact['id'];
|
||||
}
|
||||
DBA::close($contactStmt);
|
||||
}
|
||||
|
||||
if (count($allowedCids)) {
|
||||
$default_permissions['allow_cid'] = $allowedCids;
|
||||
}
|
||||
|
||||
if (!$update && !$rawmode) {
|
||||
$tabs = network_tabs($a);
|
||||
$o .= $tabs;
|
||||
|
||||
Nav::setSelected('network');
|
||||
|
||||
$content = '';
|
||||
|
||||
if ($cid) {
|
||||
// If $cid belongs to a communitity forum or a privat goup,.add a mention to the status editor
|
||||
$condition = ["`id` = ? AND (`forum` OR `prv`)", $cid];
|
||||
$contact = DBA::selectFirst('contact', ['addr', 'nick'], $condition);
|
||||
if (DBA::isResult($contact)) {
|
||||
if ($contact['addr'] != '') {
|
||||
$content = '!' . $contact['addr'];
|
||||
} else {
|
||||
$content = '!' . $contact['nick'] . '+' . $cid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$x = [
|
||||
'is_owner' => true,
|
||||
'allow_location' => $a->user['allow_location'],
|
||||
'default_location' => $a->user['default-location'],
|
||||
'nickname' => $a->user['nickname'],
|
||||
'lockstate' => ($gid || $cid || $nets || (is_array($a->user) &&
|
||||
(strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) ||
|
||||
strlen($a->user['deny_cid']) || strlen($a->user['deny_gid']))) ? 'lock' : 'unlock'),
|
||||
'default_perms' => ACL::getDefaultUserPermissions($a->user),
|
||||
'acl' => ACL::getFullSelectorHTML(DI::page(), $a->user, true, $default_permissions),
|
||||
'bang' => (($gid || $cid || $nets) ? '!' : ''),
|
||||
'visitor' => 'block',
|
||||
'profile_uid' => local_user(),
|
||||
'content' => $content,
|
||||
];
|
||||
|
||||
$o .= status_editor($a, $x);
|
||||
}
|
||||
|
||||
// We don't have to deal with ACLs on this page. You're looking at everything
|
||||
// that belongs to you, hence you can see all of it. We will filter by group if
|
||||
// desired.
|
||||
|
||||
$sql_post_table = '';
|
||||
$sql_options = ($star ? " AND `thread`.`starred` " : '');
|
||||
$sql_options .= ($bmark ? sprintf(" AND `thread`.`post-type` = %d ", Item::PT_PAGE) : '');
|
||||
$sql_extra = $sql_options;
|
||||
$sql_extra2 = '';
|
||||
$sql_extra3 = '';
|
||||
$sql_table = '`thread`';
|
||||
$sql_parent = '`iid`';
|
||||
|
||||
if ($update) {
|
||||
$sql_table = '`item`';
|
||||
$sql_parent = '`parent`';
|
||||
$sql_post_table = " INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent`";
|
||||
}
|
||||
|
||||
$sql_nets = (($nets) ? sprintf(" AND $sql_table.`network` = '%s' ", DBA::escape($nets)) : '');
|
||||
$sql_tag_nets = (($nets) ? sprintf(" AND `item`.`network` = '%s' ", DBA::escape($nets)) : '');
|
||||
|
||||
if ($gid) {
|
||||
$group = DBA::selectFirst('group', ['name'], ['id' => $gid, 'uid' => local_user()]);
|
||||
if (!DBA::isResult($group)) {
|
||||
if ($update) {
|
||||
exit();
|
||||
}
|
||||
notice(DI::l10n()->t('No such group') . EOL);
|
||||
DI::baseUrl()->redirect('network/0');
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
$contacts = Group::expand(local_user(), [$gid]);
|
||||
|
||||
if ((is_array($contacts)) && count($contacts)) {
|
||||
$contact_str_self = '';
|
||||
|
||||
$contact_str = implode(',', $contacts);
|
||||
$self = DBA::selectFirst('contact', ['id'], ['uid' => local_user(), 'self' => true]);
|
||||
if (DBA::isResult($self)) {
|
||||
$contact_str_self = $self['id'];
|
||||
}
|
||||
|
||||
$sql_post_table .= " INNER JOIN `item` AS `temp1` ON `temp1`.`id` = " . $sql_table . "." . $sql_parent;
|
||||
$sql_extra3 .= " AND (`thread`.`contact-id` IN ($contact_str) ";
|
||||
$sql_extra3 .= " OR (`thread`.`contact-id` = '$contact_str_self' AND `temp1`.`allow_gid` LIKE '" . Strings::protectSprintf('%<' . intval($gid) . '>%') . "' AND `temp1`.`private`))";
|
||||
} else {
|
||||
$sql_extra3 .= " AND false ";
|
||||
info(DI::l10n()->t('Group is empty'));
|
||||
}
|
||||
|
||||
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), [
|
||||
'$title' => DI::l10n()->t('Group: %s', $group['name'])
|
||||
]) . $o;
|
||||
} elseif ($cid) {
|
||||
$fields = ['id', 'name', 'network', 'writable', 'nurl',
|
||||
'forum', 'prv', 'contact-type', 'addr', 'thumb', 'location'];
|
||||
$condition = ["`id` = ? AND (NOT `blocked` OR `pending`)", $cid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
if (DBA::isResult($contact)) {
|
||||
$sql_extra = " AND " . $sql_table . ".`contact-id` = " . intval($cid);
|
||||
|
||||
$entries[0] = [
|
||||
'id' => 'network',
|
||||
'name' => $contact['name'],
|
||||
'itemurl' => ($contact['addr'] ?? '') ?: $contact['nurl'],
|
||||
'thumb' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB),
|
||||
'details' => $contact['location'],
|
||||
];
|
||||
|
||||
$entries[0]['account_type'] = Contact::getAccountType($contact);
|
||||
|
||||
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('viewcontact_template.tpl'), [
|
||||
'contacts' => $entries,
|
||||
'id' => 'network',
|
||||
]) . $o;
|
||||
} else {
|
||||
notice(DI::l10n()->t('Invalid contact.') . EOL);
|
||||
DI::baseUrl()->redirect('network');
|
||||
// NOTREACHED
|
||||
}
|
||||
}
|
||||
|
||||
if (!$gid && !$cid && !$update && !DI::config()->get('theme', 'hide_eventlist')) {
|
||||
$o .= Profile::getBirthdays();
|
||||
$o .= Profile::getEventsReminderHTML();
|
||||
}
|
||||
|
||||
if ($datequery) {
|
||||
$sql_extra3 .= Strings::protectSprintf(sprintf(" AND $sql_table.received <= '%s' ",
|
||||
DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get()))));
|
||||
}
|
||||
if ($datequery2) {
|
||||
$sql_extra3 .= Strings::protectSprintf(sprintf(" AND $sql_table.received >= '%s' ",
|
||||
DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get()))));
|
||||
}
|
||||
|
||||
if ($conv) {
|
||||
$sql_extra3 .= " AND $sql_table.`mention`";
|
||||
}
|
||||
|
||||
// Normal conversation view
|
||||
if ($order === 'post') {
|
||||
$ordering = '`received`';
|
||||
$order_mode = 'received';
|
||||
} else {
|
||||
$ordering = '`commented`';
|
||||
$order_mode = 'commented';
|
||||
}
|
||||
|
||||
$sql_order = "$sql_table.$ordering";
|
||||
|
||||
if (!empty($_GET['offset'])) {
|
||||
$sql_range = sprintf(" AND $sql_order <= '%s'", DBA::escape($_GET['offset']));
|
||||
} else {
|
||||
$sql_range = '';
|
||||
}
|
||||
|
||||
$pager = new Pager(DI::l10n(), DI::args()->getQueryString());
|
||||
|
||||
$pager_sql = networkPager($a, $pager, $update);
|
||||
|
||||
$last_date = '';
|
||||
|
||||
switch ($order_mode) {
|
||||
case 'received':
|
||||
if ($last_received != '') {
|
||||
$last_date = $last_received;
|
||||
$sql_range .= sprintf(" AND $sql_table.`received` < '%s'", DBA::escape($last_received));
|
||||
$pager->setPage(1);
|
||||
$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
|
||||
}
|
||||
break;
|
||||
case 'commented':
|
||||
if ($last_commented != '') {
|
||||
$last_date = $last_commented;
|
||||
$sql_range .= sprintf(" AND $sql_table.`commented` < '%s'", DBA::escape($last_commented));
|
||||
$pager->setPage(1);
|
||||
$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
|
||||
}
|
||||
break;
|
||||
case 'created':
|
||||
if ($last_created != '') {
|
||||
$last_date = $last_created;
|
||||
$sql_range .= sprintf(" AND $sql_table.`created` < '%s'", DBA::escape($last_created));
|
||||
$pager->setPage(1);
|
||||
$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
|
||||
}
|
||||
break;
|
||||
case 'id':
|
||||
if (($last_id > 0) && ($sql_table == '`thread`')) {
|
||||
$sql_range .= sprintf(" AND $sql_table.`iid` < '%s'", DBA::escape($last_id));
|
||||
$pager->setPage(1);
|
||||
$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Fetch a page full of parent items for this page
|
||||
if ($update) {
|
||||
if (!empty($parent)) {
|
||||
// Load only a single thread
|
||||
$sql_extra4 = "`item`.`id` = ".intval($parent);
|
||||
} else {
|
||||
// Load all unseen items
|
||||
$sql_extra4 = "`item`.`unseen`";
|
||||
if (DI::config()->get("system", "like_no_comment")) {
|
||||
$sql_extra4 .= " AND `item`.`gravity` IN (" . GRAVITY_PARENT . "," . GRAVITY_COMMENT . ")";
|
||||
}
|
||||
if ($order === 'post') {
|
||||
// Only show toplevel posts when updating posts in this order mode
|
||||
$sql_extra4 .= " AND `item`.`id` = `item`.`parent`";
|
||||
}
|
||||
}
|
||||
|
||||
$r = q("SELECT `item`.`parent-uri` AS `uri`, `item`.`parent` AS `item_id`, $sql_order AS `order_date`
|
||||
FROM `item` $sql_post_table
|
||||
STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
|
||||
AND (NOT `contact`.`blocked` OR `contact`.`pending`)
|
||||
AND (`item`.`gravity` != %d
|
||||
OR `contact`.`uid` = `item`.`uid` AND `contact`.`self`
|
||||
OR `contact`.`rel` IN (%d, %d) AND NOT `contact`.`readonly`)
|
||||
LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = %d
|
||||
WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`deleted`
|
||||
AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
|
||||
AND NOT `item`.`moderated` AND $sql_extra4
|
||||
$sql_extra3 $sql_extra $sql_range $sql_nets
|
||||
ORDER BY `order_date` DESC LIMIT 100",
|
||||
intval(GRAVITY_PARENT),
|
||||
intval(Contact::SHARING),
|
||||
intval(Contact::FRIEND),
|
||||
intval(local_user()),
|
||||
intval(local_user())
|
||||
);
|
||||
} else {
|
||||
$r = q("SELECT `item`.`uri`, `thread`.`iid` AS `item_id`, $sql_order AS `order_date`
|
||||
FROM `thread` $sql_post_table
|
||||
STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id`
|
||||
AND (NOT `contact`.`blocked` OR `contact`.`pending`)
|
||||
STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid`
|
||||
AND (`item`.`gravity` != %d
|
||||
OR `contact`.`uid` = `item`.`uid` AND `contact`.`self`
|
||||
OR `contact`.`rel` IN (%d, %d) AND NOT `contact`.`readonly`)
|
||||
LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = %d
|
||||
WHERE `thread`.`uid` = %d AND `thread`.`visible` AND NOT `thread`.`deleted`
|
||||
AND NOT `thread`.`moderated`
|
||||
AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
|
||||
$sql_extra2 $sql_extra3 $sql_range $sql_extra $sql_nets
|
||||
ORDER BY `order_date` DESC $pager_sql",
|
||||
intval(GRAVITY_PARENT),
|
||||
intval(Contact::SHARING),
|
||||
intval(Contact::FRIEND),
|
||||
intval(local_user()),
|
||||
intval(local_user())
|
||||
);
|
||||
}
|
||||
|
||||
// Only show it when unfiltered (no groups, no networks, ...)
|
||||
if (in_array($nets, ['', Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS]) && (strlen($sql_extra . $sql_extra2 . $sql_extra3) == 0)) {
|
||||
if (DBA::isResult($r)) {
|
||||
$top_limit = current($r)['order_date'];
|
||||
$bottom_limit = end($r)['order_date'];
|
||||
if (empty($_SESSION['network_last_top_limit']) || ($_SESSION['network_last_top_limit'] < $top_limit)) {
|
||||
$_SESSION['network_last_top_limit'] = $top_limit;
|
||||
}
|
||||
} else {
|
||||
$top_limit = $bottom_limit = DateTimeFormat::utcNow();
|
||||
}
|
||||
|
||||
// When checking for updates we need to fetch from the newest date to the newest date before
|
||||
// Only do this, when the last stored date isn't too long ago (10 times the update interval)
|
||||
$browser_update = DI::pConfig()->get(local_user(), 'system', 'update_interval', 40000) / 1000;
|
||||
|
||||
if (($browser_update > 0) && $update && !empty($_SESSION['network_last_date']) &&
|
||||
(($bottom_limit < $_SESSION['network_last_date']) || ($top_limit == $bottom_limit)) &&
|
||||
((time() - $_SESSION['network_last_date_timestamp']) < ($browser_update * 10))) {
|
||||
$bottom_limit = $_SESSION['network_last_date'];
|
||||
}
|
||||
$_SESSION['network_last_date'] = Session::get('network_last_top_limit', $top_limit);
|
||||
$_SESSION['network_last_date_timestamp'] = time();
|
||||
|
||||
if ($last_date > $top_limit) {
|
||||
$top_limit = $last_date;
|
||||
} elseif ($pager->getPage() == 1) {
|
||||
// Highest possible top limit when we are on the first page
|
||||
$top_limit = DateTimeFormat::utcNow();
|
||||
}
|
||||
|
||||
$items = DBA::p("SELECT `item`.`parent-uri` AS `uri`, 0 AS `item_id`, `item`.$ordering AS `order_date`, `author`.`url` AS `author-link` FROM `item`
|
||||
STRAIGHT_JOIN (SELECT `uri-id` FROM `tag-search-view` WHERE `name` IN
|
||||
(SELECT SUBSTR(`term`, 2) FROM `search` WHERE `uid` = ? AND `term` LIKE '#%') AND `uid` = 0) AS `tag-search`
|
||||
ON `item`.`uri-id` = `tag-search`.`uri-id`
|
||||
STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `item`.`author-id`
|
||||
WHERE `item`.`uid` = 0 AND `item`.$ordering < ? AND `item`.$ordering > ? AND `item`.`gravity` = ?
|
||||
AND NOT `author`.`hidden` AND NOT `author`.`blocked`" . $sql_tag_nets,
|
||||
local_user(), $top_limit, $bottom_limit, GRAVITY_PARENT);
|
||||
|
||||
$data = DBA::toArray($items);
|
||||
|
||||
if (count($data) > 0) {
|
||||
$tag_top_limit = current($data)['order_date'];
|
||||
if ($_SESSION['network_last_date'] < $tag_top_limit) {
|
||||
$_SESSION['network_last_date'] = $tag_top_limit;
|
||||
}
|
||||
|
||||
Logger::log('Tagged items: ' . count($data) . ' - ' . $bottom_limit . ' - ' . $top_limit . ' - ' . local_user().' - '.(int)$update);
|
||||
$s = [];
|
||||
foreach ($r as $item) {
|
||||
$s[$item['uri']] = $item;
|
||||
}
|
||||
foreach ($data as $item) {
|
||||
// Don't show hash tag posts from blocked or ignored contacts
|
||||
$condition = ["`nurl` = ? AND `uid` = ? AND (`blocked` OR `readonly`)",
|
||||
Strings::normaliseLink($item['author-link']), local_user()];
|
||||
if (!DBA::exists('contact', $condition)) {
|
||||
$s[$item['uri']] = $item;
|
||||
}
|
||||
}
|
||||
$r = $s;
|
||||
}
|
||||
}
|
||||
|
||||
$parents_str = '';
|
||||
$date_offset = '';
|
||||
|
||||
$items = $r;
|
||||
|
||||
if (DBA::isResult($items)) {
|
||||
$parents_arr = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($date_offset < $item['order_date']) {
|
||||
$date_offset = $item['order_date'];
|
||||
}
|
||||
if (!in_array($item['item_id'], $parents_arr) && ($item['item_id'] > 0)) {
|
||||
$parents_arr[] = $item['item_id'];
|
||||
}
|
||||
}
|
||||
$parents_str = implode(', ', $parents_arr);
|
||||
}
|
||||
|
||||
if (!empty($_GET['offset'])) {
|
||||
$date_offset = $_GET['offset'];
|
||||
}
|
||||
|
||||
$query_string = DI::args()->getQueryString();
|
||||
if ($date_offset && !preg_match('/[?&].offset=/', $query_string)) {
|
||||
$query_string .= '&offset=' . urlencode($date_offset);
|
||||
}
|
||||
|
||||
$pager->setQueryString($query_string);
|
||||
|
||||
// We aren't going to try and figure out at the item, group, and page
|
||||
// level which items you've seen and which you haven't. If you're looking
|
||||
// at the top level network page just mark everything seen.
|
||||
|
||||
if (!$gid && !$cid && !$star) {
|
||||
$condition = ['unseen' => true, 'uid' => local_user()];
|
||||
networkSetSeen($condition);
|
||||
} elseif ($parents_str) {
|
||||
$condition = ["`uid` = ? AND `unseen` AND `parent` IN (" . DBA::escape($parents_str) . ")", local_user()];
|
||||
networkSetSeen($condition);
|
||||
}
|
||||
|
||||
|
||||
$mode = 'network';
|
||||
$o .= networkConversation($a, $items, $pager, $mode, $update, $ordering);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the network tabs menu
|
||||
*
|
||||
* @param App $a The global App
|
||||
* @return string Html of the networktab
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function network_tabs(App $a)
|
||||
{
|
||||
// item filter tabs
|
||||
/// @TODO fix this logic, reduce duplication
|
||||
/// DI::page()['content'] .= '<div class="tabs-wrapper">';
|
||||
list($no_active, $all_active, $post_active, $conv_active, $new_active, $starred_active, $bookmarked_active) = network_query_get_sel_tab($a);
|
||||
|
||||
// if no tabs are selected, defaults to activitys
|
||||
if ($no_active == 'active') {
|
||||
$all_active = 'active';
|
||||
}
|
||||
|
||||
$cmd = DI::args()->getCommand();
|
||||
|
||||
$def_param = [];
|
||||
if (!empty($_GET['cid'])) {
|
||||
$def_param['cid'] = $_GET['cid'];
|
||||
}
|
||||
|
||||
// tabs
|
||||
$tabs = [
|
||||
[
|
||||
'label' => DI::l10n()->t('Latest Activity'),
|
||||
'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['order' => 'activity'])),
|
||||
'sel' => $all_active,
|
||||
'title' => DI::l10n()->t('Sort by latest activity'),
|
||||
'id' => 'activity-order-tab',
|
||||
'accesskey' => 'e',
|
||||
],
|
||||
[
|
||||
'label' => DI::l10n()->t('Latest Posts'),
|
||||
'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['order' => 'post'])),
|
||||
'sel' => $post_active,
|
||||
'title' => DI::l10n()->t('Sort by post received date'),
|
||||
'id' => 'post-order-tab',
|
||||
'accesskey' => 't',
|
||||
],
|
||||
];
|
||||
|
||||
$tabs[] = [
|
||||
'label' => DI::l10n()->t('Personal'),
|
||||
'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['conv' => true])),
|
||||
'sel' => $conv_active,
|
||||
'title' => DI::l10n()->t('Posts that mention or involve you'),
|
||||
'id' => 'personal-tab',
|
||||
'accesskey' => 'r',
|
||||
];
|
||||
|
||||
if (Feature::isEnabled(local_user(), 'new_tab')) {
|
||||
$tabs[] = [
|
||||
'label' => DI::l10n()->t('New'),
|
||||
'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['new' => true])),
|
||||
'sel' => $new_active,
|
||||
'title' => DI::l10n()->t('Activity Stream - by date'),
|
||||
'id' => 'activitiy-by-date-tab',
|
||||
'accesskey' => 'w',
|
||||
];
|
||||
}
|
||||
|
||||
if (Feature::isEnabled(local_user(), 'link_tab')) {
|
||||
$tabs[] = [
|
||||
'label' => DI::l10n()->t('Shared Links'),
|
||||
'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['bmark' => true])),
|
||||
'sel' => $bookmarked_active,
|
||||
'title' => DI::l10n()->t('Interesting Links'),
|
||||
'id' => 'shared-links-tab',
|
||||
'accesskey' => 'b',
|
||||
];
|
||||
}
|
||||
|
||||
$tabs[] = [
|
||||
'label' => DI::l10n()->t('Starred'),
|
||||
'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['star' => true])),
|
||||
'sel' => $starred_active,
|
||||
'title' => DI::l10n()->t('Favourite Posts'),
|
||||
'id' => 'starred-posts-tab',
|
||||
'accesskey' => 'm',
|
||||
];
|
||||
|
||||
// save selected tab, but only if not in file mode
|
||||
if (empty($_GET['file'])) {
|
||||
DI::pConfig()->set(local_user(), 'network.view', 'tab.selected', [
|
||||
$all_active, $post_active, $conv_active, $new_active, $starred_active, $bookmarked_active
|
||||
]);
|
||||
}
|
||||
|
||||
$arr = ['tabs' => $tabs];
|
||||
Hook::callAll('network_tabs', $arr);
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
|
||||
|
||||
return Renderer::replaceMacros($tpl, ['$tabs' => $arr['tabs']]);
|
||||
|
||||
// --- end item filter tabs
|
||||
}
|
||||
|
||||
/**
|
||||
* Network hook into the HTML head to enable infinite scroll.
|
||||
*
|
||||
* Since the HTML head is built after the module content has been generated, we need to retrieve the base query string
|
||||
* of the page to make the correct asynchronous call. This is obtained through the Pager that was instantiated in
|
||||
* networkThreadedView or networkFlatView.
|
||||
*
|
||||
* @param App $a
|
||||
* @param string $htmlhead The head tag HTML string
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @global Pager $pager
|
||||
*/
|
||||
function network_infinite_scroll_head(App $a, &$htmlhead)
|
||||
{
|
||||
/// @TODO this will have to be converted to a static property of the converted Module\Network class
|
||||
/**
|
||||
* @var $pager Pager
|
||||
*/
|
||||
global $pager;
|
||||
|
||||
if (DI::pConfig()->get(local_user(), 'system', 'infinite_scroll')
|
||||
&& ($_GET['mode'] ?? '') != 'minimal'
|
||||
) {
|
||||
$tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl');
|
||||
$htmlhead .= Renderer::replaceMacros($tpl, [
|
||||
'$pageno' => $pager->getPage(),
|
||||
'$reload_uri' => $pager->getBaseQueryString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ function notes_init(App $a)
|
|||
function notes_content(App $a, $update = false)
|
||||
{
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ function notes_content(App $a, $update = false)
|
|||
'default_location' => $a->user['default-location'],
|
||||
'nickname' => $a->user['nickname'],
|
||||
'lockstate' => 'lock',
|
||||
'acl' => '',
|
||||
'acl' => \Friendica\Core\ACL::getSelfOnlyHTML(local_user(), DI::l10n()->t('Personal notes are visible only by yourself.')),
|
||||
'bang' => '',
|
||||
'visitor' => 'block',
|
||||
'profile_uid' => local_user(),
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ use Friendica\App;
|
|||
use Friendica\Core\Renderer;
|
||||
use Friendica\DI;
|
||||
use Friendica\Module\Security\Login;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
function oexchange_init(App $a) {
|
||||
|
|
@ -45,7 +44,6 @@ function oexchange_content(App $a) {
|
|||
}
|
||||
|
||||
if (($a->argc > 1) && $a->argv[1] === 'done') {
|
||||
info(DI::l10n()->t('Post successful.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +56,7 @@ function oexchange_content(App $a) {
|
|||
$tags = ((!empty($_REQUEST['tags']))
|
||||
? '&tags=' . urlencode(Strings::escapeTags(trim($_REQUEST['tags']))) : '');
|
||||
|
||||
$s = Network::fetchUrl(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags);
|
||||
$s = DI::httpRequest()->fetch(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags);
|
||||
|
||||
if (!strlen($s)) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -23,13 +23,11 @@ use Friendica\App;
|
|||
use Friendica\Core\Protocol;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Network\Probe;
|
||||
use Friendica\Util\Network;
|
||||
|
||||
function ostatus_subscribe_content(App $a)
|
||||
{
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
DI::baseUrl()->redirect('ostatus_subscribe');
|
||||
// NOTREACHED
|
||||
}
|
||||
|
|
@ -47,8 +45,7 @@ function ostatus_subscribe_content(App $a)
|
|||
return $o . DI::l10n()->t('No contact provided.');
|
||||
}
|
||||
|
||||
$contact = Probe::uri($_REQUEST['url']);
|
||||
|
||||
$contact = Contact::getByURL($_REQUEST['url']);
|
||||
if (!$contact) {
|
||||
DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact');
|
||||
return $o . DI::l10n()->t('Couldn\'t fetch information for contact.');
|
||||
|
|
@ -57,7 +54,7 @@ function ostatus_subscribe_content(App $a)
|
|||
$api = $contact['baseurl'] . '/api/';
|
||||
|
||||
// Fetching friends
|
||||
$curlResult = Network::curl($api . 'statuses/friends.json?screen_name=' . $contact['nick']);
|
||||
$curlResult = DI::httpRequest()->get($api . 'statuses/friends.json?screen_name=' . $contact['nick']);
|
||||
|
||||
if (!$curlResult->isSuccess()) {
|
||||
DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact');
|
||||
|
|
@ -89,9 +86,9 @@ function ostatus_subscribe_content(App $a)
|
|||
|
||||
$o .= '<p>' . $counter . '/' . $total . ': ' . $url;
|
||||
|
||||
$probed = Probe::uri($url);
|
||||
$probed = Contact::getByURL($url);
|
||||
if ($probed['network'] == Protocol::OSTATUS) {
|
||||
$result = Contact::createFromProbe($uid, $url, true, Protocol::OSTATUS);
|
||||
$result = Contact::createFromProbe($a->user, $probed['url'], true, Protocol::OSTATUS);
|
||||
if ($result['success']) {
|
||||
$o .= ' - ' . DI::l10n()->t('success');
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -24,10 +24,11 @@
|
|||
*/
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Content\PageInfo;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\DI;
|
||||
use Friendica\Util\ParseUrl;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
|
|
@ -84,19 +85,11 @@ function parse_url_content(App $a)
|
|||
// Check if the URL is an image, video or audio file. If so format
|
||||
// the URL with the corresponding BBCode media tag
|
||||
// Fetch the header of the URL
|
||||
$curlResponse = Network::curl($url, false, ['novalidate' => true, 'nobody' => true]);
|
||||
$curlResponse = DI::httpRequest()->head($url);
|
||||
|
||||
if ($curlResponse->isSuccess()) {
|
||||
// Convert the header fields into an array
|
||||
$hdrs = [];
|
||||
$h = explode("\n", $curlResponse->getHeader());
|
||||
foreach ($h as $l) {
|
||||
$header = array_map('trim', explode(':', trim($l), 2));
|
||||
if (count($header) == 2) {
|
||||
list($k, $v) = $header;
|
||||
$hdrs[$k] = $v;
|
||||
}
|
||||
}
|
||||
$hdrs = $curlResponse->getHeaderArray();
|
||||
|
||||
$type = null;
|
||||
$content_type = '';
|
||||
$bbcode = '';
|
||||
|
|
@ -133,13 +126,17 @@ function parse_url_content(App $a)
|
|||
|
||||
$template = '[bookmark=%s]%s[/bookmark]%s';
|
||||
|
||||
$arr = ['url' => $url, 'text' => ''];
|
||||
$arr = ['url' => $url, 'format' => $format, 'text' => null];
|
||||
|
||||
Hook::callAll('parse_link', $arr);
|
||||
|
||||
if (strlen($arr['text'])) {
|
||||
echo $arr['text'];
|
||||
exit();
|
||||
if ($arr['text']) {
|
||||
if ($format == 'json') {
|
||||
System::jsonExit($arr['text']);
|
||||
} else {
|
||||
echo $arr['text'];
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// If there is already some content information submitted we don't
|
||||
|
|
@ -177,7 +174,7 @@ function parse_url_content(App $a)
|
|||
}
|
||||
|
||||
// Format it as BBCode attachment
|
||||
$info = add_page_info_data($siteinfo);
|
||||
$info = "\n" . PageInfo::getFooterFromData($siteinfo);
|
||||
|
||||
echo $info;
|
||||
|
||||
|
|
|
|||
193
mod/photos.php
|
|
@ -25,6 +25,7 @@ use Friendica\Content\Nav;
|
|||
use Friendica\Content\Pager;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Core\ACL;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Renderer;
|
||||
|
|
@ -46,7 +47,7 @@ use Friendica\Util\Crypto;
|
|||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Images;
|
||||
use Friendica\Util\Map;
|
||||
use Friendica\Util\Security;
|
||||
use Friendica\Security\Security;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Temporal;
|
||||
use Friendica\Util\XML;
|
||||
|
|
@ -82,7 +83,7 @@ function photos_init(App $a) {
|
|||
'$photo' => $profile['photo'],
|
||||
'$addr' => $profile['addr'] ?? '',
|
||||
'$account_type' => $account_type,
|
||||
'$about' => BBCode::convert($profile['about'] ?? ''),
|
||||
'$about' => BBCode::convert($profile['about']),
|
||||
]);
|
||||
|
||||
$albums = Photo::getAlbums($a->data['user']['uid']);
|
||||
|
|
@ -154,10 +155,6 @@ function photos_init(App $a) {
|
|||
|
||||
function photos_post(App $a)
|
||||
{
|
||||
Logger::log('mod-photos: photos_post: begin' , Logger::DEBUG);
|
||||
Logger::log('mod_photos: REQUEST ' . print_r($_REQUEST, true), Logger::DATA);
|
||||
Logger::log('mod_photos: FILES ' . print_r($_FILES, true), Logger::DATA);
|
||||
|
||||
$phototypes = Images::supportedTypes();
|
||||
|
||||
$can_post = false;
|
||||
|
|
@ -175,25 +172,42 @@ function photos_post(App $a)
|
|||
}
|
||||
|
||||
if (!$can_post) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
exit();
|
||||
}
|
||||
|
||||
$owner_record = User::getOwnerDataById($page_owner_uid);
|
||||
|
||||
if (!$owner_record) {
|
||||
notice(DI::l10n()->t('Contact information unavailable') . EOL);
|
||||
Logger::log('photos_post: unable to locate contact record for page owner. uid=' . $page_owner_uid);
|
||||
notice(DI::l10n()->t('Contact information unavailable'));
|
||||
DI::logger()->info('photos_post: unable to locate contact record for page owner. uid=' . $page_owner_uid);
|
||||
exit();
|
||||
}
|
||||
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
$str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $owner_record['allow_cid'] ?? '';
|
||||
$str_group_allow = isset($_REQUEST['group_allow']) ? $aclFormatter->toString($_REQUEST['group_allow']) : $owner_record['allow_gid'] ?? '';
|
||||
$str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $owner_record['deny_cid'] ?? '';
|
||||
$str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $owner_record['deny_gid'] ?? '';
|
||||
|
||||
$visibility = $_REQUEST['visibility'] ?? '';
|
||||
if ($visibility === 'public') {
|
||||
// The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected
|
||||
$str_contact_allow = $str_group_allow = $str_contact_deny = $str_group_deny = '';
|
||||
} else if ($visibility === 'custom') {
|
||||
// Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL
|
||||
// case that would make it public. So we always append the author's contact id to the allowed contacts.
|
||||
// See https://github.com/friendica/friendica/issues/9672
|
||||
$str_contact_allow .= $aclFormatter->toString(Contact::getPublicIdByUserId($page_owner_uid));
|
||||
}
|
||||
|
||||
if ($a->argc > 3 && $a->argv[2] === 'album') {
|
||||
if (!Strings::isHex($a->argv[3])) {
|
||||
DI::baseUrl()->redirect('photos/' . $a->data['user']['nickname'] . '/album');
|
||||
}
|
||||
$album = hex2bin($a->argv[3]);
|
||||
|
||||
if ($album === DI::l10n()->t('Profile Photos') || $album === 'Contact Photos' || $album === DI::l10n()->t('Contact Photos')) {
|
||||
if ($album === DI::l10n()->t('Profile Photos') || $album === Photo::CONTACT_PHOTOS || $album === DI::l10n()->t(Photo::CONTACT_PHOTOS)) {
|
||||
DI::baseUrl()->redirect($_SESSION['photo_return']);
|
||||
return; // NOTREACHED
|
||||
}
|
||||
|
|
@ -204,7 +218,7 @@ function photos_post(App $a)
|
|||
);
|
||||
|
||||
if (!DBA::isResult($r)) {
|
||||
notice(DI::l10n()->t('Album not found.') . EOL);
|
||||
notice(DI::l10n()->t('Album not found.'));
|
||||
DI::baseUrl()->redirect('photos/' . $a->data['user']['nickname'] . '/album');
|
||||
return; // NOTREACHED
|
||||
}
|
||||
|
|
@ -295,9 +309,8 @@ function photos_post(App $a)
|
|||
|
||||
// Update the photo albums cache
|
||||
Photo::clearAlbumCache($page_owner_uid);
|
||||
notice('Successfully deleted the photo.');
|
||||
} else {
|
||||
notice('Failed to delete the photo.');
|
||||
notice(DI::l10n()->t('Failed to delete the photo.'));
|
||||
DI::baseUrl()->redirect('photos/' . $a->argv[1] . '/image/' . $a->argv[3]);
|
||||
}
|
||||
|
||||
|
|
@ -313,13 +326,6 @@ function photos_post(App $a)
|
|||
$albname = !empty($_POST['albname']) ? trim($_POST['albname']) : '';
|
||||
$origaname = !empty($_POST['origaname']) ? Strings::escapeTags(trim($_POST['origaname'])) : '';
|
||||
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
|
||||
$str_group_allow = !empty($_POST['group_allow']) ? $aclFormatter->toString($_POST['group_allow']) : '';
|
||||
$str_contact_allow = !empty($_POST['contact_allow']) ? $aclFormatter->toString($_POST['contact_allow']) : '';
|
||||
$str_group_deny = !empty($_POST['group_deny']) ? $aclFormatter->toString($_POST['group_deny']) : '';
|
||||
$str_contact_deny = !empty($_POST['contact_deny']) ? $aclFormatter->toString($_POST['contact_deny']) : '';
|
||||
|
||||
$resource_id = $a->argv[3];
|
||||
|
||||
if (!strlen($albname)) {
|
||||
|
|
@ -395,7 +401,6 @@ function photos_post(App $a)
|
|||
$arr['guid'] = System::createUUID();
|
||||
$arr['uid'] = $page_owner_uid;
|
||||
$arr['uri'] = $uri;
|
||||
$arr['parent-uri'] = $uri;
|
||||
$arr['post-type'] = Item::PT_IMAGE;
|
||||
$arr['wall'] = 1;
|
||||
$arr['resource-id'] = $photo['resource-id'];
|
||||
|
|
@ -509,9 +514,9 @@ function photos_post(App $a)
|
|||
|
||||
if ($profile) {
|
||||
if (!empty($contact)) {
|
||||
$taginfo[] = [$newname, $profile, $notify, $contact, '@[url=' . str_replace(',', '%2c', $profile) . ']' . $newname . '[/url]'];
|
||||
$taginfo[] = [$newname, $profile, $notify, $contact];
|
||||
} else {
|
||||
$taginfo[] = [$newname, $profile, $notify, null, '@[url=' . $profile . ']' . $newname . '[/url]'];
|
||||
$taginfo[] = [$newname, $profile, $notify, null];
|
||||
}
|
||||
|
||||
$profile = str_replace(',', '%2c', $profile);
|
||||
|
|
@ -560,7 +565,6 @@ function photos_post(App $a)
|
|||
$arr['guid'] = System::createUUID();
|
||||
$arr['uid'] = $page_owner_uid;
|
||||
$arr['uri'] = $uri;
|
||||
$arr['parent-uri'] = $uri;
|
||||
$arr['wall'] = 1;
|
||||
$arr['contact-id'] = $owner_record['id'];
|
||||
$arr['owner-name'] = $owner_record['name'];
|
||||
|
|
@ -579,7 +583,6 @@ function photos_post(App $a)
|
|||
$arr['gravity'] = GRAVITY_PARENT;
|
||||
$arr['object-type'] = Activity\ObjectType::PERSON;
|
||||
$arr['target-type'] = Activity\ObjectType::IMAGE;
|
||||
$arr['tag'] = $tagged[4];
|
||||
$arr['inform'] = $tagged[2];
|
||||
$arr['origin'] = 1;
|
||||
$arr['body'] = DI::l10n()->t('%1$s was tagged in %2$s by %3$s', '[url=' . $tagged[1] . ']' . $tagged[0] . '[/url]', '[url=' . DI::baseUrl() . '/photos/' . $owner_record['nickname'] . '/image/' . $photo['resource-id'] . ']' . DI::l10n()->t('a photo') . '[/url]', '[url=' . $owner_record['url'] . ']' . $owner_record['name'] . '[/url]') ;
|
||||
|
|
@ -642,18 +645,6 @@ function photos_post(App $a)
|
|||
$visible = 0;
|
||||
}
|
||||
|
||||
$group_allow = $_REQUEST['group_allow'] ?? [];
|
||||
$contact_allow = $_REQUEST['contact_allow'] ?? [];
|
||||
$group_deny = $_REQUEST['group_deny'] ?? [];
|
||||
$contact_deny = $_REQUEST['contact_deny'] ?? [];
|
||||
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
|
||||
$str_group_allow = $aclFormatter->toString(is_array($group_allow) ? $group_allow : explode(',', $group_allow));
|
||||
$str_contact_allow = $aclFormatter->toString(is_array($contact_allow) ? $contact_allow : explode(',', $contact_allow));
|
||||
$str_group_deny = $aclFormatter->toString(is_array($group_deny) ? $group_deny : explode(',', $group_deny));
|
||||
$str_contact_deny = $aclFormatter->toString(is_array($contact_deny) ? $contact_deny : explode(',', $contact_deny));
|
||||
|
||||
$ret = ['src' => '', 'filename' => '', 'filesize' => 0, 'type' => ''];
|
||||
|
||||
Hook::callAll('photo_post_file', $ret);
|
||||
|
|
@ -677,21 +668,21 @@ function photos_post(App $a)
|
|||
if ($error !== UPLOAD_ERR_OK) {
|
||||
switch ($error) {
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
notice(DI::l10n()->t('Image exceeds size limit of %s', ini_get('upload_max_filesize')) . EOL);
|
||||
notice(DI::l10n()->t('Image exceeds size limit of %s', ini_get('upload_max_filesize')));
|
||||
break;
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($_REQUEST['MAX_FILE_SIZE'] ?? 0)) . EOL);
|
||||
notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($_REQUEST['MAX_FILE_SIZE'] ?? 0)));
|
||||
break;
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
notice(DI::l10n()->t('Image upload didn\'t complete, please try again') . EOL);
|
||||
notice(DI::l10n()->t('Image upload didn\'t complete, please try again'));
|
||||
break;
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
notice(DI::l10n()->t('Image file is missing') . EOL);
|
||||
notice(DI::l10n()->t('Image file is missing'));
|
||||
break;
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
case UPLOAD_ERR_CANT_WRITE:
|
||||
case UPLOAD_ERR_EXTENSION:
|
||||
notice(DI::l10n()->t('Server can\'t accept new file upload at this time, please contact your administrator') . EOL);
|
||||
notice(DI::l10n()->t('Server can\'t accept new file upload at this time, please contact your administrator'));
|
||||
break;
|
||||
}
|
||||
@unlink($src);
|
||||
|
|
@ -707,7 +698,7 @@ function photos_post(App $a)
|
|||
$maximagesize = DI::config()->get('system', 'maximagesize');
|
||||
|
||||
if ($maximagesize && ($filesize > $maximagesize)) {
|
||||
notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)) . EOL);
|
||||
notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)));
|
||||
@unlink($src);
|
||||
$foo = 0;
|
||||
Hook::callAll('photo_post_end', $foo);
|
||||
|
|
@ -715,7 +706,7 @@ function photos_post(App $a)
|
|||
}
|
||||
|
||||
if (!$filesize) {
|
||||
notice(DI::l10n()->t('Image file is empty.') . EOL);
|
||||
notice(DI::l10n()->t('Image file is empty.'));
|
||||
@unlink($src);
|
||||
$foo = 0;
|
||||
Hook::callAll('photo_post_end', $foo);
|
||||
|
|
@ -730,7 +721,7 @@ function photos_post(App $a)
|
|||
|
||||
if (!$image->isValid()) {
|
||||
Logger::log('mod/photos.php: photos_post(): unable to process image' , Logger::DEBUG);
|
||||
notice(DI::l10n()->t('Unable to process image.') . EOL);
|
||||
notice(DI::l10n()->t('Unable to process image.'));
|
||||
@unlink($src);
|
||||
$foo = 0;
|
||||
Hook::callAll('photo_post_end',$foo);
|
||||
|
|
@ -759,7 +750,7 @@ function photos_post(App $a)
|
|||
|
||||
if (!$r) {
|
||||
Logger::log('mod/photos.php: photos_post(): image store failed', Logger::DEBUG);
|
||||
notice(DI::l10n()->t('Image upload failed.') . EOL);
|
||||
notice(DI::l10n()->t('Image upload failed.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -779,7 +770,7 @@ function photos_post(App $a)
|
|||
|
||||
// Create item container
|
||||
$lat = $lon = null;
|
||||
if ($exif && $exif['GPS'] && Feature::isEnabled($page_owner_uid, 'photo_location')) {
|
||||
if (!empty($exif['GPS']) && Feature::isEnabled($page_owner_uid, 'photo_location')) {
|
||||
$lat = Photo::getGps($exif['GPS']['GPSLatitude'], $exif['GPS']['GPSLatitudeRef']);
|
||||
$lon = Photo::getGps($exif['GPS']['GPSLongitude'], $exif['GPS']['GPSLongitudeRef']);
|
||||
}
|
||||
|
|
@ -792,7 +783,6 @@ function photos_post(App $a)
|
|||
$arr['guid'] = System::createUUID();
|
||||
$arr['uid'] = $page_owner_uid;
|
||||
$arr['uri'] = $uri;
|
||||
$arr['parent-uri'] = $uri;
|
||||
$arr['type'] = 'photo';
|
||||
$arr['wall'] = 1;
|
||||
$arr['resource-id'] = $resource_id;
|
||||
|
|
@ -842,12 +832,12 @@ function photos_content(App $a)
|
|||
// photos/name/image/xxxxx/drop
|
||||
|
||||
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
|
||||
notice(DI::l10n()->t('Public access denied.') . EOL);
|
||||
notice(DI::l10n()->t('Public access denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($a->data['user'])) {
|
||||
notice(DI::l10n()->t('No photos selected') . EOL);
|
||||
notice(DI::l10n()->t('No photos selected'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -913,7 +903,7 @@ function photos_content(App $a)
|
|||
}
|
||||
|
||||
if ($a->data['user']['hidewall'] && (local_user() != $owner_uid) && !$remote_contact) {
|
||||
notice(DI::l10n()->t('Access to this item is restricted.') . EOL);
|
||||
notice(DI::l10n()->t('Access to this item is restricted.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -939,7 +929,7 @@ function photos_content(App $a)
|
|||
$albumselect .= '<option value="" ' . (!$selname ? ' selected="selected" ' : '') . '><current year></option>';
|
||||
if (!empty($a->data['albums'])) {
|
||||
foreach ($a->data['albums'] as $album) {
|
||||
if (($album['album'] === '') || ($album['album'] === 'Contact Photos') || ($album['album'] === DI::l10n()->t('Contact Photos'))) {
|
||||
if (($album['album'] === '') || ($album['album'] === Photo::CONTACT_PHOTOS) || ($album['album'] === DI::l10n()->t(Photo::CONTACT_PHOTOS))) {
|
||||
continue;
|
||||
}
|
||||
$selected = (($selname === $album['album']) ? ' selected="selected" ' : '');
|
||||
|
|
@ -989,8 +979,6 @@ function photos_content(App $a)
|
|||
'$uploadurl' => $ret['post_url'],
|
||||
|
||||
// ACL permissions box
|
||||
'$group_perms' => DI::l10n()->t('Show to Groups'),
|
||||
'$contact_perms' => DI::l10n()->t('Show to Contacts'),
|
||||
'$return_path' => DI::args()->getQueryString(),
|
||||
]);
|
||||
|
||||
|
|
@ -1042,7 +1030,6 @@ function photos_content(App $a)
|
|||
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
|
||||
'$method' => 'post',
|
||||
'$message' => DI::l10n()->t('Do you really want to delete this photo album and all its photos?'),
|
||||
'$extra_inputs' => [],
|
||||
'$confirm' => DI::l10n()->t('Delete Album'),
|
||||
'$confirm_url' => $drop_url,
|
||||
'$confirm_name' => 'dropalbum',
|
||||
|
|
@ -1052,7 +1039,7 @@ function photos_content(App $a)
|
|||
|
||||
// edit album name
|
||||
if ($cmd === 'edit') {
|
||||
if (($album !== DI::l10n()->t('Profile Photos')) && ($album !== 'Contact Photos') && ($album !== DI::l10n()->t('Contact Photos'))) {
|
||||
if (($album !== DI::l10n()->t('Profile Photos')) && ($album !== Photo::CONTACT_PHOTOS) && ($album !== DI::l10n()->t(Photo::CONTACT_PHOTOS))) {
|
||||
if ($can_post) {
|
||||
$edit_tpl = Renderer::getMarkupTemplate('album_edit.tpl');
|
||||
|
||||
|
|
@ -1069,7 +1056,7 @@ function photos_content(App $a)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (($album !== DI::l10n()->t('Profile Photos')) && ($album !== 'Contact Photos') && ($album !== DI::l10n()->t('Contact Photos')) && $can_post) {
|
||||
if (($album !== DI::l10n()->t('Profile Photos')) && ($album !== Photo::CONTACT_PHOTOS) && ($album !== DI::l10n()->t(Photo::CONTACT_PHOTOS)) && $can_post) {
|
||||
$edit = [DI::l10n()->t('Edit Album'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/edit'];
|
||||
$drop = [DI::l10n()->t('Drop Album'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/drop'];
|
||||
}
|
||||
|
|
@ -1138,7 +1125,7 @@ function photos_content(App $a)
|
|||
if (DBA::exists('photo', ['resource-id' => $datum, 'uid' => $owner_uid])) {
|
||||
notice(DI::l10n()->t('Permission denied. Access to this item may be restricted.'));
|
||||
} else {
|
||||
notice(DI::l10n()->t('Photo not available') . EOL);
|
||||
notice(DI::l10n()->t('Photo not available'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -1149,7 +1136,6 @@ function photos_content(App $a)
|
|||
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
|
||||
'$method' => 'post',
|
||||
'$message' => DI::l10n()->t('Do you really want to delete this photo?'),
|
||||
'$extra_inputs' => [],
|
||||
'$confirm' => DI::l10n()->t('Delete Photo'),
|
||||
'$confirm_url' => $drop_url,
|
||||
'$confirm_name' => 'delete',
|
||||
|
|
@ -1288,7 +1274,7 @@ function photos_content(App $a)
|
|||
}
|
||||
|
||||
if (!empty($link_item['parent']) && !empty($link_item['uid'])) {
|
||||
$condition = ["`parent` = ? AND `parent` != `id`", $link_item['parent']];
|
||||
$condition = ["`parent` = ? AND `gravity` != ?", $link_item['parent'], GRAVITY_PARENT];
|
||||
$total = DBA::count('item', $condition);
|
||||
|
||||
$pager = new Pager(DI::l10n(), DI::args()->getQueryString());
|
||||
|
|
@ -1354,8 +1340,6 @@ function photos_content(App $a)
|
|||
'$delete' => DI::l10n()->t('Delete Photo'),
|
||||
|
||||
// ACL permissions box
|
||||
'$group_perms' => DI::l10n()->t('Show to Groups'),
|
||||
'$contact_perms' => DI::l10n()->t('Show to Contacts'),
|
||||
'$return_path' => DI::args()->getQueryString(),
|
||||
]);
|
||||
}
|
||||
|
|
@ -1371,19 +1355,18 @@ function photos_content(App $a)
|
|||
$tpl = Renderer::getMarkupTemplate('photo_item.tpl');
|
||||
$return_path = DI::args()->getCommand();
|
||||
|
||||
if ($cmd === 'view' && ($can_post || Security::canWriteToUserWall($owner_uid))) {
|
||||
$like_tpl = Renderer::getMarkupTemplate('like_noshare.tpl');
|
||||
$likebuttons = Renderer::replaceMacros($like_tpl, [
|
||||
'$id' => $link_item['id'],
|
||||
'$likethis' => DI::l10n()->t("I like this \x28toggle\x29"),
|
||||
'$dislike' => DI::pConfig()->get(local_user(), 'system', 'hide_dislike') ? '' : DI::l10n()->t("I don't like this \x28toggle\x29"),
|
||||
'$wait' => DI::l10n()->t('Please wait'),
|
||||
'$return_path' => DI::args()->getQueryString(),
|
||||
]);
|
||||
}
|
||||
|
||||
if (!DBA::isResult($items)) {
|
||||
if (($can_post || Security::canWriteToUserWall($owner_uid))) {
|
||||
/*
|
||||
* Hmmm, code depending on the presence of a particular addon?
|
||||
* This should be better if done by a hook
|
||||
*/
|
||||
$qcomment = null;
|
||||
if (Addon::isEnabled('qcomment')) {
|
||||
$words = DI::pConfig()->get(local_user(), 'qcomment', 'words');
|
||||
$qcomment = $words ? explode("\n", $words) : [];
|
||||
}
|
||||
|
||||
$comments .= Renderer::replaceMacros($cmnt_tpl, [
|
||||
'$return_path' => '',
|
||||
'$jsreload' => $return_path,
|
||||
|
|
@ -1398,7 +1381,7 @@ function photos_content(App $a)
|
|||
'$preview' => DI::l10n()->t('Preview'),
|
||||
'$loading' => DI::l10n()->t('Loading...'),
|
||||
'$sourceapp' => DI::l10n()->t($a->sourcename),
|
||||
'$ww' => '',
|
||||
'$qcomment' => $qcomment,
|
||||
'$rand_num' => Crypto::randomDigits(12)
|
||||
]);
|
||||
}
|
||||
|
|
@ -1423,14 +1406,24 @@ function photos_content(App $a)
|
|||
}
|
||||
|
||||
if (!empty($conv_responses['like'][$link_item['uri']])) {
|
||||
$like = format_like($conv_responses['like'][$link_item['uri']], $conv_responses['like'][$link_item['uri'] . '-l'], 'like', $link_item['id']);
|
||||
$like = format_activity($conv_responses['like'][$link_item['uri']]['links'], 'like', $link_item['id']);
|
||||
}
|
||||
|
||||
if (!empty($conv_responses['dislike'][$link_item['uri']])) {
|
||||
$dislike = format_like($conv_responses['dislike'][$link_item['uri']], $conv_responses['dislike'][$link_item['uri'] . '-l'], 'dislike', $link_item['id']);
|
||||
$dislike = format_activity($conv_responses['dislike'][$link_item['uri']]['links'], 'dislike', $link_item['id']);
|
||||
}
|
||||
|
||||
if (($can_post || Security::canWriteToUserWall($owner_uid))) {
|
||||
/*
|
||||
* Hmmm, code depending on the presence of a particular addon?
|
||||
* This should be better if done by a hook
|
||||
*/
|
||||
$qcomment = null;
|
||||
if (Addon::isEnabled('qcomment')) {
|
||||
$words = DI::pConfig()->get(local_user(), 'qcomment', 'words');
|
||||
$qcomment = $words ? explode("\n", $words) : [];
|
||||
}
|
||||
|
||||
$comments .= Renderer::replaceMacros($cmnt_tpl,[
|
||||
'$return_path' => '',
|
||||
'$jsreload' => $return_path,
|
||||
|
|
@ -1444,7 +1437,7 @@ function photos_content(App $a)
|
|||
'$submit' => DI::l10n()->t('Submit'),
|
||||
'$preview' => DI::l10n()->t('Preview'),
|
||||
'$sourceapp' => DI::l10n()->t($a->sourcename),
|
||||
'$ww' => '',
|
||||
'$qcomment' => $qcomment,
|
||||
'$rand_num' => Crypto::randomDigits(12)
|
||||
]);
|
||||
}
|
||||
|
|
@ -1457,11 +1450,11 @@ function photos_content(App $a)
|
|||
|
||||
if (($activity->match($item['verb'], Activity::LIKE) ||
|
||||
$activity->match($item['verb'], Activity::DISLIKE)) &&
|
||||
($item['id'] != $item['parent'])) {
|
||||
($item['gravity'] != GRAVITY_PARENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$profile_url = Contact::magicLinkbyId($item['author-id']);
|
||||
$profile_url = Contact::magicLinkById($item['author-id']);
|
||||
if (strpos($profile_url, 'redir/') === 0) {
|
||||
$sparkle = ' sparkle';
|
||||
} else {
|
||||
|
|
@ -1494,6 +1487,16 @@ function photos_content(App $a)
|
|||
]);
|
||||
|
||||
if (($can_post || Security::canWriteToUserWall($owner_uid))) {
|
||||
/*
|
||||
* Hmmm, code depending on the presence of a particular addon?
|
||||
* This should be better if done by a hook
|
||||
*/
|
||||
$qcomment = null;
|
||||
if (Addon::isEnabled('qcomment')) {
|
||||
$words = DI::pConfig()->get(local_user(), 'qcomment', 'words');
|
||||
$qcomment = $words ? explode("\n", $words) : [];
|
||||
}
|
||||
|
||||
$comments .= Renderer::replaceMacros($cmnt_tpl, [
|
||||
'$return_path' => '',
|
||||
'$jsreload' => $return_path,
|
||||
|
|
@ -1507,13 +1510,35 @@ function photos_content(App $a)
|
|||
'$submit' => DI::l10n()->t('Submit'),
|
||||
'$preview' => DI::l10n()->t('Preview'),
|
||||
'$sourceapp' => DI::l10n()->t($a->sourcename),
|
||||
'$ww' => '',
|
||||
'$qcomment' => $qcomment,
|
||||
'$rand_num' => Crypto::randomDigits(12)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$responses = [];
|
||||
foreach ($conv_responses as $verb => $activity) {
|
||||
if (isset($activity[$link_item['uri']])) {
|
||||
$responses[$verb] = $activity[$link_item['uri']];
|
||||
}
|
||||
}
|
||||
|
||||
if ($cmd === 'view' && ($can_post || Security::canWriteToUserWall($owner_uid))) {
|
||||
$like_tpl = Renderer::getMarkupTemplate('like_noshare.tpl');
|
||||
$likebuttons = Renderer::replaceMacros($like_tpl, [
|
||||
'$id' => $link_item['id'],
|
||||
'$like' => DI::l10n()->t('Like'),
|
||||
'$like_title' => DI::l10n()->t('I like this (toggle)'),
|
||||
'$dislike' => DI::l10n()->t('Dislike'),
|
||||
'$wait' => DI::l10n()->t('Please wait'),
|
||||
'$dislike_title' => DI::l10n()->t('I don\'t like this (toggle)'),
|
||||
'$hide_dislike' => DI::pConfig()->get(local_user(), 'system', 'hide_dislike'),
|
||||
'$responses' => $responses,
|
||||
'$return_path' => DI::args()->getQueryString(),
|
||||
]);
|
||||
}
|
||||
|
||||
$paginate = $pager->renderFull($total);
|
||||
}
|
||||
|
||||
|
|
@ -1552,8 +1577,8 @@ function photos_content(App $a)
|
|||
$r = q("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s'
|
||||
$sql_extra GROUP BY `resource-id`",
|
||||
intval($a->data['user']['uid']),
|
||||
DBA::escape('Contact Photos'),
|
||||
DBA::escape(DI::l10n()->t('Contact Photos'))
|
||||
DBA::escape(Photo::CONTACT_PHOTOS),
|
||||
DBA::escape(DI::l10n()->t(Photo::CONTACT_PHOTOS))
|
||||
);
|
||||
if (DBA::isResult($r)) {
|
||||
$total = count($r);
|
||||
|
|
@ -1567,8 +1592,8 @@ function photos_content(App $a)
|
|||
WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s'
|
||||
$sql_extra GROUP BY `resource-id` ORDER BY `created` DESC LIMIT %d , %d",
|
||||
intval($a->data['user']['uid']),
|
||||
DBA::escape('Contact Photos'),
|
||||
DBA::escape(DI::l10n()->t('Contact Photos')),
|
||||
DBA::escape(Photo::CONTACT_PHOTOS),
|
||||
DBA::escape(DI::l10n()->t(Photo::CONTACT_PHOTOS)),
|
||||
$pager->getStart(),
|
||||
$pager->getItemsPerPage()
|
||||
);
|
||||
|
|
|
|||
98
mod/ping.php
|
|
@ -30,9 +30,10 @@ use Friendica\Model\Contact;
|
|||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Notify\Type;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Temporal;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
/**
|
||||
|
|
@ -132,14 +133,11 @@ function ping_init(App $a)
|
|||
exit();
|
||||
}
|
||||
|
||||
$notifs = ping_get_notifications(local_user());
|
||||
|
||||
$condition = ["`unseen` AND `uid` = ? AND `contact-id` != ?", local_user(), local_user()];
|
||||
$fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
|
||||
'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'wall'];
|
||||
$params = ['order' => ['received' => true]];
|
||||
$items = Item::selectForUser(local_user(), $fields, $condition, $params);
|
||||
$notifications = ping_get_notifications(local_user());
|
||||
|
||||
$condition = ["`unseen` AND `uid` = ? AND NOT `origin` AND (`vid` != ? OR `vid` IS NULL)",
|
||||
local_user(), Verb::getID(Activity::FOLLOW)];
|
||||
$items = Item::selectForUser(local_user(), ['wall', 'uid', 'uri-id'], $condition);
|
||||
if (DBA::isResult($items)) {
|
||||
$items_unseen = Item::inArray($items);
|
||||
$arr = ['items' => $items_unseen];
|
||||
|
|
@ -153,6 +151,7 @@ function ping_init(App $a)
|
|||
}
|
||||
}
|
||||
}
|
||||
DBA::close($items);
|
||||
|
||||
if ($network_count) {
|
||||
// Find out how unseen network posts are spread across groups
|
||||
|
|
@ -178,15 +177,15 @@ function ping_init(App $a)
|
|||
$intros1 = q(
|
||||
"SELECT `intro`.`id`, `intro`.`datetime`,
|
||||
`fcontact`.`name`, `fcontact`.`url`, `fcontact`.`photo`
|
||||
FROM `intro` LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id`
|
||||
WHERE `intro`.`uid` = %d AND `intro`.`blocked` = 0 AND `intro`.`ignore` = 0 AND `intro`.`fid` != 0",
|
||||
FROM `intro` INNER JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id`
|
||||
WHERE `intro`.`uid` = %d AND NOT `intro`.`blocked` AND NOT `intro`.`ignore` AND `intro`.`fid` != 0",
|
||||
intval(local_user())
|
||||
);
|
||||
$intros2 = q(
|
||||
"SELECT `intro`.`id`, `intro`.`datetime`,
|
||||
`contact`.`name`, `contact`.`url`, `contact`.`photo`
|
||||
FROM `intro` LEFT JOIN `contact` ON `intro`.`contact-id` = `contact`.`id`
|
||||
WHERE `intro`.`uid` = %d AND `intro`.`blocked` = 0 AND `intro`.`ignore` = 0 AND `intro`.`contact-id` != 0",
|
||||
FROM `intro` INNER JOIN `contact` ON `intro`.`contact-id` = `contact`.`id`
|
||||
WHERE `intro`.`uid` = %d AND NOT `intro`.`blocked` AND NOT `intro`.`ignore` AND `intro`.`contact-id` != 0 AND (`intro`.`fid` = 0 OR `intro`.`fid` IS NULL)",
|
||||
intval(local_user())
|
||||
);
|
||||
|
||||
|
|
@ -264,8 +263,8 @@ function ping_init(App $a)
|
|||
$data['birthdays'] = $birthdays;
|
||||
$data['birthdays-today'] = $birthdays_today;
|
||||
|
||||
if (DBA::isResult($notifs)) {
|
||||
foreach ($notifs as $notif) {
|
||||
if (DBA::isResult($notifications)) {
|
||||
foreach ($notifications as $notif) {
|
||||
if ($notif['seen'] == 0) {
|
||||
$sysnotify_count ++;
|
||||
}
|
||||
|
|
@ -278,30 +277,44 @@ function ping_init(App $a)
|
|||
$notif = [
|
||||
'id' => 0,
|
||||
'href' => DI::baseUrl() . '/notifications/intros/' . $intro['id'],
|
||||
'name' => $intro['name'],
|
||||
'name' => BBCode::convert($intro['name']),
|
||||
'url' => $intro['url'],
|
||||
'photo' => $intro['photo'],
|
||||
'date' => $intro['datetime'],
|
||||
'seen' => false,
|
||||
'message' => DI::l10n()->t('{0} wants to be your friend'),
|
||||
];
|
||||
$notifs[] = $notif;
|
||||
$notifications[] = $notif;
|
||||
}
|
||||
}
|
||||
|
||||
if (DBA::isResult($regs)) {
|
||||
foreach ($regs as $reg) {
|
||||
if (count($regs) <= 1 || DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
foreach ($regs as $reg) {
|
||||
$notif = [
|
||||
'id' => 0,
|
||||
'href' => DI::baseUrl() . '/admin/users/pending',
|
||||
'name' => $reg['name'],
|
||||
'url' => $reg['url'],
|
||||
'photo' => $reg['micro'],
|
||||
'date' => $reg['created'],
|
||||
'seen' => false,
|
||||
'message' => DI::l10n()->t('{0} requested registration'),
|
||||
];
|
||||
$notifications[] = $notif;
|
||||
}
|
||||
} else {
|
||||
$notif = [
|
||||
'id' => 0,
|
||||
'href' => DI::baseUrl() . '/admin/users/',
|
||||
'name' => $reg['name'],
|
||||
'url' => $reg['url'],
|
||||
'photo' => $reg['micro'],
|
||||
'date' => $reg['created'],
|
||||
'href' => DI::baseUrl() . '/admin/users/pending',
|
||||
'name' => $regs[0]['name'],
|
||||
'url' => $regs[0]['url'],
|
||||
'photo' => $regs[0]['micro'],
|
||||
'date' => $regs[0]['created'],
|
||||
'seen' => false,
|
||||
'message' => DI::l10n()->t('{0} requested registration'),
|
||||
'message' => DI::l10n()->t('{0} and %d others requested registration', count($regs) - 1),
|
||||
];
|
||||
$notifs[] = $notif;
|
||||
$notifications[] = $notif;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -324,32 +337,17 @@ function ping_init(App $a)
|
|||
}
|
||||
return ($adate < $bdate) ? 1 : -1;
|
||||
};
|
||||
usort($notifs, $sort_function);
|
||||
usort($notifications, $sort_function);
|
||||
|
||||
if (DBA::isResult($notifs)) {
|
||||
foreach ($notifs as $notif) {
|
||||
$contact = Contact::getDetailsByURL($notif['url']);
|
||||
if (isset($contact['micro'])) {
|
||||
$notif['photo'] = ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO);
|
||||
} else {
|
||||
$notif['photo'] = ProxyUtils::proxifyUrl($notif['photo'], false, ProxyUtils::SIZE_MICRO);
|
||||
}
|
||||
|
||||
$local_time = DateTimeFormat::local($notif['date']);
|
||||
|
||||
$notifications[] = [
|
||||
'id' => $notif['id'],
|
||||
'href' => $notif['href'],
|
||||
'name' => $notif['name'],
|
||||
'url' => $notif['url'],
|
||||
'photo' => $notif['photo'],
|
||||
'date' => Temporal::getRelativeDate($notif['date']),
|
||||
'message' => $notif['message'],
|
||||
'seen' => $notif['seen'],
|
||||
'timestamp' => strtotime($local_time)
|
||||
];
|
||||
array_walk($notifications, function (&$notification) {
|
||||
if (empty($notification['photo'])) {
|
||||
$contact = Contact::getByURL($notification['url'], false, ['micro', 'id', 'avatar']);
|
||||
$notification['photo'] = Contact::getMicro($contact, $notification['photo']);
|
||||
}
|
||||
}
|
||||
|
||||
$notification['timestamp'] = DateTimeFormat::local($notification['date']);
|
||||
$notification['date'] = Temporal::getRelativeDate($notification['date']);
|
||||
});
|
||||
}
|
||||
|
||||
$sysmsgs = [];
|
||||
|
|
@ -464,13 +462,13 @@ function ping_get_notifications($uid)
|
|||
|
||||
if ($notification["visible"]
|
||||
&& !$notification["deleted"]
|
||||
&& empty($result[$notification["parent"]])
|
||||
&& empty($result[$notification['parent']])
|
||||
) {
|
||||
// Should we condense the notifications or show them all?
|
||||
if (DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
$result[$notification["id"]] = $notification;
|
||||
} else {
|
||||
$result[$notification["parent"]] = $notification;
|
||||
$result[$notification['parent']] = $notification;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
383
mod/poco.php
|
|
@ -27,139 +27,37 @@ use Friendica\Core\Protocol;
|
|||
use Friendica\Core\Renderer;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Protocol\PortableContact;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
function poco_init(App $a) {
|
||||
$system_mode = false;
|
||||
|
||||
if (intval(DI::config()->get('system', 'block_public')) || (DI::config()->get('system', 'block_local_dir'))) {
|
||||
throw new \Friendica\Network\HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
if ($a->argc > 1) {
|
||||
$nickname = Strings::escapeTags(trim($a->argv[1]));
|
||||
}
|
||||
if (empty($nickname)) {
|
||||
if (!DBA::exists('profile', ['net-publish' => true])) {
|
||||
throw new \Friendica\Network\HTTPException\ForbiddenException();
|
||||
}
|
||||
$system_mode = true;
|
||||
// Only the system mode is supported
|
||||
throw new \Friendica\Network\HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
$format = ($_GET['format'] ?? '') ?: 'json';
|
||||
|
||||
$justme = false;
|
||||
$global = false;
|
||||
|
||||
if ($a->argc > 1 && $a->argv[1] === '@server') {
|
||||
// List of all servers that this server knows
|
||||
$ret = PortableContact::serverlist();
|
||||
header('Content-type: application/json');
|
||||
echo json_encode($ret);
|
||||
exit();
|
||||
$totalResults = DBA::count('profile', ['net-publish' => true]);
|
||||
if ($totalResults == 0) {
|
||||
throw new \Friendica\Network\HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
if ($a->argc > 1 && $a->argv[1] === '@global') {
|
||||
// List of all profiles that this server recently had data from
|
||||
$global = true;
|
||||
$update_limit = date(DateTimeFormat::MYSQL, time() - 30 * 86400);
|
||||
}
|
||||
if ($a->argc > 2 && $a->argv[2] === '@me') {
|
||||
$justme = true;
|
||||
}
|
||||
if ($a->argc > 3 && $a->argv[3] === '@all') {
|
||||
$justme = false;
|
||||
}
|
||||
if ($a->argc > 3 && $a->argv[3] === '@self') {
|
||||
$justme = true;
|
||||
}
|
||||
if ($a->argc > 4 && intval($a->argv[4]) && $justme == false) {
|
||||
$cid = intval($a->argv[4]);
|
||||
}
|
||||
|
||||
if (!$system_mode && !$global) {
|
||||
$user = DBA::selectFirst('owner-view', ['uid', 'nickname'], ['nickname' => $nickname, 'hide-friends' => false]);
|
||||
if (!DBA::isResult($user)) {
|
||||
throw new \Friendica\Network\HTTPException\NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
if ($justme) {
|
||||
$sql_extra = " AND `contact`.`self` = 1 ";
|
||||
} else {
|
||||
$sql_extra = "";
|
||||
}
|
||||
|
||||
if (!empty($cid)) {
|
||||
$sql_extra = sprintf(" AND `contact`.`id` = %d ", intval($cid));
|
||||
}
|
||||
if (!empty($_GET['updatedSince'])) {
|
||||
$update_limit = date(DateTimeFormat::MYSQL, strtotime($_GET['updatedSince']));
|
||||
}
|
||||
if ($global) {
|
||||
$contacts = q("SELECT count(*) AS `total` FROM `gcontact` WHERE `updated` >= '%s' AND `updated` >= `last_failure` AND NOT `hide` AND `network` IN ('%s', '%s', '%s')",
|
||||
DBA::escape($update_limit),
|
||||
DBA::escape(Protocol::DFRN),
|
||||
DBA::escape(Protocol::DIASPORA),
|
||||
DBA::escape(Protocol::OSTATUS)
|
||||
);
|
||||
} elseif ($system_mode) {
|
||||
$totalResults = DBA::count('profile', ['net-publish' => true]);
|
||||
} else {
|
||||
$contacts = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0
|
||||
AND (`success_update` >= `failure_update` OR `last-item` >= `failure_update`)
|
||||
AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra",
|
||||
intval($user['uid']),
|
||||
DBA::escape(Protocol::DFRN),
|
||||
DBA::escape(Protocol::DIASPORA),
|
||||
DBA::escape(Protocol::OSTATUS),
|
||||
DBA::escape(Protocol::STATUSNET)
|
||||
);
|
||||
}
|
||||
if (empty($totalResults) && DBA::isResult($contacts)) {
|
||||
$totalResults = intval($contacts[0]['total']);
|
||||
} elseif (empty($totalResults)) {
|
||||
$totalResults = 0;
|
||||
}
|
||||
if (!empty($_GET['startIndex'])) {
|
||||
$startIndex = intval($_GET['startIndex']);
|
||||
} else {
|
||||
$startIndex = 0;
|
||||
}
|
||||
$itemsPerPage = ((!empty($_GET['count'])) ? intval($_GET['count']) : $totalResults);
|
||||
$itemsPerPage = (!empty($_GET['count']) ? intval($_GET['count']) : $totalResults);
|
||||
|
||||
if ($global) {
|
||||
Logger::log("Start global query", Logger::DEBUG);
|
||||
$contacts = q("SELECT * FROM `gcontact` WHERE `updated` > '%s' AND NOT `hide` AND `network` IN ('%s', '%s', '%s') AND `updated` > `last_failure`
|
||||
ORDER BY `updated` DESC LIMIT %d, %d",
|
||||
DBA::escape($update_limit),
|
||||
DBA::escape(Protocol::DFRN),
|
||||
DBA::escape(Protocol::DIASPORA),
|
||||
DBA::escape(Protocol::OSTATUS),
|
||||
intval($startIndex),
|
||||
intval($itemsPerPage)
|
||||
);
|
||||
} elseif ($system_mode) {
|
||||
Logger::log("Start system mode query", Logger::DEBUG);
|
||||
$contacts = DBA::selectToArray('owner-view', [], ['net-publish' => true], ['limit' => [$startIndex, $itemsPerPage]]);
|
||||
} else {
|
||||
Logger::log("Start query for user " . $user['nickname'], Logger::DEBUG);
|
||||
$contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0
|
||||
AND (`success_update` >= `failure_update` OR `last-item` >= `failure_update`)
|
||||
AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra LIMIT %d, %d",
|
||||
intval($user['uid']),
|
||||
DBA::escape(Protocol::DFRN),
|
||||
DBA::escape(Protocol::DIASPORA),
|
||||
DBA::escape(Protocol::OSTATUS),
|
||||
DBA::escape(Protocol::STATUSNET),
|
||||
intval($startIndex),
|
||||
intval($itemsPerPage)
|
||||
);
|
||||
}
|
||||
Logger::log("Query done", Logger::DEBUG);
|
||||
Logger::info("Start system mode query");
|
||||
$contacts = DBA::selectToArray('owner-view', [], ['net-publish' => true], ['limit' => [$startIndex, $itemsPerPage]]);
|
||||
|
||||
Logger::info("Query done");
|
||||
|
||||
$ret = [];
|
||||
if (!empty($_GET['sorted'])) {
|
||||
|
|
@ -168,7 +66,7 @@ function poco_init(App $a) {
|
|||
if (!empty($_GET['filtered'])) {
|
||||
$ret['filtered'] = false;
|
||||
}
|
||||
if (!empty($_GET['updatedSince']) && ! $global) {
|
||||
if (!empty($_GET['updatedSince'])) {
|
||||
$ret['updatedSince'] = false;
|
||||
}
|
||||
$ret['startIndex'] = (int) $startIndex;
|
||||
|
|
@ -176,7 +74,6 @@ function poco_init(App $a) {
|
|||
$ret['totalResults'] = (int) $totalResults;
|
||||
$ret['entry'] = [];
|
||||
|
||||
|
||||
$fields_ret = [
|
||||
'id' => false,
|
||||
'displayName' => false,
|
||||
|
|
@ -193,7 +90,7 @@ function poco_init(App $a) {
|
|||
'generation' => false
|
||||
];
|
||||
|
||||
if (empty($_GET['fields']) || ($_GET['fields'] === '@all')) {
|
||||
if (empty($_GET['fields'])) {
|
||||
foreach ($fields_ret as $k => $v) {
|
||||
$fields_ret[$k] = true;
|
||||
}
|
||||
|
|
@ -204,145 +101,129 @@ function poco_init(App $a) {
|
|||
}
|
||||
}
|
||||
|
||||
if (is_array($contacts)) {
|
||||
if (DBA::isResult($contacts)) {
|
||||
foreach ($contacts as $contact) {
|
||||
if (!isset($contact['updated'])) {
|
||||
$contact['updated'] = '';
|
||||
}
|
||||
|
||||
if (! isset($contact['generation'])) {
|
||||
if ($global) {
|
||||
$contact['generation'] = 3;
|
||||
} elseif ($system_mode) {
|
||||
$contact['generation'] = 1;
|
||||
} else {
|
||||
$contact['generation'] = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (($contact['keywords'] == "") && isset($contact['pub_keywords'])) {
|
||||
$contact['keywords'] = $contact['pub_keywords'];
|
||||
}
|
||||
if (isset($contact['account-type'])) {
|
||||
$contact['contact-type'] = $contact['account-type'];
|
||||
}
|
||||
$about = DI::cache()->get("about:" . $contact['updated'] . ":" . $contact['nurl']);
|
||||
if (is_null($about)) {
|
||||
$about = BBCode::convert($contact['about'], false);
|
||||
DI::cache()->set("about:" . $contact['updated'] . ":" . $contact['nurl'], $about);
|
||||
}
|
||||
|
||||
// Non connected persons can only see the keywords of a Diaspora account
|
||||
if ($contact['network'] == Protocol::DIASPORA) {
|
||||
$contact['location'] = "";
|
||||
$about = "";
|
||||
}
|
||||
|
||||
$entry = [];
|
||||
if ($fields_ret['id']) {
|
||||
$entry['id'] = (int)$contact['id'];
|
||||
}
|
||||
if ($fields_ret['displayName']) {
|
||||
$entry['displayName'] = $contact['name'];
|
||||
}
|
||||
if ($fields_ret['aboutMe']) {
|
||||
$entry['aboutMe'] = $about;
|
||||
}
|
||||
if ($fields_ret['currentLocation']) {
|
||||
$entry['currentLocation'] = $contact['location'];
|
||||
}
|
||||
if ($fields_ret['generation']) {
|
||||
$entry['generation'] = (int)$contact['generation'];
|
||||
}
|
||||
if ($fields_ret['urls']) {
|
||||
$entry['urls'] = [['value' => $contact['url'], 'type' => 'profile']];
|
||||
if ($contact['addr'] && ($contact['network'] !== Protocol::MAIL)) {
|
||||
$entry['urls'][] = ['value' => 'acct:' . $contact['addr'], 'type' => 'webfinger'];
|
||||
}
|
||||
}
|
||||
if ($fields_ret['preferredUsername']) {
|
||||
$entry['preferredUsername'] = $contact['nick'];
|
||||
}
|
||||
if ($fields_ret['updated']) {
|
||||
if (! $global) {
|
||||
$entry['updated'] = $contact['success_update'];
|
||||
|
||||
if ($contact['name-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['name-date'];
|
||||
}
|
||||
if ($contact['uri-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['uri-date'];
|
||||
}
|
||||
if ($contact['avatar-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['avatar-date'];
|
||||
}
|
||||
} else {
|
||||
$entry['updated'] = $contact['updated'];
|
||||
}
|
||||
$entry['updated'] = date("c", strtotime($entry['updated']));
|
||||
}
|
||||
if ($fields_ret['photos']) {
|
||||
$entry['photos'] = [['value' => $contact['photo'], 'type' => 'profile']];
|
||||
}
|
||||
if ($fields_ret['network']) {
|
||||
$entry['network'] = $contact['network'];
|
||||
if ($entry['network'] == Protocol::STATUSNET) {
|
||||
$entry['network'] = Protocol::OSTATUS;
|
||||
}
|
||||
if (($entry['network'] == "") && ($contact['self'])) {
|
||||
$entry['network'] = Protocol::DFRN;
|
||||
}
|
||||
}
|
||||
if ($fields_ret['tags']) {
|
||||
$tags = str_replace(",", " ", $contact['keywords']);
|
||||
$tags = explode(" ", $tags);
|
||||
|
||||
$cleaned = [];
|
||||
foreach ($tags as $tag) {
|
||||
$tag = trim(strtolower($tag));
|
||||
if ($tag != "") {
|
||||
$cleaned[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
$entry['tags'] = [$cleaned];
|
||||
}
|
||||
if ($fields_ret['address']) {
|
||||
$entry['address'] = [];
|
||||
|
||||
// Deactivated. It just reveals too much data. (Although its from the default profile)
|
||||
//if (isset($rr['address']))
|
||||
// $entry['address']['streetAddress'] = $rr['address'];
|
||||
|
||||
if (isset($contact['locality'])) {
|
||||
$entry['address']['locality'] = $contact['locality'];
|
||||
}
|
||||
if (isset($contact['region'])) {
|
||||
$entry['address']['region'] = $contact['region'];
|
||||
}
|
||||
// See above
|
||||
//if (isset($rr['postal-code']))
|
||||
// $entry['address']['postalCode'] = $rr['postal-code'];
|
||||
|
||||
if (isset($contact['country'])) {
|
||||
$entry['address']['country'] = $contact['country'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($fields_ret['contactType']) {
|
||||
$entry['contactType'] = intval($contact['contact-type']);
|
||||
}
|
||||
$ret['entry'][] = $entry;
|
||||
}
|
||||
} else {
|
||||
$ret['entry'][] = [];
|
||||
}
|
||||
} else {
|
||||
if (!is_array($contacts)) {
|
||||
throw new \Friendica\Network\HTTPException\InternalServerErrorException();
|
||||
}
|
||||
|
||||
Logger::log("End of poco", Logger::DEBUG);
|
||||
if (DBA::isResult($contacts)) {
|
||||
foreach ($contacts as $contact) {
|
||||
if (!isset($contact['updated'])) {
|
||||
$contact['updated'] = '';
|
||||
}
|
||||
|
||||
if (! isset($contact['generation'])) {
|
||||
$contact['generation'] = 1;
|
||||
}
|
||||
|
||||
if (($contact['keywords'] == "") && isset($contact['pub_keywords'])) {
|
||||
$contact['keywords'] = $contact['pub_keywords'];
|
||||
}
|
||||
if (isset($contact['account-type'])) {
|
||||
$contact['contact-type'] = $contact['account-type'];
|
||||
}
|
||||
$about = DI::cache()->get("about:" . $contact['updated'] . ":" . $contact['nurl']);
|
||||
if (is_null($about)) {
|
||||
$about = BBCode::convert($contact['about'], false);
|
||||
DI::cache()->set("about:" . $contact['updated'] . ":" . $contact['nurl'], $about);
|
||||
}
|
||||
|
||||
// Non connected persons can only see the keywords of a Diaspora account
|
||||
if ($contact['network'] == Protocol::DIASPORA) {
|
||||
$contact['location'] = "";
|
||||
$about = "";
|
||||
}
|
||||
|
||||
$entry = [];
|
||||
if ($fields_ret['id']) {
|
||||
$entry['id'] = (int)$contact['id'];
|
||||
}
|
||||
if ($fields_ret['displayName']) {
|
||||
$entry['displayName'] = $contact['name'];
|
||||
}
|
||||
if ($fields_ret['aboutMe']) {
|
||||
$entry['aboutMe'] = $about;
|
||||
}
|
||||
if ($fields_ret['currentLocation']) {
|
||||
$entry['currentLocation'] = $contact['location'];
|
||||
}
|
||||
if ($fields_ret['generation']) {
|
||||
$entry['generation'] = (int)$contact['generation'];
|
||||
}
|
||||
if ($fields_ret['urls']) {
|
||||
$entry['urls'] = [['value' => $contact['url'], 'type' => 'profile']];
|
||||
if ($contact['addr'] && ($contact['network'] !== Protocol::MAIL)) {
|
||||
$entry['urls'][] = ['value' => 'acct:' . $contact['addr'], 'type' => 'webfinger'];
|
||||
}
|
||||
}
|
||||
if ($fields_ret['preferredUsername']) {
|
||||
$entry['preferredUsername'] = $contact['nick'];
|
||||
}
|
||||
if ($fields_ret['updated']) {
|
||||
$entry['updated'] = $contact['success_update'];
|
||||
|
||||
if ($contact['name-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['name-date'];
|
||||
}
|
||||
if ($contact['uri-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['uri-date'];
|
||||
}
|
||||
if ($contact['avatar-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['avatar-date'];
|
||||
}
|
||||
$entry['updated'] = date("c", strtotime($entry['updated']));
|
||||
}
|
||||
if ($fields_ret['photos']) {
|
||||
$entry['photos'] = [['value' => $contact['photo'], 'type' => 'profile']];
|
||||
}
|
||||
if ($fields_ret['network']) {
|
||||
$entry['network'] = $contact['network'];
|
||||
if ($entry['network'] == Protocol::STATUSNET) {
|
||||
$entry['network'] = Protocol::OSTATUS;
|
||||
}
|
||||
if (($entry['network'] == "") && ($contact['self'])) {
|
||||
$entry['network'] = Protocol::DFRN;
|
||||
}
|
||||
}
|
||||
if ($fields_ret['tags']) {
|
||||
$tags = str_replace(",", " ", $contact['keywords']);
|
||||
$tags = explode(" ", $tags);
|
||||
|
||||
$cleaned = [];
|
||||
foreach ($tags as $tag) {
|
||||
$tag = trim(strtolower($tag));
|
||||
if ($tag != "") {
|
||||
$cleaned[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
$entry['tags'] = [$cleaned];
|
||||
}
|
||||
if ($fields_ret['address']) {
|
||||
$entry['address'] = [];
|
||||
|
||||
if (isset($contact['locality'])) {
|
||||
$entry['address']['locality'] = $contact['locality'];
|
||||
}
|
||||
|
||||
if (isset($contact['region'])) {
|
||||
$entry['address']['region'] = $contact['region'];
|
||||
}
|
||||
|
||||
if (isset($contact['country'])) {
|
||||
$entry['address']['country'] = $contact['country'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($fields_ret['contactType']) {
|
||||
$entry['contactType'] = intval($contact['contact-type']);
|
||||
}
|
||||
$ret['entry'][] = $entry;
|
||||
}
|
||||
} else {
|
||||
$ret['entry'][] = [];
|
||||
}
|
||||
|
||||
Logger::info("End of poco");
|
||||
|
||||
if ($format === 'xml') {
|
||||
header('Content-type: text/xml');
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use Friendica\Core\Protocol;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Protocol\Feed;
|
||||
use Friendica\Protocol\OStatus;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Network;
|
||||
|
|
@ -138,20 +139,15 @@ function pubsub_post(App $a)
|
|||
hub_post_return();
|
||||
}
|
||||
|
||||
// We import feeds from OStatus, Friendica and ATOM/RSS.
|
||||
/// @todo Check if Friendica posts really arrive here - otherwise we can discard some stuff
|
||||
if (!in_array($contact['network'], [Protocol::OSTATUS, Protocol::DFRN, Protocol::FEED])) {
|
||||
// We only import feeds from OStatus here
|
||||
if ($contact['network'] != Protocol::OSTATUS) {
|
||||
Logger::warning('Unexpected network', ['contact' => $contact]);
|
||||
hub_post_return();
|
||||
}
|
||||
|
||||
Logger::log('Import item for ' . $nick . ' from ' . $contact['nick'] . ' (' . $contact['id'] . ')');
|
||||
$feedhub = '';
|
||||
consume_feed($xml, $importer, $contact, $feedhub);
|
||||
|
||||
// do it a second time for DFRN so that any children find their parents.
|
||||
if ($contact['network'] === Protocol::DFRN) {
|
||||
consume_feed($xml, $importer, $contact, $feedhub);
|
||||
}
|
||||
OStatus::import($xml, $importer, $contact, $feedhub);
|
||||
|
||||
hub_post_return();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ use Friendica\Core\Logger;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\PushSubscriber;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
function post_var($name) {
|
||||
|
|
@ -126,7 +125,7 @@ function pubsubhubbub_init(App $a) {
|
|||
$hub_callback = rtrim($hub_callback, ' ?&#');
|
||||
$separator = parse_url($hub_callback, PHP_URL_QUERY) === null ? '?' : '&';
|
||||
|
||||
$fetchResult = Network::fetchUrlFull($hub_callback . $separator . $params);
|
||||
$fetchResult = DI::httpRequest()->fetchFull($hub_callback . $separator . $params);
|
||||
$body = $fetchResult->getBody();
|
||||
$ret = $fetchResult->getReturnCode();
|
||||
|
||||
|
|
|
|||
192
mod/redir.php
|
|
@ -27,10 +27,12 @@ use Friendica\Database\DBA;
|
|||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
function redir_init(App $a) {
|
||||
if (!Session::isAuthenticated()) {
|
||||
throw new \Friendica\Network\HTTPException\ForbiddenException(DI::l10n()->t('Access denied.'));
|
||||
}
|
||||
|
||||
$url = $_GET['url'] ?? '';
|
||||
$quiet = !empty($_GET['quiet']) ? '&quiet=1' : '';
|
||||
|
|
@ -44,102 +46,102 @@ function redir_init(App $a) {
|
|||
// Try magic auth before the legacy stuff
|
||||
redir_magic($a, $cid, $url);
|
||||
|
||||
if (!empty($cid)) {
|
||||
$fields = ['id', 'uid', 'nurl', 'url', 'addr', 'name', 'network', 'poll', 'issued-id', 'dfrn-id', 'duplex', 'pending'];
|
||||
$contact = DBA::selectFirst('contact', $fields, ['id' => $cid, 'uid' => [0, local_user()]]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
notice(DI::l10n()->t('Contact not found.'));
|
||||
DI::baseUrl()->redirect();
|
||||
if (empty($cid)) {
|
||||
throw new \Friendica\Network\HTTPException\BadRequestException(DI::l10n()->t('Bad Request.'));
|
||||
}
|
||||
|
||||
$fields = ['id', 'uid', 'nurl', 'url', 'addr', 'name', 'network', 'poll', 'issued-id', 'dfrn-id', 'duplex', 'pending'];
|
||||
$contact = DBA::selectFirst('contact', $fields, ['id' => $cid, 'uid' => [0, local_user()]]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
throw new \Friendica\Network\HTTPException\NotFoundException(DI::l10n()->t('Contact not found.'));
|
||||
}
|
||||
|
||||
$contact_url = $contact['url'];
|
||||
|
||||
if (!empty($a->contact['id']) && $a->contact['id'] == $cid) {
|
||||
// Local user is already authenticated.
|
||||
redir_check_url($contact_url, $url);
|
||||
$a->redirect($url ?: $contact_url);
|
||||
}
|
||||
|
||||
if ($contact['uid'] == 0 && local_user()) {
|
||||
// Let's have a look if there is an established connection
|
||||
// between the public contact we have found and the local user.
|
||||
$contact = DBA::selectFirst('contact', $fields, ['nurl' => $contact['nurl'], 'uid' => local_user()]);
|
||||
|
||||
if (DBA::isResult($contact)) {
|
||||
$cid = $contact['id'];
|
||||
}
|
||||
|
||||
$contact_url = $contact['url'];
|
||||
if (!empty($a->contact['id']) && $a->contact['id'] == $cid) {
|
||||
// Local user is already authenticated.
|
||||
redir_check_url($contact_url, $url);
|
||||
$target_url = $url ?: $contact_url;
|
||||
Logger::log($contact['name'] . " is already authenticated. Redirecting to " . $target_url, Logger::DEBUG);
|
||||
$a->redirect($target_url);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Session::isAuthenticated() // Visitors (not logged in or not remotes) can't authenticate.
|
||||
|| (!empty($a->contact['id']) && $a->contact['id'] == $cid)) // Local user is already authenticated.
|
||||
{
|
||||
$a->redirect($url ?: $contact_url);
|
||||
if (remote_user()) {
|
||||
$host = substr(DI::baseUrl()->getUrlPath() . (DI::baseUrl()->getUrlPath() ? '/' . DI::baseUrl()->getUrlPath() : ''), strpos(DI::baseUrl()->getUrlPath(), '://') + 3);
|
||||
$remotehost = substr($contact['addr'], strpos($contact['addr'], '@') + 1);
|
||||
|
||||
// On a local instance we have to check if the local user has already authenticated
|
||||
// with the local contact. Otherwise the local user would ask the local contact
|
||||
// for authentification everytime he/she is visiting a profile page of the local
|
||||
// contact.
|
||||
if (($host == $remotehost) && (Session::getRemoteContactID(Session::get('visitor_visiting')) == Session::get('visitor_id'))) {
|
||||
// Remote user is already authenticated.
|
||||
redir_check_url($contact_url, $url);
|
||||
$target_url = $url ?: $contact_url;
|
||||
Logger::log($contact['name'] . " is already authenticated. Redirecting to " . $target_url, Logger::DEBUG);
|
||||
$a->redirect($target_url);
|
||||
}
|
||||
}
|
||||
|
||||
// Doing remote auth with dfrn.
|
||||
if (local_user() && (!empty($contact['dfrn-id']) || !empty($contact['issued-id'])) && empty($contact['pending'])) {
|
||||
$dfrn_id = $orig_id = (($contact['issued-id']) ? $contact['issued-id'] : $contact['dfrn-id']);
|
||||
|
||||
if ($contact['duplex'] && $contact['issued-id']) {
|
||||
$orig_id = $contact['issued-id'];
|
||||
$dfrn_id = '1:' . $orig_id;
|
||||
}
|
||||
if ($contact['duplex'] && $contact['dfrn-id']) {
|
||||
$orig_id = $contact['dfrn-id'];
|
||||
$dfrn_id = '0:' . $orig_id;
|
||||
}
|
||||
|
||||
if ($contact['uid'] == 0 && local_user()) {
|
||||
// Let's have a look if there is an established connection
|
||||
// between the public contact we have found and the local user.
|
||||
$contact = DBA::selectFirst('contact', $fields, ['nurl' => $contact['nurl'], 'uid' => local_user()]);
|
||||
$sec = Strings::getRandomHex();
|
||||
|
||||
if (DBA::isResult($contact)) {
|
||||
$cid = $contact['id'];
|
||||
}
|
||||
$fields = ['uid' => local_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id,
|
||||
'sec' => $sec, 'expire' => time() + 45];
|
||||
DBA::insert('profile_check', $fields);
|
||||
|
||||
if (!empty($a->contact['id']) && $a->contact['id'] == $cid) {
|
||||
// Local user is already authenticated.
|
||||
$target_url = $url ?: $contact_url;
|
||||
Logger::log($contact['name'] . " is already authenticated. Redirecting to " . $target_url, Logger::DEBUG);
|
||||
$a->redirect($target_url);
|
||||
}
|
||||
}
|
||||
Logger::log('mod_redir: ' . $contact['name'] . ' ' . $sec, Logger::DEBUG);
|
||||
|
||||
if (remote_user()) {
|
||||
$host = substr(DI::baseUrl()->getUrlPath() . (DI::baseUrl()->getUrlPath() ? '/' . DI::baseUrl()->getUrlPath() : ''), strpos(DI::baseUrl()->getUrlPath(), '://') + 3);
|
||||
$remotehost = substr($contact['addr'], strpos($contact['addr'], '@') + 1);
|
||||
$dest = (!empty($url) ? '&destination_url=' . $url : '');
|
||||
|
||||
// On a local instance we have to check if the local user has already authenticated
|
||||
// with the local contact. Otherwise the local user would ask the local contact
|
||||
// for authentification everytime he/she is visiting a profile page of the local
|
||||
// contact.
|
||||
if (($host == $remotehost) && (Session::getRemoteContactID(Session::get('visitor_visiting')) == Session::get('visitor_id'))) {
|
||||
// Remote user is already authenticated.
|
||||
$target_url = $url ?: $contact_url;
|
||||
Logger::log($contact['name'] . " is already authenticated. Redirecting to " . $target_url, Logger::DEBUG);
|
||||
$a->redirect($target_url);
|
||||
}
|
||||
}
|
||||
System::externalRedirect($contact['poll'] . '?dfrn_id=' . $dfrn_id
|
||||
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION . '&type=profile&sec=' . $sec . $dest . $quiet);
|
||||
}
|
||||
|
||||
// Doing remote auth with dfrn.
|
||||
if (local_user() && (!empty($contact['dfrn-id']) || !empty($contact['issued-id'])) && empty($contact['pending'])) {
|
||||
$dfrn_id = $orig_id = (($contact['issued-id']) ? $contact['issued-id'] : $contact['dfrn-id']);
|
||||
|
||||
if ($contact['duplex'] && $contact['issued-id']) {
|
||||
$orig_id = $contact['issued-id'];
|
||||
$dfrn_id = '1:' . $orig_id;
|
||||
}
|
||||
if ($contact['duplex'] && $contact['dfrn-id']) {
|
||||
$orig_id = $contact['dfrn-id'];
|
||||
$dfrn_id = '0:' . $orig_id;
|
||||
}
|
||||
|
||||
$sec = Strings::getRandomHex();
|
||||
|
||||
$fields = ['uid' => local_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id,
|
||||
'sec' => $sec, 'expire' => time() + 45];
|
||||
DBA::insert('profile_check', $fields);
|
||||
|
||||
Logger::log('mod_redir: ' . $contact['name'] . ' ' . $sec, Logger::DEBUG);
|
||||
|
||||
$dest = (!empty($url) ? '&destination_url=' . $url : '');
|
||||
|
||||
System::externalRedirect($contact['poll'] . '?dfrn_id=' . $dfrn_id
|
||||
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION . '&type=profile&sec=' . $sec . $dest . $quiet);
|
||||
}
|
||||
|
||||
$url = $url ?: $contact_url;
|
||||
if (empty($url)) {
|
||||
throw new \Friendica\Network\HTTPException\BadRequestException(DI::l10n()->t('Bad Request.'));
|
||||
}
|
||||
|
||||
// If we don't have a connected contact, redirect with
|
||||
// the 'zrl' parameter.
|
||||
if (!empty($url)) {
|
||||
$my_profile = Profile::getMyURL();
|
||||
$my_profile = Profile::getMyURL();
|
||||
|
||||
if (!empty($my_profile) && !Strings::compareLink($my_profile, $url)) {
|
||||
$separator = strpos($url, '?') ? '&' : '?';
|
||||
if (!empty($my_profile) && !Strings::compareLink($my_profile, $url)) {
|
||||
$separator = strpos($url, '?') ? '&' : '?';
|
||||
|
||||
$url .= $separator . 'zrl=' . urlencode($my_profile);
|
||||
}
|
||||
|
||||
Logger::log('redirecting to ' . $url, Logger::DEBUG);
|
||||
$a->redirect($url);
|
||||
$url .= $separator . 'zrl=' . urlencode($my_profile);
|
||||
}
|
||||
|
||||
notice(DI::l10n()->t('Contact not found.'));
|
||||
DI::baseUrl()->redirect();
|
||||
Logger::log('redirecting to ' . $url, Logger::DEBUG);
|
||||
$a->redirect($url);
|
||||
}
|
||||
|
||||
function redir_magic($a, $cid, $url)
|
||||
|
|
@ -152,15 +154,10 @@ function redir_magic($a, $cid, $url)
|
|||
$contact = DBA::selectFirst('contact', ['url'], ['id' => $cid]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
Logger::info('Contact not found', ['id' => $cid]);
|
||||
// Shouldn't happen under normal conditions
|
||||
notice(DI::l10n()->t('Contact not found.'));
|
||||
if (!empty($url)) {
|
||||
System::externalRedirect($url);
|
||||
} else {
|
||||
DI::baseUrl()->redirect();
|
||||
}
|
||||
throw new \Friendica\Network\HTTPException\NotFoundException(DI::l10n()->t('Contact not found.'));
|
||||
} else {
|
||||
$contact_url = $contact['url'];
|
||||
redir_check_url($contact_url, $url);
|
||||
$target_url = $url ?: $contact_url;
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +170,7 @@ function redir_magic($a, $cid, $url)
|
|||
}
|
||||
|
||||
// Test for magic auth on the target system
|
||||
$serverret = Network::curl($basepath . '/magic');
|
||||
$serverret = DI::httpRequest()->get($basepath . '/magic');
|
||||
if ($serverret->isSuccess()) {
|
||||
$separator = strpos($target_url, '?') ? '&' : '?';
|
||||
$target_url .= $separator . 'zrl=' . urlencode($visitor) . '&addr=' . urlencode($contact_url);
|
||||
|
|
@ -184,3 +181,24 @@ function redir_magic($a, $cid, $url)
|
|||
Logger::info('No magic for contact', ['contact' => $contact_url]);
|
||||
}
|
||||
}
|
||||
|
||||
function redir_check_url(string $contact_url, string $url)
|
||||
{
|
||||
if (empty($contact_url) || empty($url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url_host = parse_url($url, PHP_URL_HOST);
|
||||
if (empty($url_host)) {
|
||||
$url_host = parse_url(DI::baseUrl(), PHP_URL_HOST);
|
||||
}
|
||||
|
||||
$contact_url_host = parse_url($contact_url, PHP_URL_HOST);
|
||||
|
||||
if ($url_host == $contact_url_host) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::error('URL check host mismatch', ['contact' => $contact_url, 'url' => $url]);
|
||||
throw new \Friendica\Network\HTTPException\ForbiddenException(DI::l10n()->t('Access denied.'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use Friendica\Model\Contact;
|
|||
function repair_ostatus_content(App $a) {
|
||||
|
||||
if (! local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
DI::baseUrl()->redirect('ostatus_repair');
|
||||
// NOTREACHED
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ function repair_ostatus_content(App $a) {
|
|||
|
||||
$o .= "<p>".DI::l10n()->t("Keep this window open until done.")."</p>";
|
||||
|
||||
Contact::createFromProbe($uid, $r[0]["url"], true);
|
||||
Contact::createFromProbe($a->user, $r[0]["url"], true);
|
||||
|
||||
DI::page()['htmlhead'] = '<meta http-equiv="refresh" content="1; URL=' . DI::baseUrl() . '/repair_ostatus?counter='.$counter.'">';
|
||||
|
||||
|
|
|
|||
|
|
@ -42,15 +42,11 @@ function salmon_post(App $a, $xml = '') {
|
|||
|
||||
$nick = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : '');
|
||||
|
||||
$r = q("SELECT * FROM `user` WHERE `nickname` = '%s' AND `account_expired` = 0 AND `account_removed` = 0 LIMIT 1",
|
||||
DBA::escape($nick)
|
||||
);
|
||||
if (! DBA::isResult($r)) {
|
||||
$importer = DBA::selectFirst('user', [], ['nickname' => $nick, 'account_expired' => false, 'account_removed' => false]);
|
||||
if (! DBA::isResult($importer)) {
|
||||
throw new \Friendica\Network\HTTPException\InternalServerErrorException();
|
||||
}
|
||||
|
||||
$importer = $r[0];
|
||||
|
||||
// parse the xml
|
||||
|
||||
$dom = simplexml_load_string($xml,'SimpleXMLElement',0, ActivityNamespace::SALMON_ME);
|
||||
|
|
@ -83,7 +79,7 @@ function salmon_post(App $a, $xml = '') {
|
|||
// stash away some other stuff for later
|
||||
|
||||
$type = $base->data[0]->attributes()->type[0];
|
||||
$keyhash = $base->sig[0]->attributes()->keyhash[0];
|
||||
$keyhash = $base->sig[0]->attributes()->keyhash[0] ?? '';
|
||||
$encoding = $base->encoding;
|
||||
$alg = $base->alg;
|
||||
|
||||
|
|
@ -124,7 +120,7 @@ function salmon_post(App $a, $xml = '') {
|
|||
$m = Strings::base64UrlDecode($key_info[1]);
|
||||
$e = Strings::base64UrlDecode($key_info[2]);
|
||||
|
||||
Logger::log('key details: ' . print_r($key_info,true), Logger::DEBUG);
|
||||
Logger::info('key details', ['info' => $key_info]);
|
||||
|
||||
$pubkey = Crypto::meToPem($m, $e);
|
||||
|
||||
|
|
@ -175,7 +171,7 @@ function salmon_post(App $a, $xml = '') {
|
|||
Logger::log('Author ' . $author_link . ' unknown to user ' . $importer['uid'] . '.');
|
||||
|
||||
if (DI::pConfig()->get($importer['uid'], 'system', 'ostatus_autofriend')) {
|
||||
$result = Contact::createFromProbe($importer['uid'], $author_link);
|
||||
$result = Contact::createFromProbe($importer, $author_link);
|
||||
|
||||
if ($result['success']) {
|
||||
$r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s')
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ use Friendica\Core\Worker;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GContact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Notify\Type;
|
||||
use Friendica\Model\User;
|
||||
|
|
@ -63,7 +62,7 @@ function settings_post(App $a)
|
|||
}
|
||||
|
||||
if (count($a->user) && !empty($a->user['uid']) && $a->user['uid'] != local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +182,7 @@ function settings_post(App $a)
|
|||
intval($mail_pubmail),
|
||||
intval(local_user())
|
||||
);
|
||||
Logger::log("mail: updating mailaccount. Response: ".print_r($r, true));
|
||||
Logger::notice('updating mailaccount', ['response' => $r]);
|
||||
$r = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1",
|
||||
intval(local_user())
|
||||
);
|
||||
|
|
@ -198,13 +197,10 @@ function settings_post(App $a)
|
|||
unset($dcrpass);
|
||||
if (!$mbox) {
|
||||
$failed = true;
|
||||
notice(DI::l10n()->t('Failed to connect with email account using the settings provided.') . EOL);
|
||||
notice(DI::l10n()->t('Failed to connect with email account using the settings provided.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$failed) {
|
||||
info(DI::l10n()->t('Email settings updated.') . EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +215,6 @@ function settings_post(App $a)
|
|||
DI::pConfig()->set(local_user(), 'feature', substr($k, 8), ((intval($v)) ? 1 : 0));
|
||||
}
|
||||
}
|
||||
info(DI::l10n()->t('Features updated') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -230,10 +225,11 @@ function settings_post(App $a)
|
|||
if (isset($_FILES['importcontact-filename'])) {
|
||||
// was there an error
|
||||
if ($_FILES['importcontact-filename']['error'] > 0) {
|
||||
Logger::notice('Contact CSV file upload error');
|
||||
info(DI::l10n()->t('Contact CSV file upload error'));
|
||||
Logger::notice('Contact CSV file upload error', ['error' => $_FILES['importcontact-filename']['error']]);
|
||||
notice(DI::l10n()->t('Contact CSV file upload error'));
|
||||
} else {
|
||||
$csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name']));
|
||||
Logger::info('Import started', ['lines' => count($csvArray)]);
|
||||
// import contacts
|
||||
foreach ($csvArray as $csvRow) {
|
||||
// The 1st row may, or may not contain the headers of the table
|
||||
|
|
@ -245,11 +241,14 @@ function settings_post(App $a)
|
|||
Worker::add(PRIORITY_LOW, 'AddContact', $_SESSION['uid'], $csvRow[0]);
|
||||
}
|
||||
}
|
||||
Logger::info('Import done');
|
||||
|
||||
info(DI::l10n()->t('Importing Contacts done'));
|
||||
// delete temp file
|
||||
unlink($_FILES['importcontact-filename']['tmp_name']);
|
||||
}
|
||||
} else {
|
||||
Logger::info('Import triggered, but no import file was found.');
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -424,10 +423,10 @@ function settings_post(App $a)
|
|||
$hidewall = 1;
|
||||
if (!$str_contact_allow && !$str_group_allow && !$str_contact_deny && !$str_group_deny) {
|
||||
if ($def_gid) {
|
||||
info(DI::l10n()->t('Private forum has no privacy permissions. Using default privacy group.'). EOL);
|
||||
info(DI::l10n()->t('Private forum has no privacy permissions. Using default privacy group.'));
|
||||
$str_group_allow = '<' . $def_gid . '>';
|
||||
} else {
|
||||
notice(DI::l10n()->t('Private forum has no privacy permissions and no default privacy group.') . EOL);
|
||||
notice(DI::l10n()->t('Private forum has no privacy permissions and no default privacy group.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -443,8 +442,8 @@ function settings_post(App $a)
|
|||
$fields['openidserver'] = '';
|
||||
}
|
||||
|
||||
if (DBA::update('user', $fields, ['uid' => local_user()])) {
|
||||
info(DI::l10n()->t('Settings updated.') . EOL);
|
||||
if (!DBA::update('user', $fields, ['uid' => local_user()])) {
|
||||
notice(DI::l10n()->t('Settings were not updated.'));
|
||||
}
|
||||
|
||||
// clear session language
|
||||
|
|
@ -475,9 +474,6 @@ function settings_post(App $a)
|
|||
|
||||
Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user());
|
||||
|
||||
// Update the global contact for the user
|
||||
GContact::updateForUser(local_user());
|
||||
|
||||
DI::baseUrl()->redirect('settings');
|
||||
return; // NOTREACHED
|
||||
}
|
||||
|
|
@ -489,12 +485,12 @@ function settings_content(App $a)
|
|||
Nav::setSelected('settings');
|
||||
|
||||
if (!local_user()) {
|
||||
//notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
//notice(DI::l10n()->t('Permission denied.'));
|
||||
return Login::form();
|
||||
}
|
||||
|
||||
if (!empty($_SESSION['submanage'])) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -722,7 +718,7 @@ function settings_content(App $a)
|
|||
|
||||
$profile = DBA::selectFirst('profile', [], ['uid' => local_user()]);
|
||||
if (!DBA::isResult($profile)) {
|
||||
notice(DI::l10n()->t('Unable to find your profile. Please contact your admin.') . EOL);
|
||||
notice(DI::l10n()->t('Unable to find your profile. Please contact your admin.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -837,26 +833,6 @@ function settings_content(App $a)
|
|||
|
||||
$stpl = Renderer::getMarkupTemplate('settings/settings.tpl');
|
||||
|
||||
// Private/public post links for the non-JS ACL form
|
||||
$private_post = 1;
|
||||
if (!empty($_REQUEST['public']) && !$_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';
|
||||
}
|
||||
|
||||
/* Installed langs */
|
||||
$lang_choices = DI::l10n()->getAvailableLanguages();
|
||||
|
||||
|
|
@ -874,7 +850,7 @@ function settings_content(App $a)
|
|||
'$password1'=> ['password', DI::l10n()->t('New Password:'), '', DI::l10n()->t('Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces, accentuated letters and colon (:).')],
|
||||
'$password2'=> ['confirm', DI::l10n()->t('Confirm:'), '', DI::l10n()->t('Leave password fields blank unless changing')],
|
||||
'$password3'=> ['opassword', DI::l10n()->t('Current Password:'), '', DI::l10n()->t('Your current password to confirm the changes')],
|
||||
'$password4'=> ['mpassword', DI::l10n()->t('Password:'), '', DI::l10n()->t('Your current password to confirm the changes')],
|
||||
'$password4'=> ['mpassword', DI::l10n()->t('Password:'), '', DI::l10n()->t('Your current password to confirm the changes of the email address')],
|
||||
'$oid_enable' => (!DI::config()->get('system', 'no_openid')),
|
||||
'$openid' => $openid_field,
|
||||
'$delete_openid' => ['delete_openid', DI::l10n()->t('Delete OpenID URL'), false, ''],
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Item;
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ function share_init(App $a) {
|
|||
$pos = strpos($item['body'], "[share");
|
||||
$o = substr($item['body'], $pos);
|
||||
} else {
|
||||
$o = share_header($item['author-name'], $item['author-link'], $item['author-avatar'], $item['guid'], $item['created'], $item['plink']);
|
||||
$o = BBCode::getShareOpeningTag($item['author-name'], $item['author-link'], $item['author-avatar'], $item['plink'], $item['created'], $item['guid']);
|
||||
|
||||
if ($item['title']) {
|
||||
$o .= '[h3]'.$item['title'].'[/h3]'."\n";
|
||||
|
|
@ -55,22 +56,3 @@ function share_init(App $a) {
|
|||
echo $o;
|
||||
exit();
|
||||
}
|
||||
|
||||
/// @TODO Rewrite to handle over whole record array
|
||||
function share_header($author, $profile, $avatar, $guid, $posted, $link) {
|
||||
$header = "[share author='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $author).
|
||||
"' profile='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $profile).
|
||||
"' avatar='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $avatar);
|
||||
|
||||
if ($guid) {
|
||||
$header .= "' guid='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $guid);
|
||||
}
|
||||
|
||||
if ($posted) {
|
||||
$header .= "' posted='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $posted);
|
||||
}
|
||||
|
||||
$header .= "' link='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $link)."']";
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ function subthread_content(App $a)
|
|||
|
||||
$item_id = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : 0);
|
||||
|
||||
if (!Item::performActivity($item_id, 'follow')) {
|
||||
if (!Item::performActivity($item_id, 'follow', local_user())) {
|
||||
Logger::info('Following item failed', ['item' => $item_id]);
|
||||
throw new HTTPException\BadRequestException();
|
||||
}
|
||||
|
|
|
|||
101
mod/suggest.php
|
|
@ -20,39 +20,18 @@
|
|||
*/
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Content\ContactSelector;
|
||||
use Friendica\Content\Widget;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GContact;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
|
||||
function suggest_init(App $a)
|
||||
{
|
||||
if (! local_user()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function suggest_post(App $a)
|
||||
{
|
||||
if (!empty($_POST['ignore']) && !empty($_POST['confirm'])) {
|
||||
DBA::insert('gcign', ['uid' => local_user(), 'gcid' => $_POST['ignore']]);
|
||||
notice(DI::l10n()->t('Contact suggestion successfully ignored.'));
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect('suggest');
|
||||
}
|
||||
use Friendica\Module\Contact as ModuleContact;
|
||||
use Friendica\Network\HTTPException;
|
||||
|
||||
function suggest_content(App $a)
|
||||
{
|
||||
$o = '';
|
||||
|
||||
if (! local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
return;
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.'));
|
||||
}
|
||||
|
||||
$_SESSION['return_path'] = DI::args()->getCommand();
|
||||
|
|
@ -60,80 +39,20 @@ function suggest_content(App $a)
|
|||
DI::page()['aside'] .= Widget::findPeople();
|
||||
DI::page()['aside'] .= Widget::follow();
|
||||
|
||||
|
||||
$r = GContact::suggestionQuery(local_user());
|
||||
|
||||
if (! DBA::isResult($r)) {
|
||||
$o .= DI::l10n()->t('No suggestions available. If this is a new site, please try again in 24 hours.');
|
||||
return $o;
|
||||
$contacts = Contact\Relation::getSuggestions(local_user());
|
||||
if (!DBA::isResult($contacts)) {
|
||||
return DI::l10n()->t('No suggestions available. If this is a new site, please try again in 24 hours.');
|
||||
}
|
||||
|
||||
|
||||
if (!empty($_GET['ignore'])) {
|
||||
// <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' => 'post',
|
||||
'$message' => DI::l10n()->t('Do you really want to delete this suggestion?'),
|
||||
'$extra_inputs' => $inputs,
|
||||
'$confirm' => DI::l10n()->t('Yes'),
|
||||
'$confirm_url' => $query['base'],
|
||||
'$confirm_name' => 'confirm',
|
||||
'$cancel' => DI::l10n()->t('Cancel'),
|
||||
]);
|
||||
}
|
||||
|
||||
$id = 0;
|
||||
$entries = [];
|
||||
|
||||
foreach ($r as $rr) {
|
||||
$connlnk = DI::baseUrl() . '/follow/?url=' . (($rr['connect']) ? $rr['connect'] : $rr['url']);
|
||||
$ignlnk = DI::baseUrl() . '/suggest?ignore=' . $rr['id'];
|
||||
$photo_menu = [
|
||||
'profile' => [DI::l10n()->t("View Profile"), Contact::magicLink($rr["url"])],
|
||||
'follow' => [DI::l10n()->t("Connect/Follow"), $connlnk],
|
||||
'hide' => [DI::l10n()->t('Ignore/Hide'), $ignlnk]
|
||||
];
|
||||
|
||||
$contact_details = Contact::getDetailsByURL($rr["url"], local_user(), $rr);
|
||||
|
||||
$entry = [
|
||||
'url' => Contact::magicLink($rr['url']),
|
||||
'itemurl' => (($contact_details['addr'] != "") ? $contact_details['addr'] : $rr['url']),
|
||||
'img_hover' => $rr['url'],
|
||||
'name' => $contact_details['name'],
|
||||
'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB),
|
||||
'details' => $contact_details['location'],
|
||||
'tags' => $contact_details['keywords'],
|
||||
'about' => $contact_details['about'],
|
||||
'account_type' => Contact::getAccountType($contact_details),
|
||||
'ignlnk' => $ignlnk,
|
||||
'ignid' => $rr['id'],
|
||||
'conntxt' => DI::l10n()->t('Connect'),
|
||||
'connlnk' => $connlnk,
|
||||
'photo_menu' => $photo_menu,
|
||||
'ignore' => DI::l10n()->t('Ignore/Hide'),
|
||||
'network' => ContactSelector::networkToName($rr['network'], $rr['url']),
|
||||
'id' => ++$id,
|
||||
];
|
||||
$entries[] = $entry;
|
||||
foreach ($contacts as $contact) {
|
||||
$entries[] = ModuleContact::getContactTemplateVars($contact);
|
||||
}
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl');
|
||||
|
||||
$o .= Renderer::replaceMacros($tpl,[
|
||||
return Renderer::replaceMacros($tpl,[
|
||||
'$title' => DI::l10n()->t('Friend Suggestions'),
|
||||
'$contacts' => $entries,
|
||||
]);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ EOT;
|
|||
$arr['wall'] = $item['wall'];
|
||||
$arr['gravity'] = GRAVITY_COMMENT;
|
||||
$arr['parent'] = $item['id'];
|
||||
$arr['parent-uri'] = $item['uri'];
|
||||
$arr['thr-parent'] = $item['uri'];
|
||||
$arr['owner-name'] = $item['author-name'];
|
||||
$arr['owner-link'] = $item['author-link'];
|
||||
$arr['owner-avatar'] = $item['author-avatar'];
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ function tagrm_post(App $a)
|
|||
|
||||
$item_id = $_POST['item'] ?? 0;
|
||||
update_tags($item_id, $tags);
|
||||
info(DI::l10n()->t('Tag(s) removed') . EOL);
|
||||
|
||||
DI::baseUrl()->redirect($_SESSION['photo_return']);
|
||||
// NOTREACHED
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use Friendica\DI;
|
|||
function uimport_post(App $a)
|
||||
{
|
||||
if ((DI::config()->get('config', 'register_policy') != \Friendica\Module\Register::OPEN) && !is_site_admin()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ function uimport_post(App $a)
|
|||
function uimport_content(App $a)
|
||||
{
|
||||
if ((DI::config()->get('config', 'register_policy') != \Friendica\Module\Register::OPEN) && !is_site_admin()) {
|
||||
notice(DI::l10n()->t('User imports on closed servers can only be done by an administrator.') . EOL);
|
||||
notice(DI::l10n()->t('User imports on closed servers can only be done by an administrator.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ function uimport_content(App $a)
|
|||
$r = q("select count(*) as total from user where register_date > UTC_TIMESTAMP - INTERVAL 1 day");
|
||||
if ($r && $r[0]['total'] >= $max_dailies) {
|
||||
Logger::log('max daily registrations exceeded.');
|
||||
notice(DI::l10n()->t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL);
|
||||
notice(DI::l10n()->t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,57 +31,15 @@ use Friendica\Util\Strings;
|
|||
|
||||
function unfollow_post(App $a)
|
||||
{
|
||||
$base_return_path = 'contact';
|
||||
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
DI::baseUrl()->redirect('login');
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
$uid = local_user();
|
||||
$url = Strings::escapeTags(trim($_REQUEST['url'] ?? ''));
|
||||
|
||||
$condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
|
||||
$uid, Contact::SHARING, Contact::FRIEND, Strings::normaliseLink($url),
|
||||
Strings::normaliseLink($url), $url];
|
||||
$contact = DBA::selectFirst('contact', [], $condition);
|
||||
|
||||
if (!DBA::isResult($contact)) {
|
||||
notice(DI::l10n()->t("You aren't following this contact."));
|
||||
DI::baseUrl()->redirect($base_return_path);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
if (!empty($_REQUEST['cancel'])) {
|
||||
DI::baseUrl()->redirect($base_return_path . '/' . $contact['id']);
|
||||
}
|
||||
|
||||
if (!in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
|
||||
notice(DI::l10n()->t('Unfollowing is currently not supported by your network.'));
|
||||
DI::baseUrl()->redirect($base_return_path . '/' . $contact['id']);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
$dissolve = ($contact['rel'] == Contact::SHARING);
|
||||
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if ($owner) {
|
||||
Contact::terminateFriendship($owner, $contact, $dissolve);
|
||||
}
|
||||
|
||||
// Sharing-only contacts get deleted as there no relationship any more
|
||||
if ($dissolve) {
|
||||
Contact::remove($contact['id']);
|
||||
$return_path = $base_return_path;
|
||||
} else {
|
||||
DBA::update('contact', ['rel' => Contact::FOLLOWER], ['id' => $contact['id']]);
|
||||
$return_path = $base_return_path . '/' . $contact['id'];
|
||||
}
|
||||
|
||||
info(DI::l10n()->t('Contact unfollowed'));
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
// NOTREACHED
|
||||
unfollow_process($url);
|
||||
}
|
||||
|
||||
function unfollow_content(App $a)
|
||||
|
|
@ -129,6 +87,10 @@ function unfollow_content(App $a)
|
|||
// Makes the connection request for friendica contacts easier
|
||||
$_SESSION['fastlane'] = $contact['url'];
|
||||
|
||||
if (!empty($_REQUEST['auto'])) {
|
||||
unfollow_process($contact['url']);
|
||||
}
|
||||
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$header' => DI::l10n()->t('Disconnect/Unfollow'),
|
||||
'$page_desc' => '',
|
||||
|
|
@ -146,7 +108,7 @@ function unfollow_content(App $a)
|
|||
]);
|
||||
|
||||
DI::page()['aside'] = '';
|
||||
Profile::load($a, '', Contact::getDetailsByURL($contact['url']));
|
||||
Profile::load($a, '', Contact::getByURL($contact['url'], false));
|
||||
|
||||
$o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), ['$title' => DI::l10n()->t('Status Messages and Posts')]);
|
||||
|
||||
|
|
@ -155,3 +117,45 @@ function unfollow_content(App $a)
|
|||
|
||||
return $o;
|
||||
}
|
||||
|
||||
function unfollow_process(string $url)
|
||||
{
|
||||
$base_return_path = 'contact';
|
||||
|
||||
$uid = local_user();
|
||||
|
||||
$condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
|
||||
$uid, Contact::SHARING, Contact::FRIEND, Strings::normaliseLink($url),
|
||||
Strings::normaliseLink($url), $url];
|
||||
$contact = DBA::selectFirst('contact', [], $condition);
|
||||
|
||||
if (!DBA::isResult($contact)) {
|
||||
notice(DI::l10n()->t("You aren't following this contact."));
|
||||
DI::baseUrl()->redirect($base_return_path);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
if (!in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
|
||||
notice(DI::l10n()->t('Unfollowing is currently not supported by your network.'));
|
||||
DI::baseUrl()->redirect($base_return_path . '/' . $contact['id']);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
$dissolve = ($contact['rel'] == Contact::SHARING);
|
||||
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if ($owner) {
|
||||
Contact::terminateFriendship($owner, $contact, $dissolve);
|
||||
}
|
||||
|
||||
// Sharing-only contacts get deleted as there no relationship any more
|
||||
if ($dissolve) {
|
||||
Contact::remove($contact['id']);
|
||||
$return_path = $base_return_path;
|
||||
} else {
|
||||
DBA::update('contact', ['rel' => Contact::FOLLOWER], ['id' => $contact['id']]);
|
||||
$return_path = $base_return_path . '/' . $contact['id'];
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
}
|
||||
|
|
@ -24,15 +24,21 @@
|
|||
use Friendica\App;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Module\Contact;
|
||||
|
||||
function update_contact_content(App $a)
|
||||
{
|
||||
if (!empty($_GET['force']) || !DI::pConfig()->get(local_user(), 'system', 'no_auto_update')) {
|
||||
$text = Contact::content([], true);
|
||||
if (!empty($a->argv[1]) && (!empty($_GET['force']) || !DI::pConfig()->get(local_user(), 'system', 'no_auto_update'))) {
|
||||
if (!empty($_GET['item'])) {
|
||||
$item = Item::selectFirst(['parent'], ['id' => $_GET['item']]);
|
||||
$parentid = $item['parent'] ?? 0;
|
||||
} else {
|
||||
$parentid = 0;
|
||||
}
|
||||
$text = Contact::getConversationsHMTL($a, $a->argv[1], true, $parentid);
|
||||
} else {
|
||||
$text = '';
|
||||
}
|
||||
|
||||
System::htmlUpdateExit($text);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ use Friendica\Model\Item;
|
|||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Module\BaseProfile;
|
||||
use Friendica\Util\Security;
|
||||
use Friendica\Security\Security;
|
||||
|
||||
function videos_init(App $a)
|
||||
{
|
||||
|
|
@ -67,7 +67,7 @@ function videos_init(App $a)
|
|||
'$photo' => $profile['photo'],
|
||||
'$addr' => $profile['addr'] ?? '',
|
||||
'$account_type' => $account_type,
|
||||
'$about' => BBCode::convert($profile['about'] ?? ''),
|
||||
'$about' => BBCode::convert($profile['about']),
|
||||
]);
|
||||
|
||||
// If not there, create 'aside' empty
|
||||
|
|
@ -126,7 +126,7 @@ function videos_content(App $a)
|
|||
|
||||
|
||||
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
|
||||
notice(DI::l10n()->t('Public access denied.') . EOL);
|
||||
notice(DI::l10n()->t('Public access denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +179,7 @@ function videos_content(App $a)
|
|||
}
|
||||
|
||||
if ($a->data['user']['hidewall'] && (local_user() != $owner_uid) && !$remote_contact) {
|
||||
notice(DI::l10n()->t('Access to this item is restricted.') . EOL);
|
||||
notice(DI::l10n()->t('Access to this item is restricted.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ function wall_attach_post(App $a) {
|
|||
if ($r_json) {
|
||||
echo json_encode(['error' => $msg]);
|
||||
} else {
|
||||
notice($msg . EOL);
|
||||
notice($msg);
|
||||
}
|
||||
@unlink($src);
|
||||
exit();
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
|||
echo json_encode(['error' => DI::l10n()->t('Permission denied.')]);
|
||||
exit();
|
||||
}
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
exit();
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
|||
echo json_encode(['error' => DI::l10n()->t('Invalid request.')]);
|
||||
exit();
|
||||
}
|
||||
notice(DI::l10n()->t('Invalid request.').EOL);
|
||||
notice(DI::l10n()->t('Invalid request.'));
|
||||
exit();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ function wallmessage_post(App $a) {
|
|||
|
||||
$replyto = Profile::getMyURL();
|
||||
if (!$replyto) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ function wallmessage_post(App $a) {
|
|||
$user = $r[0];
|
||||
|
||||
if (! intval($user['unkmail'])) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -73,19 +73,17 @@ function wallmessage_post(App $a) {
|
|||
|
||||
switch ($ret) {
|
||||
case -1:
|
||||
notice(DI::l10n()->t('No recipient selected.') . EOL);
|
||||
notice(DI::l10n()->t('No recipient selected.'));
|
||||
break;
|
||||
case -2:
|
||||
notice(DI::l10n()->t('Unable to check your home location.') . EOL);
|
||||
notice(DI::l10n()->t('Unable to check your home location.'));
|
||||
break;
|
||||
case -3:
|
||||
notice(DI::l10n()->t('Message could not be sent.') . EOL);
|
||||
notice(DI::l10n()->t('Message could not be sent.'));
|
||||
break;
|
||||
case -4:
|
||||
notice(DI::l10n()->t('Message collection failure.') . EOL);
|
||||
notice(DI::l10n()->t('Message collection failure.'));
|
||||
break;
|
||||
default:
|
||||
info(DI::l10n()->t('Message sent.') . EOL);
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect('profile/'.$user['nickname']);
|
||||
|
|
@ -95,14 +93,14 @@ function wallmessage_post(App $a) {
|
|||
function wallmessage_content(App $a) {
|
||||
|
||||
if (!Profile::getMyURL()) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$recipient = (($a->argc > 1) ? $a->argv[1] : '');
|
||||
|
||||
if (!$recipient) {
|
||||
notice(DI::l10n()->t('No recipient.') . EOL);
|
||||
notice(DI::l10n()->t('No recipient.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +109,7 @@ function wallmessage_content(App $a) {
|
|||
);
|
||||
|
||||
if (! DBA::isResult($r)) {
|
||||
notice(DI::l10n()->t('No recipient.') . EOL);
|
||||
notice(DI::l10n()->t('No recipient.'));
|
||||
Logger::log('wallmessage: no recipient');
|
||||
return;
|
||||
}
|
||||
|
|
@ -119,7 +117,7 @@ function wallmessage_content(App $a) {
|
|||
$user = $r[0];
|
||||
|
||||
if (!intval($user['unkmail'])) {
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL);
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ volumes:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -79,7 +79,7 @@ volumes:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -122,7 +122,7 @@ volumes:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -169,7 +169,7 @@ volumes:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -211,7 +211,7 @@ volumes:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -253,7 +253,7 @@ volumes:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -282,7 +282,7 @@ services:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -306,7 +306,7 @@ services:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -330,7 +330,7 @@ services:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -360,7 +360,7 @@ services:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -384,7 +384,7 @@ services:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -408,7 +408,7 @@ services:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -439,7 +439,7 @@ services:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -463,7 +463,7 @@ services:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
@ -487,7 +487,7 @@ services:
|
|||
|
||||
trigger:
|
||||
branch:
|
||||
# - master
|
||||
# - stable
|
||||
- develop
|
||||
# - "*-rc"
|
||||
# event:
|
||||
|
|
|
|||
|
|
@ -105,6 +105,9 @@ $HTTP["scheme"] == "https" {
|
|||
"^\/([^\?]*)\?(.*)$" => "/index.php?pagename=$1&$2",
|
||||
"^\/(.*)$" => "/index.php?pagename=$1"
|
||||
)
|
||||
$HOST["url"] =~ "^/bin/" {
|
||||
url.access.deny ( "" )
|
||||
}
|
||||
}
|
||||
else $HTTP["host"] !~ "(friendica.example.com|wordpress.example.com)" {
|
||||
server.document-root = "/var/www/wordpress"
|
||||
|
|
|
|||
|
|
@ -141,4 +141,9 @@ server {
|
|||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# deny access to the CLI scripts
|
||||
location ^~ /bin {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 32 KiB |
37
src/App.php
|
|
@ -24,7 +24,7 @@ namespace Friendica;
|
|||
use Exception;
|
||||
use Friendica\App\Arguments;
|
||||
use Friendica\App\BaseURL;
|
||||
use Friendica\App\Authentication;
|
||||
use Friendica\Security\Authentication;
|
||||
use Friendica\Core\Config\Cache;
|
||||
use Friendica\Core\Config\IConfig;
|
||||
use Friendica\Core\PConfig\IPConfig;
|
||||
|
|
@ -77,7 +77,6 @@ class App
|
|||
public $sourcename = '';
|
||||
public $videowidth = 425;
|
||||
public $videoheight = 350;
|
||||
public $force_max_items = 0;
|
||||
public $theme_events_in_profile = true;
|
||||
public $queue;
|
||||
|
||||
|
|
@ -240,22 +239,6 @@ class App
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current UserAgent as a String
|
||||
*
|
||||
* @return string the UserAgent as a String
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public function getUserAgent()
|
||||
{
|
||||
return
|
||||
FRIENDICA_PLATFORM . " '" .
|
||||
FRIENDICA_CODENAME . "' " .
|
||||
FRIENDICA_VERSION . '-' .
|
||||
DB_UPDATE_VERSION . '; ' .
|
||||
$this->baseURL->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current theme name. May be overriden by the mobile theme name.
|
||||
*
|
||||
|
|
@ -432,8 +415,11 @@ class App
|
|||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public function runFrontend(App\Module $module, App\Router $router, IPConfig $pconfig, Authentication $auth, App\Page $page)
|
||||
public function runFrontend(App\Module $module, App\Router $router, IPConfig $pconfig, Authentication $auth, App\Page $page, float $start_time)
|
||||
{
|
||||
$this->profiler->set($start_time, 'start');
|
||||
$this->profiler->set(microtime(true), 'classinit');
|
||||
|
||||
$moduleName = $module->getName();
|
||||
|
||||
try {
|
||||
|
|
@ -459,12 +445,7 @@ class App
|
|||
Core\Hook::callAll('init_1');
|
||||
}
|
||||
|
||||
// Exclude the backend processes from the session management
|
||||
if ($this->mode->isBackend()) {
|
||||
Core\Worker::executeIfIdle();
|
||||
}
|
||||
|
||||
if ($this->mode->isNormal()) {
|
||||
if ($this->mode->isNormal() && !$this->mode->isBackend()) {
|
||||
$requester = HTTPSignature::getSigner('', $_SERVER);
|
||||
if (!empty($requester)) {
|
||||
Profile::addVisitorCookieForHandle($requester);
|
||||
|
|
@ -472,7 +453,7 @@ class App
|
|||
}
|
||||
|
||||
// ZRL
|
||||
if (!empty($_GET['zrl']) && $this->mode->isNormal()) {
|
||||
if (!empty($_GET['zrl']) && $this->mode->isNormal() && !$this->mode->isBackend()) {
|
||||
if (!local_user()) {
|
||||
// Only continue when the given profile link seems valid
|
||||
// Valid profile links contain a path with "/profile/" and no query parameters
|
||||
|
|
@ -568,12 +549,12 @@ class App
|
|||
$module = $module->determineClass($this->args, $router, $this->config);
|
||||
|
||||
// Let the module run it's internal process (init, get, post, ...)
|
||||
$module->run($this->l10n, $this->baseURL, $this->logger, $_SERVER, $_POST);
|
||||
$module->run($this->l10n, $this->baseURL, $this->logger, $this->profiler, $_SERVER, $_POST);
|
||||
} catch (HTTPException $e) {
|
||||
ModuleHTTPException::rawContent($e);
|
||||
}
|
||||
|
||||
$page->run($this, $this->baseURL, $this->mode, $module, $this->l10n, $this->config, $pconfig);
|
||||
$page->run($this, $this->baseURL, $this->mode, $module, $this->l10n, $this->profiler, $this->config, $pconfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class Arguments
|
|||
*/
|
||||
private $argc;
|
||||
|
||||
public function __construct(string $queryString = '', string $command = '', array $argv = [Module::DEFAULT], int $argc = 1)
|
||||
public function __construct(string $queryString = '', string $command = '', array $argv = [], int $argc = 0)
|
||||
{
|
||||
$this->queryString = $queryString;
|
||||
$this->command = $command;
|
||||
|
|
@ -56,7 +56,7 @@ class Arguments
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string The whole query string of this call
|
||||
* @return string The whole query string of this call with url-encoded query parameters
|
||||
*/
|
||||
public function getQueryString()
|
||||
{
|
||||
|
|
@ -121,50 +121,27 @@ class Arguments
|
|||
*/
|
||||
public function determine(array $server, array $get)
|
||||
{
|
||||
$queryString = '';
|
||||
// removing leading / - maybe a nginx problem
|
||||
$server['QUERY_STRING'] = ltrim($server['QUERY_STRING'] ?? '', '/');
|
||||
|
||||
if (!empty($server['QUERY_STRING']) && strpos($server['QUERY_STRING'], 'pagename=') === 0) {
|
||||
$queryString = urldecode(substr($server['QUERY_STRING'], 9));
|
||||
} elseif (!empty($server['QUERY_STRING']) && strpos($server['QUERY_STRING'], 'q=') === 0) {
|
||||
$queryString = urldecode(substr($server['QUERY_STRING'], 2));
|
||||
}
|
||||
|
||||
// eventually strip ZRL
|
||||
$queryString = $this->stripZRLs($queryString);
|
||||
|
||||
// eventually strip OWT
|
||||
$queryString = $this->stripQueryParam($queryString, 'owt');
|
||||
|
||||
// removing trailing / - maybe a nginx problem
|
||||
$queryString = ltrim($queryString, '/');
|
||||
$queryParameters = [];
|
||||
parse_str($server['QUERY_STRING'], $queryParameters);
|
||||
|
||||
if (!empty($get['pagename'])) {
|
||||
$command = trim($get['pagename'], '/\\');
|
||||
} elseif (!empty($queryParameters['pagename'])) {
|
||||
$command = trim($queryParameters['pagename'], '/\\');
|
||||
} elseif (!empty($get['q'])) {
|
||||
// Legacy page name parameter, now conflicts with the search query parameter
|
||||
$command = trim($get['q'], '/\\');
|
||||
} else {
|
||||
$command = Module::DEFAULT;
|
||||
$command = '';
|
||||
}
|
||||
|
||||
|
||||
// fix query_string
|
||||
if (!empty($command)) {
|
||||
$queryString = str_replace(
|
||||
$command . '&',
|
||||
$command . '?',
|
||||
$queryString
|
||||
);
|
||||
}
|
||||
|
||||
// unix style "homedir"
|
||||
if (substr($command, 0, 1) === '~') {
|
||||
$command = 'profile/' . substr($command, 1);
|
||||
}
|
||||
|
||||
// Diaspora style profile url
|
||||
if (substr($command, 0, 2) === 'u/') {
|
||||
$command = 'profile/' . substr($command, 2);
|
||||
}
|
||||
// Remove generated and one-time use parameters
|
||||
unset($queryParameters['pagename']);
|
||||
unset($queryParameters['zrl']);
|
||||
unset($queryParameters['owt']);
|
||||
|
||||
/*
|
||||
* Break the URL path into C style argc/argv style arguments for our
|
||||
|
|
@ -173,41 +150,17 @@ class Arguments
|
|||
* [0] => 'module'
|
||||
* [1] => 'arg1'
|
||||
* [2] => 'arg2'
|
||||
*
|
||||
*
|
||||
* There will always be one argument. If provided a naked domain
|
||||
* URL, $this->argv[0] is set to "home".
|
||||
*/
|
||||
if ($command) {
|
||||
$argv = explode('/', $command);
|
||||
} else {
|
||||
$argv = [];
|
||||
}
|
||||
|
||||
$argv = explode('/', $command);
|
||||
$argc = count($argv);
|
||||
|
||||
$queryString = $command . ($queryParameters ? '?' . http_build_query($queryParameters) : '');
|
||||
|
||||
return new Arguments($queryString, $command, $argv, $argc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip zrl parameter from a string.
|
||||
*
|
||||
* @param string $queryString The input string.
|
||||
*
|
||||
* @return string The zrl.
|
||||
*/
|
||||
public function stripZRLs(string $queryString)
|
||||
{
|
||||
return preg_replace('/[?&]zrl=(.*?)(&|$)/ism', '$2', $queryString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip query parameter from a string.
|
||||
*
|
||||
* @param string $queryString The input string.
|
||||
* @param string $param
|
||||
*
|
||||
* @return string The query parameter.
|
||||
*/
|
||||
public function stripQueryParam(string $queryString, string $param)
|
||||
{
|
||||
return preg_replace('/[?&]' . $param . '=(.*?)(&|$)/ism', '$2', $queryString);
|
||||
}
|
||||
}
|
||||
|
|
@ -38,12 +38,26 @@ class Mode
|
|||
const DBCONFIGAVAILABLE = 4;
|
||||
const MAINTENANCEDISABLED = 8;
|
||||
|
||||
const UNDEFINED = 0;
|
||||
const INDEX = 1;
|
||||
const DAEMON = 2;
|
||||
const WORKER = 3;
|
||||
|
||||
const BACKEND_CONTENT_TYPES = ['application/jrd+json', 'text/xml',
|
||||
'application/rss+xml', 'application/atom+xml', 'application/activity+json'];
|
||||
|
||||
/***
|
||||
* @var int The mode of this Application
|
||||
*
|
||||
*/
|
||||
private $mode;
|
||||
|
||||
/***
|
||||
* @var int Who executes this Application
|
||||
*
|
||||
*/
|
||||
private $executor = self::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @var bool True, if the call is a backend call
|
||||
*/
|
||||
|
|
@ -134,8 +148,13 @@ class Mode
|
|||
*/
|
||||
public function determineRunMode(bool $isBackend, Module $module, array $server, MobileDetect $mobileDetect)
|
||||
{
|
||||
$isBackend = $isBackend ||
|
||||
$module->isBackend();
|
||||
foreach (self::BACKEND_CONTENT_TYPES as $type) {
|
||||
if (strpos(strtolower($server['HTTP_ACCEPT'] ?? ''), $type) !== false) {
|
||||
$isBackend = true;
|
||||
}
|
||||
}
|
||||
|
||||
$isBackend = $isBackend || $module->isBackend();
|
||||
$isMobile = $mobileDetect->isMobile();
|
||||
$isTablet = $mobileDetect->isTablet();
|
||||
$isAjax = strtolower($server['HTTP_X_REQUESTED_WITH'] ?? '') == 'xmlhttprequest';
|
||||
|
|
@ -155,6 +174,31 @@ class Mode
|
|||
return ($this->mode & $mode) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the execution mode
|
||||
*
|
||||
* @param integer $executor Execution Mode
|
||||
* @return void
|
||||
*/
|
||||
public function setExecutor(int $executor)
|
||||
{
|
||||
$this->executor = $executor;
|
||||
|
||||
// Daemon and worker are always backend
|
||||
if (in_array($executor, [self::DAEMON, self::WORKER])) {
|
||||
$this->isBackend = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*isBackend = true;*
|
||||
* get the execution mode
|
||||
*
|
||||
* @return int Execution Mode
|
||||
*/
|
||||
public function getExecutor()
|
||||
{
|
||||
return $this->executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install mode is when the local config file is missing or the DB schema hasn't been installed yet.
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ use Friendica\Module\HTTPException\MethodNotAllowed;
|
|||
use Friendica\Module\HTTPException\PageNotFound;
|
||||
use Friendica\Network\HTTPException\MethodNotAllowedException;
|
||||
use Friendica\Network\HTTPException\NotFoundException;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
|
|
@ -234,10 +235,10 @@ class Module
|
|||
*
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public function run(Core\L10n $l10n, App\BaseURL $baseUrl, LoggerInterface $logger, array $server, array $post)
|
||||
public function run(Core\L10n $l10n, App\BaseURL $baseUrl, LoggerInterface $logger, Profiler $profiler, array $server, array $post)
|
||||
{
|
||||
if ($this->printNotAllowedAddon) {
|
||||
info($l10n->t("You must be logged in to use addons. "));
|
||||
notice($l10n->t("You must be logged in to use addons. "));
|
||||
}
|
||||
|
||||
/* The URL provided does not resolve to a valid module.
|
||||
|
|
@ -266,10 +267,15 @@ class Module
|
|||
|
||||
$placeholder = '';
|
||||
|
||||
$profiler->set(microtime(true), 'ready');
|
||||
$timestamp = microtime(true);
|
||||
|
||||
Core\Hook::callAll($this->module . '_mod_init', $placeholder);
|
||||
|
||||
call_user_func([$this->module_class, 'init'], $this->module_parameters);
|
||||
|
||||
$profiler->set(microtime(true) - $timestamp, 'init');
|
||||
|
||||
if ($server['REQUEST_METHOD'] === 'POST') {
|
||||
Core\Hook::callAll($this->module . '_mod_post', $post);
|
||||
call_user_func([$this->module_class, 'post'], $this->module_parameters);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ use Friendica\Module\Special\HTTPException as ModuleHTTPException;
|
|||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Profiler;
|
||||
|
||||
/**
|
||||
* Contains the page specific environment variables for the current Page
|
||||
|
|
@ -165,11 +166,10 @@ class Page implements ArrayAccess
|
|||
* The path can be absolute or relative to the Friendica installation base folder.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @param string $media
|
||||
* @see Page::initHead()
|
||||
*
|
||||
*/
|
||||
public function registerStylesheet($path)
|
||||
public function registerStylesheet($path, string $media = 'screen')
|
||||
{
|
||||
$path = Network::appendQueryParam($path, ['v' => FRIENDICA_VERSION]);
|
||||
|
||||
|
|
@ -177,7 +177,7 @@ class Page implements ArrayAccess
|
|||
$path = mb_substr($path, mb_strlen($this->basePath . DIRECTORY_SEPARATOR));
|
||||
}
|
||||
|
||||
$this->stylesheets[] = trim($path, '/');
|
||||
$this->stylesheets[trim($path, '/')] = $media;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -234,7 +234,7 @@ class Page implements ArrayAccess
|
|||
|
||||
$touch_icon = $config->get('system', 'touch_icon');
|
||||
if ($touch_icon == '') {
|
||||
$touch_icon = 'images/friendica-128.png';
|
||||
$touch_icon = 'images/friendica-192.png';
|
||||
}
|
||||
|
||||
Hook::callAll('head', $this->page['htmlhead']);
|
||||
|
|
@ -252,7 +252,7 @@ class Page implements ArrayAccess
|
|||
'$shortcut_icon' => $shortcut_icon,
|
||||
'$touch_icon' => $touch_icon,
|
||||
'$block_public' => intval($config->get('system', 'block_public')),
|
||||
'$stylesheets' => array_unique($this->stylesheets),
|
||||
'$stylesheets' => $this->stylesheets,
|
||||
]) . $this->page['htmlhead'];
|
||||
}
|
||||
|
||||
|
|
@ -276,7 +276,7 @@ class Page implements ArrayAccess
|
|||
// If you're just visiting, let javascript take you home
|
||||
if (!empty($_SESSION['visitor_home'])) {
|
||||
$homebase = $_SESSION['visitor_home'];
|
||||
} elseif (local_user()) {
|
||||
} elseif (!empty($app->user['nickname'])) {
|
||||
$homebase = 'profile/' . $app->user['nickname'];
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +376,7 @@ class Page implements ArrayAccess
|
|||
*
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public function run(App $app, BaseURL $baseURL, Mode $mode, Module $module, L10n $l10n, IConfig $config, IPConfig $pconfig)
|
||||
public function run(App $app, BaseURL $baseURL, Mode $mode, Module $module, L10n $l10n, Profiler $profiler, IConfig $config, IPConfig $pconfig)
|
||||
{
|
||||
$moduleName = $module->getName();
|
||||
|
||||
|
|
@ -385,7 +385,9 @@ class Page implements ArrayAccess
|
|||
*
|
||||
* Sets the $Page->page['content'] variable
|
||||
*/
|
||||
$timestamp = microtime(true);
|
||||
$this->initContent($module, $mode);
|
||||
$profiler->set(microtime(true) - $timestamp, 'content');
|
||||
|
||||
// Load current theme info after module has been initialized as theme could have been set in module
|
||||
$currentTheme = $app->getCurrentTheme();
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ use FastRoute\DataGenerator\GroupCountBased;
|
|||
use FastRoute\Dispatcher;
|
||||
use FastRoute\RouteCollector;
|
||||
use FastRoute\RouteParser\Std;
|
||||
use Friendica\Core\Cache\Duration;
|
||||
use Friendica\Core\Cache\ICache;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Network\HTTPException;
|
||||
|
|
@ -42,12 +44,18 @@ use Friendica\Network\HTTPException;
|
|||
*/
|
||||
class Router
|
||||
{
|
||||
const POST = 'POST';
|
||||
const GET = 'GET';
|
||||
const DELETE = 'DELETE';
|
||||
const GET = 'GET';
|
||||
const PATCH = 'PATCH';
|
||||
const POST = 'POST';
|
||||
const PUT = 'PUT';
|
||||
|
||||
const ALLOWED_METHODS = [
|
||||
self::POST,
|
||||
self::DELETE,
|
||||
self::GET,
|
||||
self::PATCH,
|
||||
self::POST,
|
||||
self::PUT,
|
||||
];
|
||||
|
||||
/** @var RouteCollector */
|
||||
|
|
@ -66,14 +74,24 @@ class Router
|
|||
/** @var L10n */
|
||||
private $l10n;
|
||||
|
||||
/** @var ICache */
|
||||
private $cache;
|
||||
|
||||
/** @var string */
|
||||
private $baseRoutesFilepath;
|
||||
|
||||
/**
|
||||
* @param array $server The $_SERVER variable
|
||||
* @param L10n $l10n
|
||||
* @param RouteCollector|null $routeCollector Optional the loaded Route collector
|
||||
* @param array $server The $_SERVER variable
|
||||
* @param string $baseRoutesFilepath The path to a base routes file to leverage cache, can be empty
|
||||
* @param L10n $l10n
|
||||
* @param ICache $cache
|
||||
* @param RouteCollector|null $routeCollector
|
||||
*/
|
||||
public function __construct(array $server, L10n $l10n, RouteCollector $routeCollector = null)
|
||||
public function __construct(array $server, string $baseRoutesFilepath, L10n $l10n, ICache $cache, RouteCollector $routeCollector = null)
|
||||
{
|
||||
$this->baseRoutesFilepath = $baseRoutesFilepath;
|
||||
$this->l10n = $l10n;
|
||||
$this->cache = $cache;
|
||||
|
||||
$httpMethod = $server['REQUEST_METHOD'] ?? self::GET;
|
||||
$this->httpMethod = in_array($httpMethod, self::ALLOWED_METHODS) ? $httpMethod : self::GET;
|
||||
|
|
@ -81,9 +99,16 @@ class Router
|
|||
$this->routeCollector = isset($routeCollector) ?
|
||||
$routeCollector :
|
||||
new RouteCollector(new Std(), new GroupCountBased());
|
||||
|
||||
if ($this->baseRoutesFilepath && !file_exists($this->baseRoutesFilepath)) {
|
||||
throw new HTTPException\InternalServerErrorException('Routes file path does\'n exist.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will be called either automatically if a base routes file path was submitted,
|
||||
* or can be called manually with a custom route array.
|
||||
*
|
||||
* @param array $routes The routes to add to the Router
|
||||
*
|
||||
* @return self The router instance with the loaded routes
|
||||
|
|
@ -100,6 +125,9 @@ class Router
|
|||
|
||||
$this->routeCollector = $routeCollector;
|
||||
|
||||
// Add routes from addons
|
||||
Hook::callAll('route_collection', $this->routeCollector);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
@ -191,12 +219,9 @@ class Router
|
|||
*/
|
||||
public function getModuleClass($cmd)
|
||||
{
|
||||
// Add routes from addons
|
||||
Hook::callAll('route_collection', $this->routeCollector);
|
||||
|
||||
$cmd = '/' . ltrim($cmd, '/');
|
||||
|
||||
$dispatcher = new Dispatcher\GroupCountBased($this->routeCollector->getData());
|
||||
$dispatcher = new Dispatcher\GroupCountBased($this->getCachedDispatchData());
|
||||
|
||||
$moduleClass = null;
|
||||
$this->parameters = [];
|
||||
|
|
@ -223,4 +248,64 @@ class Router
|
|||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a base routes file path has been provided, we can load routes from it if the cache misses.
|
||||
*
|
||||
* @return array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
private function getDispatchData()
|
||||
{
|
||||
$dispatchData = [];
|
||||
|
||||
if ($this->baseRoutesFilepath) {
|
||||
$dispatchData = require $this->baseRoutesFilepath;
|
||||
if (!is_array($dispatchData)) {
|
||||
throw new HTTPException\InternalServerErrorException('Invalid base routes file');
|
||||
}
|
||||
}
|
||||
|
||||
$this->loadRoutes($dispatchData);
|
||||
|
||||
return $this->routeCollector->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* We cache the dispatch data for speed, as computing the current routes (version 2020.09)
|
||||
* takes about 850ms for each requests.
|
||||
*
|
||||
* The cached "routerDispatchData" lasts for a day, and must be cleared manually when there
|
||||
* is any changes in the enabled addons list.
|
||||
*
|
||||
* Additionally, we check for the base routes file last modification time to automatically
|
||||
* trigger re-computing the dispatch data.
|
||||
*
|
||||
* @return array|mixed
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
private function getCachedDispatchData()
|
||||
{
|
||||
$routerDispatchData = $this->cache->get('routerDispatchData');
|
||||
$lastRoutesFileModifiedTime = $this->cache->get('lastRoutesFileModifiedTime');
|
||||
$forceRecompute = false;
|
||||
|
||||
if ($this->baseRoutesFilepath) {
|
||||
$routesFileModifiedTime = filemtime($this->baseRoutesFilepath);
|
||||
$forceRecompute = $lastRoutesFileModifiedTime != $routesFileModifiedTime;
|
||||
}
|
||||
|
||||
if (!$forceRecompute && $routerDispatchData) {
|
||||
return $routerDispatchData;
|
||||
}
|
||||
|
||||
$routerDispatchData = $this->getDispatchData();
|
||||
|
||||
$this->cache->set('routerDispatchData', $routerDispatchData, Duration::DAY);
|
||||
if (!empty($routesFileModifiedTime)) {
|
||||
$this->cache->set('lastRoutesFileMtime', $routesFileModifiedTime, Duration::MONTH);
|
||||
}
|
||||
|
||||
return $routerDispatchData;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ abstract class BaseModel extends BaseEntity
|
|||
|
||||
protected function checkValid()
|
||||
{
|
||||
if (empty($this->data['id'])) {
|
||||
if (!isset($this->data['id']) || is_null($this->data['id'])) {
|
||||
throw new HTTPException\InternalServerErrorException(static::class . ' record uninitialized');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,11 +96,11 @@ abstract class BaseModule
|
|||
* Functions used to protect against Cross-Site Request Forgery
|
||||
* The security token has to base on at least one value that an attacker can't know - here it's the session ID and the private key.
|
||||
* In this implementation, a security token is reusable (if the user submits a form, goes back and resubmits the form, maybe with small changes;
|
||||
* or if the security token is used for ajax-calls that happen several times), but only valid for a certain amout of time (3hours).
|
||||
* The "typename" seperates the security tokens of different types of forms. This could be relevant in the following case:
|
||||
* A security token is used to protekt a link from CSRF (e.g. the "delete this profile"-link).
|
||||
* or if the security token is used for ajax-calls that happen several times), but only valid for a certain amount of time (3hours).
|
||||
* The "typename" separates the security tokens of different types of forms. This could be relevant in the following case:
|
||||
* A security token is used to protect a link from CSRF (e.g. the "delete this profile"-link).
|
||||
* If the new page contains by any chance external elements, then the used security token is exposed by the referrer.
|
||||
* Actually, important actions should not be triggered by Links / GET-Requests at all, but somethimes they still are,
|
||||
* Actually, important actions should not be triggered by Links / GET-Requests at all, but sometimes they still are,
|
||||
* so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types).
|
||||
*/
|
||||
public static function getFormSecurityToken($typename = '')
|
||||
|
|
@ -108,7 +108,7 @@ abstract class BaseModule
|
|||
$a = DI::app();
|
||||
|
||||
$timestamp = time();
|
||||
$sec_hash = hash('whirlpool', $a->user['guid'] . $a->user['prvkey'] . session_id() . $timestamp . $typename);
|
||||
$sec_hash = hash('whirlpool', ($a->user['guid'] ?? '') . ($a->user['prvkey'] ?? '') . session_id() . $timestamp . $typename);
|
||||
|
||||
return $timestamp . '.' . $sec_hash;
|
||||
}
|
||||
|
|
@ -140,7 +140,7 @@ abstract class BaseModule
|
|||
return false;
|
||||
}
|
||||
|
||||
$sec_hash = hash('whirlpool', $a->user['guid'] . $a->user['prvkey'] . session_id() . $x[0] . $typename);
|
||||
$sec_hash = hash('whirlpool', ($a->user['guid'] ?? '') . ($a->user['prvkey'] ?? '') . session_id() . $x[0] . $typename);
|
||||
|
||||
return ($sec_hash == $x[1]);
|
||||
}
|
||||
|
|
@ -171,4 +171,40 @@ abstract class BaseModule
|
|||
throw new \Friendica\Network\HTTPException\ForbiddenException();
|
||||
}
|
||||
}
|
||||
|
||||
protected static function getContactFilterTabs(string $baseUrl, string $current, bool $displayCommonTab)
|
||||
{
|
||||
$tabs = [
|
||||
[
|
||||
'label' => DI::l10n()->t('All contacts'),
|
||||
'url' => $baseUrl . '/contacts',
|
||||
'sel' => !$current || $current == 'all' ? 'active' : '',
|
||||
],
|
||||
[
|
||||
'label' => DI::l10n()->t('Followers'),
|
||||
'url' => $baseUrl . '/contacts/followers',
|
||||
'sel' => $current == 'followers' ? 'active' : '',
|
||||
],
|
||||
[
|
||||
'label' => DI::l10n()->t('Following'),
|
||||
'url' => $baseUrl . '/contacts/following',
|
||||
'sel' => $current == 'following' ? 'active' : '',
|
||||
],
|
||||
[
|
||||
'label' => DI::l10n()->t('Mutual friends'),
|
||||
'url' => $baseUrl . '/contacts/mutuals',
|
||||
'sel' => $current == 'mutuals' ? 'active' : '',
|
||||
],
|
||||
];
|
||||
|
||||
if ($displayCommonTab) {
|
||||
$tabs[] = [
|
||||
'label' => DI::l10n()->t('Common'),
|
||||
'url' => $baseUrl . '/contacts/common',
|
||||
'sel' => $current == 'common' ? 'active' : '',
|
||||
];
|
||||
}
|
||||
|
||||
return $tabs;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,37 +97,52 @@ abstract class BaseRepository extends BaseFactory
|
|||
* Populates the collection according to the condition. Retrieves a limited subset of models depending on the boundaries
|
||||
* and the limit. The total count of rows matching the condition is stored in the collection.
|
||||
*
|
||||
* max_id and min_id are susceptible to the query order:
|
||||
* - min_id alone only reliably works with ASC order
|
||||
* - max_id alone only reliably works with DESC order
|
||||
* If the wrong order is detected in either case, we inverse the query order and we reverse the model array after the query
|
||||
*
|
||||
* Chainable.
|
||||
*
|
||||
* @param array $condition
|
||||
* @param array $params
|
||||
* @param int? $max_id
|
||||
* @param int? $since_id
|
||||
* @param int? $min_id Retrieve models with an id no fewer than this, as close to it as possible
|
||||
* @param int? $max_id Retrieve models with an id no greater than this, as close to it as possible
|
||||
* @param int $limit
|
||||
* @return BaseCollection
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function selectByBoundaries(array $condition = [], array $params = [], int $max_id = null, int $since_id = null, int $limit = self::LIMIT)
|
||||
public function selectByBoundaries(array $condition = [], array $params = [], int $min_id = null, int $max_id = null, int $limit = self::LIMIT)
|
||||
{
|
||||
$condition = DBA::collapseCondition($condition);
|
||||
$totalCount = DBA::count(static::$table_name, $condition);
|
||||
|
||||
$boundCondition = $condition;
|
||||
|
||||
if (isset($max_id)) {
|
||||
$boundCondition[0] .= " AND `id` < ?";
|
||||
$boundCondition[] = $max_id;
|
||||
$reverseModels = false;
|
||||
|
||||
if (isset($min_id)) {
|
||||
$boundCondition = DBA::mergeConditions($boundCondition, ['`id` > ?', $min_id]);
|
||||
if (!isset($max_id) && isset($params['order']['id']) && ($params['order']['id'] === true || $params['order']['id'] === 'DESC')) {
|
||||
$reverseModels = true;
|
||||
$params['order']['id'] = 'ASC';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($since_id)) {
|
||||
$boundCondition[0] .= " AND `id` > ?";
|
||||
$boundCondition[] = $since_id;
|
||||
if (isset($max_id)) {
|
||||
$boundCondition = DBA::mergeConditions($boundCondition, ['`id` < ?', $max_id]);
|
||||
if (!isset($min_id) && (!isset($params['order']['id']) || $params['order']['id'] === false || $params['order']['id'] === 'ASC')) {
|
||||
$reverseModels = true;
|
||||
$params['order']['id'] = 'DESC';
|
||||
}
|
||||
}
|
||||
|
||||
$params['limit'] = $limit;
|
||||
|
||||
$models = $this->selectModels($boundCondition, $params);
|
||||
|
||||
$totalCount = DBA::count(static::$table_name, $condition);
|
||||
if ($reverseModels) {
|
||||
$models = array_reverse($models);
|
||||
}
|
||||
|
||||
return new static::$collection_class($models, $totalCount);
|
||||
}
|
||||
|
|
@ -221,6 +236,8 @@ abstract class BaseRepository extends BaseFactory
|
|||
}
|
||||
}
|
||||
|
||||
$this->dba->close($result);
|
||||
|
||||
return $models;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,19 +48,25 @@ class DatabaseStructure extends \Asika\SimpleConsole\Console
|
|||
$help = <<<HELP
|
||||
console dbstructure - Performs database updates
|
||||
Usage
|
||||
bin/console dbstructure <command> [-h|--help|-?] |-f|--force] [-v]
|
||||
bin/console dbstructure <command> [options]
|
||||
|
||||
Commands
|
||||
dryrun Show database update schema queries without running them
|
||||
update Update database schema
|
||||
dumpsql Dump database schema
|
||||
toinnodb Convert all tables from MyISAM or InnoDB in the Antelope file format to InnoDB in the Barracuda file format
|
||||
drop Show tables that aren't in use by Friendica anymore and can be dropped
|
||||
-e|--execute Execute the dropping
|
||||
|
||||
Options
|
||||
update Update database schema
|
||||
-f|--force Force the update command (Even if the database structure matches)
|
||||
-o|--override Override running or stalling updates
|
||||
|
||||
dryrun Show database update schema queries without running them
|
||||
dumpsql Dump database schema
|
||||
toinnodb Convert all tables from MyISAM or InnoDB in the Antelope file format to InnoDB in the Barracuda file format
|
||||
initial Set needed initial values in the tables
|
||||
version Set the database to a given number
|
||||
|
||||
General Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
-f|--force Force the update command (Even if the database structure matches)
|
||||
-o|--override Override running or stalling updates
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
|
@ -86,8 +92,10 @@ HELP;
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
if ((count($this->args) > 1) && ($this->getArgument(0) != 'version')) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
} elseif ((count($this->args) != 2) && ($this->getArgument(0) == 'version')) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('This command needs two arguments');
|
||||
}
|
||||
|
||||
if (!$this->dba->isConnected()) {
|
||||
|
|
@ -105,6 +113,12 @@ HELP;
|
|||
$override = $this->getOption(['o', 'override'], false);
|
||||
$output = Update::run($basePath, $force, $override,true, false);
|
||||
break;
|
||||
case "drop":
|
||||
$execute = $this->getOption(['e', 'execute'], false);
|
||||
ob_start();
|
||||
DBStructure::dropTables($execute);
|
||||
$output = ob_get_clean();
|
||||
break;
|
||||
case "dumpsql":
|
||||
ob_start();
|
||||
DBStructure::printStructure($basePath);
|
||||
|
|
@ -115,11 +129,21 @@ HELP;
|
|||
DBStructure::convertToInnoDB();
|
||||
$output = ob_get_clean();
|
||||
break;
|
||||
case "version":
|
||||
ob_start();
|
||||
DBStructure::setDatabaseVersion($this->getArgument(1));
|
||||
$output = ob_get_clean();
|
||||
break;
|
||||
case "initial":
|
||||
ob_start();
|
||||
DBStructure::checkInitialValues(true);
|
||||
$output = ob_get_clean();
|
||||
break;
|
||||
default:
|
||||
$output = 'Unknown command: ' . $this->getArgument(0);
|
||||
}
|
||||
|
||||
$this->out($output);
|
||||
$this->out(trim($output));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
163
src/Console/FixAPDeliveryWorkerTaskParameters.php
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
<?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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Console;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Util\Strings;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* License: AGPLv3 or later, same as Friendica
|
||||
*/
|
||||
class FixAPDeliveryWorkerTaskParameters extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
/**
|
||||
* @var App\Mode
|
||||
*/
|
||||
private $appMode;
|
||||
/**
|
||||
* @var Database
|
||||
*/
|
||||
private $dba;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $examined;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $processed;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $errored;
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console fixapdeliveryworkertaskparameters - fix APDelivery worker task parameters corrupted during the 2020.12 RC period
|
||||
Usage
|
||||
bin/console fixapdeliveryworkertaskparameters [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
During the 2020.12 RC period some worker task parameters have been corrupted, resulting in the impossibility to execute them.
|
||||
This command restores their expected parameters.
|
||||
If you didn't run Friendica during the 2020.12 RC period, you do not need to use this command.
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
public function __construct(App\Mode $appMode, Database $dba, \Friendica\Core\L10n $l10n, array $argv = null)
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
$this->appMode = $appMode;
|
||||
$this->dba = $dba;
|
||||
$this->l10n = $l10n;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) > 0) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
if ($this->appMode->isInstall()) {
|
||||
throw new RuntimeException('Friendica isn\'t properly installed yet.');
|
||||
}
|
||||
|
||||
$this->examined = 0;
|
||||
$this->processed = 0;
|
||||
$this->errored = 0;
|
||||
|
||||
do {
|
||||
$result = $this->dba->p('SELECT `id`, `parameter` FROM `workerqueue` WHERE `command` = "APDelivery" AND `parameter` LIKE "[\"%\",\"\",%" LIMIT ' . $this->examined . ', 100');
|
||||
while ($row = $this->dba->fetch($result)) {
|
||||
$this->examined++;
|
||||
$this->processRow($row);
|
||||
}
|
||||
} while ($this->dba->isResult($result));
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Examined: ' . $this->examined);
|
||||
$this->out('Processed: ' . $this->processed);
|
||||
$this->out('Errored: ' . $this->errored);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function processRow(array $workerqueueItem)
|
||||
{
|
||||
$parameters = json_decode($workerqueueItem['parameter'], true);
|
||||
|
||||
if (!$parameters) {
|
||||
$this->errored++;
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Unabled to parse parameter JSON of the row with id ' . $workerqueueItem['id']);
|
||||
$this->out('JSON: ' . var_export($workerqueueItem['parameter'], true));
|
||||
}
|
||||
}
|
||||
|
||||
if ($parameters[1] !== '' && !is_array($parameters[2])) {
|
||||
// Nothing to do, we save a write
|
||||
return;
|
||||
}
|
||||
|
||||
if ($parameters[1] === '') {
|
||||
$parameters[1] = 0;
|
||||
}
|
||||
|
||||
if (is_array($parameters[2])) {
|
||||
$parameters[4] = $parameters[2];
|
||||
$contact = Contact::getById(current($parameters[2]), ['url']);
|
||||
$parameters[2] = $contact['url'];
|
||||
}
|
||||
|
||||
$fields = ['parameter' => json_encode($parameters)];
|
||||
if ($this->dba->update('workerqueue', $fields, ['id' => $workerqueueItem['id']])) {
|
||||
$this->processed++;
|
||||
} else {
|
||||
$this->errored++;
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Unabled to update the row with id ' . $workerqueueItem['id']);
|
||||
$this->out('Fields: ' . var_export($fields, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ class GlobalCommunitySilence extends \Asika\SimpleConsole\Console
|
|||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console globalcommunitysilence - Silence remote profile from global community page
|
||||
console globalcommunitysilence - Silence a profile from the global community page
|
||||
Usage
|
||||
bin/console globalcommunitysilence <profile_url> [-h|--help|-?] [-v]
|
||||
|
||||
|
|
|
|||
139
src/Console/Relay.php
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
<?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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Console;
|
||||
|
||||
use Asika\SimpleConsole\CommandArgsException;
|
||||
use Friendica\Model\APContact;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Protocol\ActivityPub\Transmitter;
|
||||
|
||||
/**
|
||||
* tool to control the list of ActivityPub relay servers from the CLI
|
||||
*
|
||||
* With this script you can access the relay servers of your node from
|
||||
* the CLI.
|
||||
*/
|
||||
class Relay extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
/**
|
||||
* @var $dba Friendica\Database\Database
|
||||
*/
|
||||
private $dba;
|
||||
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console relay - Manage ActivityPub relay configuration
|
||||
Synopsis
|
||||
bin/console relay list [-h|--help|-?] [-v]
|
||||
bin/console relay add <actor> [-h|--help|-?] [-v]
|
||||
bin/console relay remove <actor> [-f|--force] [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
bin/console relay list
|
||||
Lists all active relay servers
|
||||
|
||||
bin/console relay add <actor>
|
||||
Add a relay actor in the format https://relayserver.tld/actor
|
||||
|
||||
bin/console relay remove <actor>
|
||||
Remove a relay actor in the format https://relayserver.tld/actor
|
||||
|
||||
Options
|
||||
-f|--force Change the relay status in the system even if the unsubscribe message failed
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
public function __construct(\Friendica\Database\Database $dba, array $argv = null)
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
$this->dba = $dba;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Executable: ' . $this->executable);
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) > 2) {
|
||||
throw new CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
if ((count($this->args) == 1) && ($this->getArgument(0) == 'list')) {
|
||||
$contacts = $this->dba->select('apcontact', ['url'],
|
||||
["`type` = ? AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` = ?)",
|
||||
'Application', 0, Contact::FRIEND]);
|
||||
while ($contact = $this->dba->fetch($contacts)) {
|
||||
$this->out($contact['url']);
|
||||
}
|
||||
$this->dba->close($contacts);
|
||||
} elseif (count($this->args) == 0) {
|
||||
throw new CommandArgsException('too few arguments');
|
||||
} elseif (count($this->args) == 1) {
|
||||
throw new CommandArgsException($this->getArgument(0) . ' is no valid command');
|
||||
}
|
||||
|
||||
if (count($this->args) == 2) {
|
||||
$mode = $this->getArgument(0);
|
||||
$actor = $this->getArgument(1);
|
||||
|
||||
$apcontact = APContact::getByURL($actor);
|
||||
if (empty($apcontact) || ($apcontact['type'] != 'Application')) {
|
||||
$this->out($actor . ' is no relay actor');
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($mode == 'add') {
|
||||
if (Transmitter::sendRelayFollow($actor)) {
|
||||
$this->out('Successfully added ' . $actor);
|
||||
} else {
|
||||
$this->out($actor . " couldn't be added");
|
||||
}
|
||||
} elseif ($mode == 'remove') {
|
||||
$force = $this->getOption(['f', 'force'], false);
|
||||
|
||||
if (Transmitter::sendRelayUndoFollow($actor, $force)) {
|
||||
$this->out('Successfully removed ' . $actor);
|
||||
} elseif (!$force) {
|
||||
$this->out($actor . " couldn't be removed");
|
||||
} else {
|
||||
$this->out($actor . " is forcefully removed");
|
||||
}
|
||||
} else {
|
||||
throw new CommandArgsException($mode . ' is no valid command');
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -48,13 +48,17 @@ class ServerBlock extends Console
|
|||
$help = <<<HELP
|
||||
console serverblock - Manage blocked server domain patterns
|
||||
Usage
|
||||
bin/console serverblock [-h|--help|-?] [-v]
|
||||
bin/console serverblock add <pattern> <reason> [-h|--help|-?] [-v]
|
||||
bin/console serverblock remove <pattern> [-h|--help|-?] [-v]
|
||||
bin/console serverblock [-h|--help|-?] [-v]
|
||||
bin/console serverblock add <pattern> <reason> [-h|--help|-?] [-v]
|
||||
bin/console serverblock remove <pattern> [-h|--help|-?] [-v]
|
||||
bin/console serverblock export <filename>
|
||||
bin/console serverblock import <filename>
|
||||
|
||||
Description
|
||||
With this tool, you can list the current blocked server domain patterns
|
||||
With this tool, you can list the current blocked server domain patterns
|
||||
or you can add / remove a blocked server domain pattern from the list.
|
||||
Using the export and import options you can share your server blocklist
|
||||
with other node admins by CSV files.
|
||||
|
||||
Patterns are case-insensitive shell wildcard comprising the following special characters:
|
||||
- * : Any number of characters
|
||||
|
|
@ -87,12 +91,79 @@ HELP;
|
|||
return $this->addBlockedServer($this->config);
|
||||
case 'remove':
|
||||
return $this->removeBlockedServer($this->config);
|
||||
case 'export':
|
||||
return $this->exportBlockedServers($this->config);
|
||||
case 'import':
|
||||
return $this->importBlockedServers($this->config);
|
||||
default:
|
||||
throw new CommandArgsException('Unknown command.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the list of blocked domains including the reason for the
|
||||
* block to a CSV file.
|
||||
*
|
||||
* @param IConfig $config
|
||||
*/
|
||||
private function exportBlockedServers(IConfig $config)
|
||||
{
|
||||
$filename = $this->getArgument(1);
|
||||
$blocklist = $config->get('system', 'blocklist', []);
|
||||
$fp = fopen($filename, 'w');
|
||||
if (!$fp) {
|
||||
throw new Exception(sprintf('The file "%s" could not be created.', $filename));
|
||||
}
|
||||
foreach ($blocklist as $domain) {
|
||||
fputcsv($fp, $domain);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Imports a list of domains and a reason for the block from a CSV
|
||||
* file, e.g. created with the export function.
|
||||
*
|
||||
* @param IConfig $config
|
||||
*/
|
||||
private function importBlockedServers(IConfig $config)
|
||||
{
|
||||
$filename = $this->getArgument(1);
|
||||
$currBlockList = $config->get('system', 'blocklist', []);
|
||||
$newBlockList = [];
|
||||
if (($fp = fopen($filename, 'r')) !== false) {
|
||||
while (($data = fgetcsv($fp, 1000, ',')) !== false) {
|
||||
$domain = $data[0];
|
||||
if (count($data) == 0) {
|
||||
$reason = self::DEFAULT_REASON;
|
||||
} else {
|
||||
$reason = $data[1];
|
||||
}
|
||||
$data = [
|
||||
'domain' => $domain,
|
||||
'reason' => $reason
|
||||
];
|
||||
if (!in_array($data, $newBlockList)) {
|
||||
$newBlockList[] = $data;
|
||||
}
|
||||
}
|
||||
foreach ($currBlockList as $blocked) {
|
||||
if (!in_array($blocked, $newBlockList)) {
|
||||
$newBlockList[] = $blocked;
|
||||
}
|
||||
}
|
||||
if ($config->set('system', 'blocklist', $newBlockList)) {
|
||||
$this->out(sprintf("Entries from %s that were not blocked before are now blocked", $filename));
|
||||
return 0;
|
||||
} else {
|
||||
$this->out(sprintf("Couldn't save '%s' as blocked server", $domain));
|
||||
return 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Exception(sprintf('The file "%s" could not be opened for importing', $filename));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the whole list of blocked domains including the reason
|
||||
*
|
||||
|
|
@ -127,9 +198,9 @@ HELP;
|
|||
|
||||
$update = false;
|
||||
|
||||
$currBlocklist = $config->get('system', 'blocklist', []);
|
||||
$currBlockList = $config->get('system', 'blocklist', []);
|
||||
$newBlockList = [];
|
||||
foreach ($currBlocklist as $blocked) {
|
||||
foreach ($currBlockList as $blocked) {
|
||||
if ($blocked['domain'] === $domain) {
|
||||
$update = true;
|
||||
$newBlockList[] = [
|
||||
|
|
@ -178,9 +249,9 @@ HELP;
|
|||
|
||||
$found = false;
|
||||
|
||||
$currBlocklist = $config->get('system', 'blocklist', []);
|
||||
$currBlockList = $config->get('system', 'blocklist', []);
|
||||
$newBlockList = [];
|
||||
foreach ($currBlocklist as $blocked) {
|
||||
foreach ($currBlockList as $blocked) {
|
||||
if ($blocked['domain'] === $domain) {
|
||||
$found = true;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ HELP;
|
|||
$isregisterd = false;
|
||||
foreach ($this->storageManager->listBackends() as $name => $class) {
|
||||
$issel = ' ';
|
||||
if ($current::getName() == $name) {
|
||||
if ($current && $current::getName() == $name) {
|
||||
$issel = '*';
|
||||
$isregisterd = true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ console user - Modify user settings per console commands.
|
|||
Usage
|
||||
bin/console user password <nickname> [<password>] [-h|--help|-?] [-v]
|
||||
bin/console user add [<name> [<nickname> [<email> [<language>]]]] [-h|--help|-?] [-v]
|
||||
bin/console user delete [<nickname>] [-q] [-h|--help|-?] [-v]
|
||||
bin/console user delete [<nickname>] [-y] [-h|--help|-?] [-v]
|
||||
bin/console user allow [<nickname>] [-h|--help|-?] [-v]
|
||||
bin/console user deny [<nickname>] [-h|--help|-?] [-v]
|
||||
bin/console user block [<nickname>] [-h|--help|-?] [-v]
|
||||
|
|
@ -78,8 +78,8 @@ Description
|
|||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
-q Quiet mode (don't ask for a command).
|
||||
-v Show more debug information
|
||||
-y Non-interactive mode, assume "yes" as answer to the user deletion prompt
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
|
@ -304,19 +304,24 @@ HELP;
|
|||
}
|
||||
}
|
||||
|
||||
$user = $this->dba->selectFirst('user', ['uid'], ['nickname' => $nick]);
|
||||
$user = $this->dba->selectFirst('user', ['uid', 'account_removed'], ['nickname' => $nick]);
|
||||
if (empty($user)) {
|
||||
throw new RuntimeException($this->l10n->t('User not found'));
|
||||
}
|
||||
|
||||
if (!$this->getOption('q')) {
|
||||
if (!empty($user['account_removed'])) {
|
||||
$this->out($this->l10n->t('User has already been marked for deletion.'));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$this->getOption('y')) {
|
||||
$this->out($this->l10n->t('Type "yes" to delete %s', $nick));
|
||||
if (CliPrompt::prompt() !== 'yes') {
|
||||
throw new RuntimeException('Delete abort.');
|
||||
throw new RuntimeException($this->l10n->t('Deletion aborted.'));
|
||||
}
|
||||
}
|
||||
|
||||
return UserModel::remove($user['uid'] ?? -1);
|
||||
return UserModel::remove($user['uid']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -404,7 +409,7 @@ HELP;
|
|||
case 'guid':
|
||||
$user = UserModel::getByGuid($param, $fields);
|
||||
break;
|
||||
case 'email':
|
||||
case 'mail':
|
||||
$user = UserModel::getByEmail($param, $fields);
|
||||
break;
|
||||
case 'nick':
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use Friendica\Util\Network;
|
|||
use Friendica\Util\Strings;
|
||||
|
||||
/**
|
||||
* This pager should be used by lists using the since_id†/max_id† parameters
|
||||
* This pager should be used by lists using the min_id†/max_id† parameters
|
||||
*
|
||||
* This pager automatically identifies if the sorting is done increasingly or decreasingly if the first item id†
|
||||
* and last item id† are different. Otherwise it defaults to decreasingly like reverse chronological lists.
|
||||
|
|
@ -60,9 +60,9 @@ class BoundariesPager extends Pager
|
|||
if (!empty($parsed['query'])) {
|
||||
parse_str($parsed['query'], $queryParameters);
|
||||
|
||||
$this->first_page = !($queryParameters['since_id'] ?? null) && !($queryParameters['max_id'] ?? null);
|
||||
$this->first_page = !($queryParameters['min_id'] ?? null) && !($queryParameters['max_id'] ?? null);
|
||||
|
||||
unset($queryParameters['since_id']);
|
||||
unset($queryParameters['min_id']);
|
||||
unset($queryParameters['max_id']);
|
||||
|
||||
$parsed['query'] = http_build_query($queryParameters);
|
||||
|
|
@ -111,7 +111,7 @@ class BoundariesPager extends Pager
|
|||
'prev' => [
|
||||
'url' => Strings::ensureQueryParameter($this->baseQueryString .
|
||||
($this->first_item_id >= $this->last_item_id ?
|
||||
'&since_id=' . $this->first_item_id : '&max_id=' . $this->first_item_id)
|
||||
'&min_id=' . $this->first_item_id : '&max_id=' . $this->first_item_id)
|
||||
),
|
||||
'text' => $this->l10n->t('newer'),
|
||||
'class' => 'previous' . ($this->first_page ? ' disabled' : '')
|
||||
|
|
@ -119,7 +119,7 @@ class BoundariesPager extends Pager
|
|||
'next' => [
|
||||
'url' => Strings::ensureQueryParameter($this->baseQueryString .
|
||||
($this->first_item_id >= $this->last_item_id ?
|
||||
'&max_id=' . $this->last_item_id : '&since_id=' . $this->last_item_id)
|
||||
'&max_id=' . $this->last_item_id : '&min_id=' . $this->last_item_id)
|
||||
),
|
||||
'text' => $this->l10n->t('older'),
|
||||
'class' => 'next' . ($displayedItemCount < $this->getItemsPerPage() ? ' disabled' : '')
|
||||
|
|
|
|||
|
|
@ -76,14 +76,6 @@ class ContactSelector
|
|||
$server_url = Strings::normaliseLink($contact['baseurl']);
|
||||
}
|
||||
|
||||
if (empty($server_url)) {
|
||||
// Fetch the server url from the gcontact table
|
||||
$gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($profile)]);
|
||||
if (!empty($gcontact) && !empty($gcontact['server_url'])) {
|
||||
$server_url = Strings::normaliseLink($gcontact['server_url']);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($server_url)) {
|
||||
// Create the server url out of the profile url
|
||||
$parts = parse_url($profile);
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ class Feature
|
|||
DI::l10n()->t('General Features'),
|
||||
//array('expire', DI::l10n()->t('Content Expiration'), DI::l10n()->t('Remove old posts/comments after a period of time')),
|
||||
['photo_location', DI::l10n()->t('Photo Location'), DI::l10n()->t("Photo metadata is normally stripped. This extracts the location \x28if present\x29 prior to stripping metadata and links it to a map."), false, DI::config()->get('feature_lock', 'photo_location', false)],
|
||||
['export_calendar', DI::l10n()->t('Export Public Calendar'), DI::l10n()->t('Ability for visitors to download the public calendar'), false, DI::config()->get('feature_lock', 'export_calendar', false)],
|
||||
['trending_tags', DI::l10n()->t('Trending Tags'), DI::l10n()->t('Show a community page widget with a list of the most popular tags in recent public posts.'), false, DI::config()->get('feature_lock', 'trending_tags', false)],
|
||||
],
|
||||
|
||||
|
|
@ -107,20 +106,6 @@ class Feature
|
|||
['explicit_mentions', DI::l10n()->t('Explicit Mentions'), DI::l10n()->t('Add explicit mentions to comment box for manual control over who gets mentioned in replies.'), false, DI::config()->get('feature_lock', 'explicit_mentions', false)],
|
||||
],
|
||||
|
||||
// Network sidebar widgets
|
||||
'widgets' => [
|
||||
DI::l10n()->t('Network Sidebar'),
|
||||
['archives', DI::l10n()->t('Archives'), DI::l10n()->t('Ability to select posts by date ranges'), false, DI::config()->get('feature_lock', 'archives', false)],
|
||||
['networks', DI::l10n()->t('Protocol Filter'), DI::l10n()->t('Enable widget to display Network posts only from selected protocols'), false, DI::config()->get('feature_lock', 'networks', false)],
|
||||
],
|
||||
|
||||
// Network tabs
|
||||
'net_tabs' => [
|
||||
DI::l10n()->t('Network Tabs'),
|
||||
['new_tab', DI::l10n()->t('Network New Tab'), DI::l10n()->t("Enable tab to display only new Network posts \x28from the last 12 hours\x29"), false, DI::config()->get('feature_lock', 'new_tab', false)],
|
||||
['link_tab', DI::l10n()->t('Network Shared Links Tab'), DI::l10n()->t('Enable tab to display only Network posts with links in them'), false, DI::config()->get('feature_lock', 'link_tab', false)],
|
||||
],
|
||||
|
||||
// Item tools
|
||||
'tools' => [
|
||||
DI::l10n()->t('Post/Comment Tools'),
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ use Friendica\Core\Renderer;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
|
||||
/**
|
||||
* This class handles methods related to the forum functionality
|
||||
|
|
@ -72,7 +71,7 @@ class ForumManager
|
|||
|
||||
$forumlist = [];
|
||||
|
||||
$fields = ['id', 'url', 'name', 'micro', 'thumb'];
|
||||
$fields = ['id', 'url', 'name', 'micro', 'thumb', 'avatar'];
|
||||
$condition = [$condition_str, Protocol::DFRN, Protocol::ACTIVITYPUB, $uid];
|
||||
$contacts = DBA::select('contact', $fields, $condition, $params);
|
||||
if (!$contacts) {
|
||||
|
|
@ -100,13 +99,14 @@ class ForumManager
|
|||
* Sidebar widget to show subcribed friendica forums. If activated
|
||||
* in the settings, it appears at the notwork page sidebar
|
||||
*
|
||||
* @param int $uid The ID of the User
|
||||
* @param int $cid The contact id which is used to mark a forum as "selected"
|
||||
* @param string $baseurl Base module path
|
||||
* @param int $uid The ID of the User
|
||||
* @param int $cid The contact id which is used to mark a forum as "selected"
|
||||
* @return string
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function widget($uid, $cid = 0)
|
||||
public static function widget(string $baseurl, int $uid, int $cid = 0)
|
||||
{
|
||||
$o = '';
|
||||
|
||||
|
|
@ -126,12 +126,12 @@ class ForumManager
|
|||
$selected = (($cid == $contact['id']) ? ' forum-selected' : '');
|
||||
|
||||
$entry = [
|
||||
'url' => 'network?cid=' . $contact['id'],
|
||||
'url' => $baseurl . '/' . $contact['id'],
|
||||
'external_url' => Contact::magicLink($contact['url']),
|
||||
'name' => $contact['name'],
|
||||
'cid' => $contact['id'],
|
||||
'selected' => $selected,
|
||||
'micro' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO)),
|
||||
'micro' => DI::baseUrl()->remove(Contact::getMicro($contact)),
|
||||
'id' => ++$id,
|
||||
];
|
||||
$entries[] = $entry;
|
||||
|
|
@ -147,6 +147,7 @@ class ForumManager
|
|||
'$link_desc' => DI::l10n()->t('External link to forum'),
|
||||
'$total' => $total,
|
||||
'$visible_forums' => $visible_forums,
|
||||
'$showless' => DI::l10n()->t('show less'),
|
||||
'$showmore' => DI::l10n()->t('show more')]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,10 @@
|
|||
|
||||
namespace Friendica\Content;
|
||||
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\FileTag;
|
||||
use Friendica\Model\Tag;
|
||||
|
||||
/**
|
||||
* A content helper class for displaying items
|
||||
|
|
@ -100,4 +103,129 @@ class Item
|
|||
|
||||
return [$categories, $folders];
|
||||
}
|
||||
|
||||
/**
|
||||
* This function removes the tag $tag from the text $body and replaces it with
|
||||
* the appropriate link.
|
||||
*
|
||||
* @param string $body the text to replace the tag in
|
||||
* @param string $inform a comma-seperated string containing everybody to inform
|
||||
* @param integer $profile_uid the user id to replace the tag for (0 = anyone)
|
||||
* @param string $tag the tag to replace
|
||||
* @param string $network The network of the post
|
||||
*
|
||||
* @return array|bool ['replaced' => $replaced, 'contact' => $contact];
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function replaceTag(&$body, &$inform, $profile_uid, $tag, $network = '')
|
||||
{
|
||||
$replaced = false;
|
||||
|
||||
//is it a person tag?
|
||||
if (Tag::isType($tag, Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION)) {
|
||||
$tag_type = substr($tag, 0, 1);
|
||||
//is it already replaced?
|
||||
if (strpos($tag, '[url=')) {
|
||||
// Checking for the alias that is used for OStatus
|
||||
$pattern = '/[@!]\[url\=(.*?)\](.*?)\[\/url\]/ism';
|
||||
if (preg_match($pattern, $tag, $matches)) {
|
||||
$data = Contact::getByURL($matches[1], false, ['alias', 'nick']);
|
||||
|
||||
if ($data['alias'] != '') {
|
||||
$newtag = '@[url=' . $data['alias'] . ']' . $data['nick'] . '[/url]';
|
||||
}
|
||||
}
|
||||
|
||||
return $replaced;
|
||||
}
|
||||
|
||||
//get the person's name
|
||||
$name = substr($tag, 1);
|
||||
|
||||
// Sometimes the tag detection doesn't seem to work right
|
||||
// This is some workaround
|
||||
$nameparts = explode(' ', $name);
|
||||
$name = $nameparts[0];
|
||||
|
||||
// Try to detect the contact in various ways
|
||||
if (strpos($name, 'http://') || strpos($name, '@')) {
|
||||
$contact = Contact::getByURLForUser($name, $profile_uid);
|
||||
} else {
|
||||
$contact = false;
|
||||
$fields = ['id', 'url', 'nick', 'name', 'alias', 'network', 'forum', 'prv'];
|
||||
|
||||
if (strrpos($name, '+')) {
|
||||
// Is it in format @nick+number?
|
||||
$tagcid = intval(substr($name, strrpos($name, '+') + 1));
|
||||
$contact = DBA::selectFirst('contact', $fields, ['id' => $tagcid, 'uid' => $profile_uid]);
|
||||
}
|
||||
|
||||
// select someone by nick in the current network
|
||||
if (!DBA::isResult($contact) && ($network != '')) {
|
||||
$condition = ["`nick` = ? AND `network` = ? AND `uid` = ?",
|
||||
$name, $network, $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
|
||||
// select someone by attag in the current network
|
||||
if (!DBA::isResult($contact) && ($network != '')) {
|
||||
$condition = ["`attag` = ? AND `network` = ? AND `uid` = ?",
|
||||
$name, $network, $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
|
||||
//select someone by name in the current network
|
||||
if (!DBA::isResult($contact) && ($network != '')) {
|
||||
$condition = ['name' => $name, 'network' => $network, 'uid' => $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
|
||||
// select someone by nick in any network
|
||||
if (!DBA::isResult($contact)) {
|
||||
$condition = ["`nick` = ? AND `uid` = ?", $name, $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
|
||||
// select someone by attag in any network
|
||||
if (!DBA::isResult($contact)) {
|
||||
$condition = ["`attag` = ? AND `uid` = ?", $name, $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
|
||||
// select someone by name in any network
|
||||
if (!DBA::isResult($contact)) {
|
||||
$condition = ['name' => $name, 'uid' => $profile_uid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if $contact has been successfully loaded
|
||||
if (DBA::isResult($contact)) {
|
||||
if (strlen($inform) && (isset($contact['notify']) || isset($contact['id']))) {
|
||||
$inform .= ',';
|
||||
}
|
||||
|
||||
if (isset($contact['id'])) {
|
||||
$inform .= 'cid:' . $contact['id'];
|
||||
} elseif (isset($contact['notify'])) {
|
||||
$inform .= $contact['notify'];
|
||||
}
|
||||
|
||||
$profile = $contact['url'];
|
||||
$newname = ($contact['name'] ?? '') ?: $contact['nick'];
|
||||
}
|
||||
|
||||
//if there is an url for this persons profile
|
||||
if (isset($profile) && ($newname != '')) {
|
||||
$replaced = true;
|
||||
// create profile link
|
||||
$profile = str_replace(',', '%2c', $profile);
|
||||
$newtag = $tag_type.'[url=' . $profile . ']' . $newname . '[/url]';
|
||||
$body = str_replace($tag_type . $name, $newtag, $body);
|
||||
}
|
||||
}
|
||||
|
||||
return ['replaced' => $replaced, 'contact' => $contact];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use Friendica\Core\Renderer;
|
|||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\User;
|
||||
|
||||
|
|
@ -171,20 +172,24 @@ class Nav
|
|||
}
|
||||
|
||||
if (local_user()) {
|
||||
// user menu
|
||||
$nav['usermenu'][] = ['profile/' . $a->user['nickname'], DI::l10n()->t('Status'), '', DI::l10n()->t('Your posts and conversations')];
|
||||
$nav['usermenu'][] = ['profile/' . $a->user['nickname'] . '/profile', DI::l10n()->t('Profile'), '', DI::l10n()->t('Your profile page')];
|
||||
$nav['usermenu'][] = ['photos/' . $a->user['nickname'], DI::l10n()->t('Photos'), '', DI::l10n()->t('Your photos')];
|
||||
$nav['usermenu'][] = ['videos/' . $a->user['nickname'], DI::l10n()->t('Videos'), '', DI::l10n()->t('Your videos')];
|
||||
$nav['usermenu'][] = ['events/', DI::l10n()->t('Events'), '', DI::l10n()->t('Your events')];
|
||||
$nav['usermenu'][] = ['notes/', DI::l10n()->t('Personal notes'), '', DI::l10n()->t('Your personal notes')];
|
||||
if (!empty($a->user)) {
|
||||
// user menu
|
||||
$nav['usermenu'][] = ['profile/' . $a->user['nickname'], DI::l10n()->t('Status'), '', DI::l10n()->t('Your posts and conversations')];
|
||||
$nav['usermenu'][] = ['profile/' . $a->user['nickname'] . '/profile', DI::l10n()->t('Profile'), '', DI::l10n()->t('Your profile page')];
|
||||
$nav['usermenu'][] = ['photos/' . $a->user['nickname'], DI::l10n()->t('Photos'), '', DI::l10n()->t('Your photos')];
|
||||
$nav['usermenu'][] = ['videos/' . $a->user['nickname'], DI::l10n()->t('Videos'), '', DI::l10n()->t('Your videos')];
|
||||
$nav['usermenu'][] = ['events/', DI::l10n()->t('Events'), '', DI::l10n()->t('Your events')];
|
||||
$nav['usermenu'][] = ['notes/', DI::l10n()->t('Personal notes'), '', DI::l10n()->t('Your personal notes')];
|
||||
|
||||
// user info
|
||||
$contact = DBA::selectFirst('contact', ['micro'], ['uid' => $a->user['uid'], 'self' => true]);
|
||||
$userinfo = [
|
||||
'icon' => (DBA::isResult($contact) ? DI::baseUrl()->remove($contact['micro']) : 'images/person-48.jpg'),
|
||||
'name' => $a->user['username'],
|
||||
];
|
||||
// user info
|
||||
$contact = DBA::selectFirst('contact', ['micro'], ['uid' => $a->user['uid'], 'self' => true]);
|
||||
$userinfo = [
|
||||
'icon' => (DBA::isResult($contact) ? DI::baseUrl()->remove($contact['micro']) : Contact::DEFAULT_AVATAR_MICRO),
|
||||
'name' => $a->user['username'],
|
||||
];
|
||||
} else {
|
||||
DI::logger()->warning('Empty $a->user for local user', ['local_user' => local_user(), '$a' => $a]);
|
||||
}
|
||||
}
|
||||
|
||||
// "Home" should also take you home from an authenticated remote profile connection
|
||||
|
|
@ -252,7 +257,7 @@ class Nav
|
|||
}
|
||||
|
||||
// The following nav links are only show to logged in users
|
||||
if (local_user()) {
|
||||
if (local_user() && !empty($a->user)) {
|
||||
$nav['network'] = ['network', DI::l10n()->t('Network'), '', DI::l10n()->t('Conversations from your friends')];
|
||||
|
||||
$nav['home'] = ['profile/' . $a->user['nickname'], DI::l10n()->t('Home'), '', DI::l10n()->t('Your posts and conversations')];
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ use Exception;
|
|||
use Friendica\Core\Cache\Duration;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
|
|
@ -95,7 +96,7 @@ class OEmbed
|
|||
|
||||
if (!in_array($ext, $noexts)) {
|
||||
// try oembed autodiscovery
|
||||
$html_text = Network::fetchUrl($embedurl, false, 15, 'text/*');
|
||||
$html_text = DI::httpRequest()->fetch($embedurl, 15, 'text/*');
|
||||
if ($html_text) {
|
||||
$dom = @DOMDocument::loadHTML($html_text);
|
||||
if ($dom) {
|
||||
|
|
@ -103,14 +104,14 @@ class OEmbed
|
|||
$entries = $xpath->query("//link[@type='application/json+oembed']");
|
||||
foreach ($entries as $e) {
|
||||
$href = $e->getAttributeNode('href')->nodeValue;
|
||||
$json_string = Network::fetchUrl($href . '&maxwidth=' . $a->videowidth);
|
||||
$json_string = DI::httpRequest()->fetch($href . '&maxwidth=' . $a->videowidth);
|
||||
break;
|
||||
}
|
||||
|
||||
$entries = $xpath->query("//link[@type='text/json+oembed']");
|
||||
foreach ($entries as $e) {
|
||||
$href = $e->getAttributeNode('href')->nodeValue;
|
||||
$json_string = Network::fetchUrl($href . '&maxwidth=' . $a->videowidth);
|
||||
$json_string = DI::httpRequest()->fetch($href . '&maxwidth=' . $a->videowidth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -131,7 +132,7 @@ class OEmbed
|
|||
'maxwidth' => $a->videowidth,
|
||||
'content' => $json_string,
|
||||
'created' => DateTimeFormat::utcNow()
|
||||
], true);
|
||||
], Database::INSERT_UPDATE);
|
||||
$cache_ttl = Duration::DAY;
|
||||
} else {
|
||||
$cache_ttl = Duration::FIVE_MINUTES;
|
||||
|
|
|
|||
310
src/Content/PageInfo.php
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
<?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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Content;
|
||||
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\DI;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\ParseUrl;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
/**
|
||||
* Extracts trailing URLs from post bodies to transform them in enriched attachment tags through Site Info query
|
||||
*/
|
||||
class PageInfo
|
||||
{
|
||||
/**
|
||||
* @param string $body
|
||||
* @param bool $searchNakedUrls
|
||||
* @param bool $no_photos
|
||||
* @return string
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function searchAndAppendToBody(string $body, bool $searchNakedUrls = false, bool $no_photos = false)
|
||||
{
|
||||
Logger::info('add_page_info_to_body: fetch page info for body', ['body' => $body]);
|
||||
|
||||
$url = self::getRelevantUrlFromBody($body, $searchNakedUrls);
|
||||
if (!$url) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
$data = self::queryUrl($url);
|
||||
if (!$data) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
return self::appendDataToBody($body, $data, $no_photos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @param array $data
|
||||
* @param bool $no_photos
|
||||
* @return string
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function appendDataToBody(string $body, array $data, bool $no_photos = false)
|
||||
{
|
||||
// Only one [attachment] tag per body is allowed
|
||||
$existingAttachmentPos = strpos($body, '[attachment');
|
||||
if ($existingAttachmentPos !== false) {
|
||||
$linkTitle = $data['title'] ?: $data['url'];
|
||||
// Additional link attachments are prepended before the existing [attachment] tag
|
||||
$body = substr_replace($body, "\n[bookmark=" . $data['url'] . ']' . $linkTitle . "[/bookmark]\n", $existingAttachmentPos, 0);
|
||||
} else {
|
||||
$footer = PageInfo::getFooterFromData($data, $no_photos);
|
||||
$body = self::stripTrailingUrlFromBody($body, $data['url']);
|
||||
$body .= "\n" . $footer;
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param bool $no_photos
|
||||
* @param string $photo
|
||||
* @param bool $keywords
|
||||
* @param string $keyword_denylist
|
||||
* @return string
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function getFooterFromUrl(string $url, bool $no_photos = false, string $photo = '', bool $keywords = false, string $keyword_denylist = '')
|
||||
{
|
||||
$data = self::queryUrl($url, $photo, $keywords, $keyword_denylist);
|
||||
|
||||
return self::getFooterFromData($data, $no_photos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param bool $no_photos
|
||||
* @return string
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function getFooterFromData(array $data, bool $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['url'])) {
|
||||
$text .= " url='" . $data['url'] . "'";
|
||||
}
|
||||
|
||||
if (!empty($data['title'])) {
|
||||
$text .= " title='" . $data['title'] . "'";
|
||||
}
|
||||
|
||||
if (empty($data['text'])) {
|
||||
$data['text'] = '';
|
||||
}
|
||||
|
||||
// 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 . "'";
|
||||
|
||||
if (empty($data['text'])) {
|
||||
$data['text'] = $data['title'];
|
||||
}
|
||||
|
||||
if (empty($data['text'])) {
|
||||
$data['text'] = $data['url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$text .= ']' . $data['text'] . '[/attachment]';
|
||||
|
||||
$hashtags = '';
|
||||
if (!empty($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 $text . $hashtags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $photo
|
||||
* @param bool $keywords
|
||||
* @param string $keyword_denylist
|
||||
* @return array|bool
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function queryUrl(string $url, string $photo = '', bool $keywords = false, string $keyword_denylist = '')
|
||||
{
|
||||
$data = ParseUrl::getSiteinfoCached($url, true);
|
||||
|
||||
if ($photo != '') {
|
||||
$data['images'][0]['src'] = $photo;
|
||||
}
|
||||
|
||||
if (!$keywords) {
|
||||
unset($data['keywords']);
|
||||
} elseif ($keyword_denylist && !empty($data['keywords'])) {
|
||||
$list = explode(', ', $keyword_denylist);
|
||||
|
||||
foreach ($list as $keyword) {
|
||||
$keyword = trim($keyword);
|
||||
|
||||
$index = array_search($keyword, $data['keywords']);
|
||||
if ($index !== false) {
|
||||
unset($data['keywords'][$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::info('fetch page info for URL', ['url' => $url, 'data' => $data]);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $photo
|
||||
* @param string $keyword_denylist
|
||||
* @return array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function getTagsFromUrl(string $url, string $photo = '', string $keyword_denylist = '')
|
||||
{
|
||||
$data = self::queryUrl($url, $photo, true, $keyword_denylist);
|
||||
|
||||
if (empty($data['keywords'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$taglist = [];
|
||||
foreach ($data['keywords'] as $keyword) {
|
||||
$hashtag = str_replace([' ', '+', '/', '.', '#', "'"],
|
||||
['', '', '', '', '', ''], $keyword);
|
||||
|
||||
$taglist[] = $hashtag;
|
||||
}
|
||||
|
||||
return $taglist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a non-hashtag, non-mention, schemeful URL at the end of the provided body string to be converted into Page Info.
|
||||
*
|
||||
* @param string $body
|
||||
* @param bool $searchNakedUrls Whether we should pick a naked URL (outside of BBCode tags) as a last resort
|
||||
* @return string|null
|
||||
*/
|
||||
protected static function getRelevantUrlFromBody(string $body, bool $searchNakedUrls = false)
|
||||
{
|
||||
$URLSearchString = 'https?://[^\[\]]*';
|
||||
|
||||
// Fix for Mastodon where the mentions are in a different format
|
||||
$body = preg_replace("~\[url=($URLSearchString)]([#!@])(.*?)\[/url]~is", '$2[url=$1]$3[/url]', $body);
|
||||
|
||||
preg_match("~(?<![!#@])\[url]($URLSearchString)\[/url]$~is", $body, $matches);
|
||||
|
||||
if (!$matches) {
|
||||
preg_match("~(?<![!#@])\[url=($URLSearchString)].*\[/url]$~is", $body, $matches);
|
||||
}
|
||||
|
||||
if (!$matches && $searchNakedUrls) {
|
||||
preg_match(Strings::autoLinkRegEx(), $body, $matches);
|
||||
if ($matches && !Strings::endsWith($body, $matches[1])) {
|
||||
unset($matches);
|
||||
}
|
||||
}
|
||||
|
||||
return $matches[1] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the provided URL from the body if it is at the end of it.
|
||||
* Keep the link label if it isn't the full URL or a shortened version of it.
|
||||
*
|
||||
* @param string $body
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
protected static function stripTrailingUrlFromBody(string $body, string $url)
|
||||
{
|
||||
$quotedUrl = preg_quote($url, '#');
|
||||
$body = preg_replace_callback("#(?:
|
||||
\[url]$quotedUrl\[/url]|
|
||||
\[url=$quotedUrl]$quotedUrl\[/url]|
|
||||
\[url=$quotedUrl]([^[]*?)\[/url]|
|
||||
$quotedUrl
|
||||
)$#isx", function ($match) use ($url) {
|
||||
// Stripping URLs with no label
|
||||
if (empty($match[1])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Stripping link labels that include a shortened version of the URL
|
||||
if (strpos($url, trim($match[1], '.…')) !== false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Keep all other labels
|
||||
return $match[1];
|
||||
}, $body);
|
||||
|
||||
return rtrim($body);
|
||||
}
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ class Pager
|
|||
/**
|
||||
* Sets the base query string from a full query string.
|
||||
*
|
||||
* Strips the 'page' parameter, and remove the 'q=' string for some reason.
|
||||
* Strips the 'page' parameter
|
||||
*
|
||||
* @param string $queryString
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ namespace Friendica\Content\Text;
|
|||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
use Exception;
|
||||
use Friendica\Content\ContactSelector;
|
||||
use Friendica\Content\Item;
|
||||
use Friendica\Content\OEmbed;
|
||||
use Friendica\Content\Smilies;
|
||||
use Friendica\Core\Hook;
|
||||
|
|
@ -35,12 +37,11 @@ use Friendica\DI;
|
|||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Event;
|
||||
use Friendica\Model\Photo;
|
||||
use Friendica\Network\Probe;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Object\Image;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Util\Images;
|
||||
use Friendica\Util\Map;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\ParseUrl;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Util\Strings;
|
||||
|
|
@ -48,6 +49,18 @@ use Friendica\Util\XML;
|
|||
|
||||
class BBCode
|
||||
{
|
||||
// Update this value to the current date whenever changes are made to BBCode::convert
|
||||
const VERSION = '2020-12-18-video-embeds';
|
||||
|
||||
const INTERNAL = 0;
|
||||
const API = 2;
|
||||
const DIASPORA = 3;
|
||||
const CONNECTORS = 4;
|
||||
const OSTATUS = 7;
|
||||
const TWITTER = 8;
|
||||
const BACKLINK = 8;
|
||||
const ACTIVITYPUB = 9;
|
||||
|
||||
/**
|
||||
* Fetches attachment data that were generated the old way
|
||||
*
|
||||
|
|
@ -136,13 +149,15 @@ class BBCode
|
|||
public static function getAttachmentData($body)
|
||||
{
|
||||
$data = [
|
||||
'type' => '',
|
||||
'text' => '',
|
||||
'after' => '',
|
||||
'image' => null,
|
||||
'url' => '',
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
'type' => '',
|
||||
'text' => '',
|
||||
'after' => '',
|
||||
'image' => null,
|
||||
'url' => '',
|
||||
'provider_name' => '',
|
||||
'provider_url' => '',
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
];
|
||||
|
||||
if (!preg_match("/(.*)\[attachment(.*?)\](.*?)\[\/attachment\](.*)/ism", $body, $match)) {
|
||||
|
|
@ -243,6 +258,16 @@ class BBCode
|
|||
|
||||
$data['after'] = trim($match[4]);
|
||||
|
||||
$parts = parse_url($data['url']);
|
||||
if (!empty($parts['scheme']) && !empty($parts['host'])) {
|
||||
$data['provider_name'] = $parts['host'];
|
||||
$data['provider_url'] = $parts['scheme'] . '://' . $parts['host'];
|
||||
|
||||
if (!empty($parts['port'])) {
|
||||
$data['provider_url'] .= ':' . $parts['port'];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
|
@ -434,15 +459,15 @@ class BBCode
|
|||
*/
|
||||
public static function toPlaintext($text, $keep_urls = true)
|
||||
{
|
||||
$naked_text = HTML::toPlaintext(BBCode::convert($text, false, 0, true), 0, !$keep_urls);
|
||||
$naked_text = HTML::toPlaintext(self::convert($text, false, 0, true), 0, !$keep_urls);
|
||||
|
||||
return $naked_text;
|
||||
}
|
||||
|
||||
private static function proxyUrl($image, $simplehtml = false)
|
||||
private static function proxyUrl($image, $simplehtml = self::INTERNAL)
|
||||
{
|
||||
// Only send proxied pictures to API and for internal display
|
||||
if (in_array($simplehtml, [false, 2])) {
|
||||
if (in_array($simplehtml, [self::INTERNAL, self::API])) {
|
||||
return ProxyUtils::proxifyUrl($image);
|
||||
} else {
|
||||
return $image;
|
||||
|
|
@ -475,7 +500,7 @@ class BBCode
|
|||
continue;
|
||||
}
|
||||
|
||||
$curlResult = Network::curl($mtch[1], true);
|
||||
$curlResult = DI::httpRequest()->get($mtch[1]);
|
||||
if (!$curlResult->isSuccess()) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -494,14 +519,14 @@ class BBCode
|
|||
$Image->scaleDown(640);
|
||||
$new_width = $Image->getWidth();
|
||||
$new_height = $Image->getHeight();
|
||||
Logger::log('scale_external_images: ' . $orig_width . '->' . $new_width . 'w ' . $orig_height . '->' . $new_height . 'h' . ' match: ' . $mtch[0], Logger::DEBUG);
|
||||
Logger::info('External images scaled', ['orig_width' => $orig_width, 'new_width' => $new_width, 'orig_height' => $orig_height, 'new_height' => $new_height, 'match' => $mtch[0]]);
|
||||
$s = str_replace(
|
||||
$mtch[0],
|
||||
'[img=' . $new_width . 'x' . $new_height. ']' . $mtch[1] . '[/img]'
|
||||
. "\n",
|
||||
$s
|
||||
);
|
||||
Logger::log('scale_external_images: new string: ' . $s, Logger::DEBUG);
|
||||
Logger::info('New string', ['image' => $s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -529,7 +554,7 @@ class BBCode
|
|||
// than the maximum, then don't waste time looking for the images
|
||||
if ($maxlen && (strlen($body) > $maxlen)) {
|
||||
|
||||
Logger::log('the total body length exceeds the limit', Logger::DEBUG);
|
||||
Logger::info('the total body length exceeds the limit', ['maxlen' => $maxlen, 'body_len' => strlen($body)]);
|
||||
|
||||
$orig_body = $body;
|
||||
$new_body = '';
|
||||
|
|
@ -549,7 +574,7 @@ class BBCode
|
|||
|
||||
if (($textlen + $img_start) > $maxlen) {
|
||||
if ($textlen < $maxlen) {
|
||||
Logger::log('the limit happens before an embedded image', Logger::DEBUG);
|
||||
Logger::info('the limit happens before an embedded image');
|
||||
$new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
|
||||
$textlen = $maxlen;
|
||||
}
|
||||
|
|
@ -563,7 +588,7 @@ class BBCode
|
|||
|
||||
if (($textlen + $img_end) > $maxlen) {
|
||||
if ($textlen < $maxlen) {
|
||||
Logger::log('the limit happens before the end of a non-embedded image', Logger::DEBUG);
|
||||
Logger::info('the limit happens before the end of a non-embedded image');
|
||||
$new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
|
||||
$textlen = $maxlen;
|
||||
}
|
||||
|
|
@ -586,11 +611,11 @@ class BBCode
|
|||
|
||||
if (($textlen + strlen($orig_body)) > $maxlen) {
|
||||
if ($textlen < $maxlen) {
|
||||
Logger::log('the limit happens after the end of the last image', Logger::DEBUG);
|
||||
Logger::info('the limit happens after the end of the last image');
|
||||
$new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
|
||||
}
|
||||
} else {
|
||||
Logger::log('the text size with embedded images extracted did not violate the limit', Logger::DEBUG);
|
||||
Logger::info('the text size with embedded images extracted did not violate the limit');
|
||||
$new_body = $new_body . $orig_body;
|
||||
}
|
||||
|
||||
|
|
@ -605,13 +630,13 @@ class BBCode
|
|||
*
|
||||
* Note: Can produce a [bookmark] tag in the returned string
|
||||
*
|
||||
* @param string $text
|
||||
* @param bool|int $simplehtml
|
||||
* @param bool $tryoembed
|
||||
* @param string $text
|
||||
* @param integer $simplehtml
|
||||
* @param bool $tryoembed
|
||||
* @return string
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
private static function convertAttachment($text, $simplehtml = false, $tryoembed = true)
|
||||
private static function convertAttachment($text, $simplehtml = self::INTERNAL, $tryoembed = true)
|
||||
{
|
||||
$data = self::getAttachmentData($text);
|
||||
if (empty($data) || empty($data['url'])) {
|
||||
|
|
@ -640,7 +665,7 @@ class BBCode
|
|||
} catch (Exception $e) {
|
||||
$data['title'] = ($data['title'] ?? '') ?: $data['url'];
|
||||
|
||||
if ($simplehtml != 4) {
|
||||
if ($simplehtml != self::CONNECTORS) {
|
||||
$return = sprintf('<div class="type-%s">', $data['type']);
|
||||
}
|
||||
|
||||
|
|
@ -649,9 +674,9 @@ class BBCode
|
|||
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-image" /></a>', $data['url'], self::proxyUrl($data['image'], $simplehtml), $data['title']);
|
||||
} else {
|
||||
if (!empty($data['image'])) {
|
||||
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-image" /></a><br />', $data['url'], self::proxyUrl($data['image'], $simplehtml), $data['title']);
|
||||
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-image" /></a><br>', $data['url'], self::proxyUrl($data['image'], $simplehtml), $data['title']);
|
||||
} elseif (!empty($data['preview'])) {
|
||||
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-preview" /></a><br />', $data['url'], self::proxyUrl($data['preview'], $simplehtml), $data['title']);
|
||||
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-preview" /></a><br>', $data['url'], self::proxyUrl($data['preview'], $simplehtml), $data['title']);
|
||||
}
|
||||
$return .= sprintf('<h4><a href="%s">%s</a></h4>', $data['url'], $data['title']);
|
||||
}
|
||||
|
|
@ -667,7 +692,7 @@ class BBCode
|
|||
$return .= sprintf('<sup><a href="%s">%s</a></sup>', $data['url'], parse_url($data['url'], PHP_URL_HOST));
|
||||
}
|
||||
|
||||
if ($simplehtml != 4) {
|
||||
if ($simplehtml != self::CONNECTORS) {
|
||||
$return .= '</div>';
|
||||
}
|
||||
}
|
||||
|
|
@ -966,27 +991,12 @@ class BBCode
|
|||
function ($match) use ($callback) {
|
||||
$attribute_string = $match[2];
|
||||
$attributes = [];
|
||||
foreach (['author', 'profile', 'avatar', 'link', 'posted'] as $field) {
|
||||
foreach (['author', 'profile', 'avatar', 'link', 'posted', 'guid'] as $field) {
|
||||
preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches);
|
||||
$attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
// We only call this so that a previously unknown contact can be added.
|
||||
// This is important for the function "Model\Contact::getDetailsByURL()".
|
||||
// This function then can fetch an entry from the contact table.
|
||||
$default['url'] = $attributes['profile'];
|
||||
|
||||
if (!empty($attributes['author'])) {
|
||||
$default['name'] = $attributes['author'];
|
||||
}
|
||||
|
||||
if (!empty($attributes['avatar'])) {
|
||||
$default['photo'] = $attributes['avatar'];
|
||||
}
|
||||
|
||||
Contact::getIdForURL($attributes['profile'], 0, true, $default);
|
||||
|
||||
$author_contact = Contact::getDetailsByURL($attributes['profile']);
|
||||
$author_contact = Contact::getByURL($attributes['profile'], false, ['url', 'addr', 'name', 'micro']);
|
||||
$author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']);
|
||||
$author_contact['addr'] = ($author_contact['addr'] ?? '') ?: Protocol::getAddrFromProfileUrl($attributes['profile']);
|
||||
|
||||
|
|
@ -998,7 +1008,9 @@ class BBCode
|
|||
$attributes['avatar'] = ProxyUtils::proxifyUrl($attributes['avatar'], false, ProxyUtils::SIZE_THUMB);
|
||||
}
|
||||
|
||||
return $match[1] . $callback($attributes, $author_contact, $match[3], trim($match[1]) != '');
|
||||
$content = preg_replace(Strings::autoLinkRegEx(), '<a href="$1">$1</a>', $match[3]);
|
||||
|
||||
return $match[1] . $callback($attributes, $author_contact, $content, trim($match[1]) != '');
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
|
@ -1025,13 +1037,10 @@ class BBCode
|
|||
$mention = Protocol::formatMention($attributes['profile'], $attributes['author']);
|
||||
|
||||
switch ($simplehtml) {
|
||||
case 1:
|
||||
$text = ($is_quote_share? '<br />' : '') . '<p>' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' <a href="' . $attributes['profile'] . '">' . $mention . '</a>: </p>' . "\n" . '«' . $content . '»';
|
||||
case self::API:
|
||||
$text = ($is_quote_share? '<br>' : '') . '<p>' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . ': </p>' . "\n" . $content;
|
||||
break;
|
||||
case 2:
|
||||
$text = ($is_quote_share? '<br />' : '') . '<p>' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . ': </p>' . "\n" . $content;
|
||||
break;
|
||||
case 3: // Diaspora
|
||||
case self::DIASPORA:
|
||||
if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0) {
|
||||
$text = ($is_quote_share? '<hr />' : '') . '<p><a href="' . $attributes['link'] . '">' . $attributes['link'] . '</a></p>' . "\n";
|
||||
} else {
|
||||
|
|
@ -1049,7 +1058,7 @@ class BBCode
|
|||
}
|
||||
|
||||
break;
|
||||
case 4:
|
||||
case self::CONNECTORS:
|
||||
$headline = '<p><b>' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8');
|
||||
$headline .= DI::l10n()->t('<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a> %3$s', $attributes['link'], $mention, $attributes['posted']);
|
||||
$headline .= ':</b></p>' . "\n";
|
||||
|
|
@ -1057,37 +1066,32 @@ class BBCode
|
|||
$text = ($is_quote_share? '<hr />' : '') . $headline . '<blockquote class="shared_content">' . trim($content) . '</blockquote>' . "\n";
|
||||
|
||||
break;
|
||||
case 5:
|
||||
$text = ($is_quote_share? '<br />' : '') . '<p>' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . ': </p>' . "\n" . $content;
|
||||
case self::OSTATUS:
|
||||
$text = ($is_quote_share? '<br>' : '') . '<p>' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' @' . $author_contact['addr'] . ': ' . $content . '</p>' . "\n";
|
||||
break;
|
||||
case 7: // statusnet/GNU Social
|
||||
$text = ($is_quote_share? '<br />' : '') . '<p>' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' @' . $author_contact['addr'] . ': ' . $content . '</p>' . "\n";
|
||||
break;
|
||||
case 9: // ActivityPub
|
||||
case self::ACTIVITYPUB:
|
||||
$author = '@<span class="vcard"><a href="' . $author_contact['url'] . '" class="url u-url mention" title="' . $author_contact['addr'] . '"><span class="fn nickname mention">' . $author_contact['addr'] . '</span></a>:</span>';
|
||||
$text = '<div><a href="' . $attributes['link'] . '">' . html_entity_decode('♲', ENT_QUOTES, 'UTF-8') . '</a> ' . $author . '<blockquote>' . $content . '</blockquote></div>' . "\n";
|
||||
break;
|
||||
default:
|
||||
// Transforms quoted tweets in rich attachments to avoid nested tweets
|
||||
if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0 && OEmbed::isAllowedURL($attributes['link'])) {
|
||||
try {
|
||||
$text = ($is_quote_share? '<br />' : '') . OEmbed::getHTML($attributes['link']);
|
||||
} catch (Exception $e) {
|
||||
$text = ($is_quote_share? '<br />' : '') . sprintf('[bookmark=%s]%s[/bookmark]', $attributes['link'], $content);
|
||||
}
|
||||
} else {
|
||||
$text = ($is_quote_share? "\n" : '');
|
||||
$text = ($is_quote_share? "\n" : '');
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('shared_content.tpl');
|
||||
$text .= Renderer::replaceMacros($tpl, [
|
||||
'$profile' => $attributes['profile'],
|
||||
'$avatar' => $attributes['avatar'],
|
||||
'$author' => $attributes['author'],
|
||||
'$link' => $attributes['link'],
|
||||
'$posted' => $attributes['posted'],
|
||||
'$content' => trim($content)
|
||||
]);
|
||||
}
|
||||
$contact = Contact::getByURL($attributes['profile'], false, ['network']);
|
||||
$network = $contact['network'] ?? Protocol::PHANTOM;
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('shared_content.tpl');
|
||||
$text .= Renderer::replaceMacros($tpl, [
|
||||
'$profile' => $attributes['profile'],
|
||||
'$avatar' => $attributes['avatar'],
|
||||
'$author' => $attributes['author'],
|
||||
'$link' => $attributes['link'],
|
||||
'$link_title' => DI::l10n()->t('link to source'),
|
||||
'$posted' => $attributes['posted'],
|
||||
'$guid' => $attributes['guid'],
|
||||
'$network_name' => ContactSelector::networkToName($network, $attributes['profile']),
|
||||
'$network_icon' => ContactSelector::networkToIcon($network, $attributes['profile']),
|
||||
'$content' => self::setMentions(trim($content), 0, $network),
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1107,11 +1111,11 @@ class BBCode
|
|||
$ch = @curl_init($match[1]);
|
||||
@curl_setopt($ch, CURLOPT_NOBODY, true);
|
||||
@curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
@curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent());
|
||||
@curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent());
|
||||
@curl_exec($ch);
|
||||
$curl_info = @curl_getinfo($ch);
|
||||
|
||||
DI::profiler()->saveTimestamp($stamp1, "network", System::callstack());
|
||||
DI::profiler()->saveTimestamp($stamp1, "network");
|
||||
|
||||
if (substr($curl_info['content_type'], 0, 6) == 'image/') {
|
||||
$text = "[url=" . $match[1] . ']' . $match[1] . "[/url]";
|
||||
|
|
@ -1119,7 +1123,7 @@ class BBCode
|
|||
$text = "[url=" . $match[2] . ']' . $match[2] . "[/url]";
|
||||
|
||||
// if its not a picture then look if its a page that contains a picture link
|
||||
$body = Network::fetchUrl($match[1]);
|
||||
$body = DI::httpRequest()->fetch($match[1]);
|
||||
|
||||
$doc = new DOMDocument();
|
||||
@$doc->loadHTML($body);
|
||||
|
|
@ -1181,11 +1185,11 @@ class BBCode
|
|||
$ch = @curl_init($match[1]);
|
||||
@curl_setopt($ch, CURLOPT_NOBODY, true);
|
||||
@curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
@curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent());
|
||||
@curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent());
|
||||
@curl_exec($ch);
|
||||
$curl_info = @curl_getinfo($ch);
|
||||
|
||||
DI::profiler()->saveTimestamp($stamp1, "network", System::callstack());
|
||||
DI::profiler()->saveTimestamp($stamp1, "network");
|
||||
|
||||
// if its a link to a picture then embed this picture
|
||||
if (substr($curl_info['content_type'], 0, 6) == 'image/') {
|
||||
|
|
@ -1198,7 +1202,7 @@ class BBCode
|
|||
}
|
||||
|
||||
// if its not a picture then look if its a page that contains a picture link
|
||||
$body = Network::fetchUrl($match[1]);
|
||||
$body = DI::httpRequest()->fetch($match[1]);
|
||||
|
||||
$doc = new DOMDocument();
|
||||
@$doc->loadHTML($body);
|
||||
|
|
@ -1233,6 +1237,17 @@ class BBCode
|
|||
return $return;
|
||||
}
|
||||
|
||||
public static function removeLinks(string $bbcode)
|
||||
{
|
||||
$bbcode = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", ' $1 ', $bbcode);
|
||||
$bbcode = preg_replace("/\[img.*?\[\/img\]/ism", ' ', $bbcode);
|
||||
|
||||
$bbcode = preg_replace('/[@!#]\[url\=.*?\].*?\[\/url\]/ism', '', $bbcode);
|
||||
$bbcode = preg_replace("/\[url=[^\[\]]*\](.*)\[\/url\]/Usi", ' $1 ', $bbcode);
|
||||
$bbcode = preg_replace('/[@!#]?\[url.*?\[\/url\]/ism', '', $bbcode);
|
||||
return $bbcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BBCode message to HTML message
|
||||
*
|
||||
|
|
@ -1258,679 +1273,629 @@ class BBCode
|
|||
* @return string
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function convert($text, $try_oembed = true, $simple_html = 0, $for_plaintext = false)
|
||||
public static function convert(string $text = null, $try_oembed = true, $simple_html = self::INTERNAL, $for_plaintext = false)
|
||||
{
|
||||
$a = DI::app();
|
||||
|
||||
/*
|
||||
* preg_match_callback function to replace potential Oembed tags with Oembed content
|
||||
*
|
||||
* $match[0] = [tag]$url[/tag] or [tag=$url]$title[/tag]
|
||||
* $match[1] = $url
|
||||
* $match[2] = $title or absent
|
||||
*/
|
||||
$try_oembed_callback = function ($match)
|
||||
{
|
||||
$url = $match[1];
|
||||
$title = $match[2] ?? null;
|
||||
|
||||
try {
|
||||
$return = OEmbed::getHTML($url, $title);
|
||||
} catch (Exception $ex) {
|
||||
$return = $match[0];
|
||||
}
|
||||
|
||||
return $return;
|
||||
};
|
||||
|
||||
// Extracting code blocks before the whitespace processing and the autolinker
|
||||
$codeblocks = [];
|
||||
|
||||
$text = preg_replace_callback("#\[code(?:=([^\]]*))?\](.*?)\[\/code\]#ism",
|
||||
function ($matches) use (&$codeblocks) {
|
||||
$return = '#codeblock-' . count($codeblocks) . '#';
|
||||
if (strpos($matches[2], "\n") !== false) {
|
||||
$codeblocks[] = '<pre><code class="language-' . trim($matches[1]) . '">' . htmlspecialchars(trim($matches[2], "\n\r"), ENT_NOQUOTES, 'UTF-8') . '</code></pre>';
|
||||
} else {
|
||||
$codeblocks[] = '<code>' . htmlspecialchars($matches[2], ENT_NOQUOTES, 'UTF-8') . '</code>';
|
||||
}
|
||||
|
||||
return $return;
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
// Hide all [noparse] contained bbtags by spacefying them
|
||||
// POSSIBLE BUG --> Will the 'preg' functions crash if there's an embedded image?
|
||||
|
||||
$text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'self::escapeNoparseCallback', $text);
|
||||
$text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'self::escapeNoparseCallback', $text);
|
||||
$text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'self::escapeNoparseCallback', $text);
|
||||
|
||||
// Remove the abstract element. It is a non visible element.
|
||||
$text = self::stripAbstract($text);
|
||||
|
||||
// Move all spaces out of the tags
|
||||
$text = preg_replace("/\[(\w*)\](\s*)/ism", '$2[$1]', $text);
|
||||
$text = preg_replace("/(\s*)\[\/(\w*)\]/ism", '[/$2]$1', $text);
|
||||
|
||||
// Extract the private images which use data urls since preg has issues with
|
||||
// large data sizes. Stash them away while we do bbcode conversion, and then put them back
|
||||
// in after we've done all the regex matching. We cannot use any preg functions to do this.
|
||||
|
||||
$extracted = self::extractImagesFromItemBody($text);
|
||||
$text = $extracted['body'];
|
||||
$saved_image = $extracted['images'];
|
||||
|
||||
// If we find any event code, turn it into an event.
|
||||
// After we're finished processing the bbcode we'll
|
||||
// replace all of the event code with a reformatted version.
|
||||
|
||||
$ev = Event::fromBBCode($text);
|
||||
|
||||
// Replace any html brackets with HTML Entities to prevent executing HTML or script
|
||||
// Don't use strip_tags here because it breaks [url] search by replacing & with amp
|
||||
|
||||
$text = str_replace("<", "<", $text);
|
||||
$text = str_replace(">", ">", $text);
|
||||
|
||||
// remove some newlines before the general conversion
|
||||
$text = preg_replace("/\s?\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "[share$1]$2[/share]", $text);
|
||||
$text = preg_replace("/\s?\[quote(.*?)\]\s?(.*?)\s?\[\/quote\]\s?/ism", "[quote$1]$2[/quote]", $text);
|
||||
|
||||
// when the content is meant exporting to other systems then remove the avatar picture since this doesn't really look good on these systems
|
||||
if (!$try_oembed) {
|
||||
$text = preg_replace("/\[share(.*?)avatar\s?=\s?'.*?'\s?(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "\n[share$1$2]$3[/share]", $text);
|
||||
// Accounting for null default column values
|
||||
if (is_null($text) || $text === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Convert new line chars to html <br /> tags
|
||||
|
||||
// nlbr seems to be hopelessly messed up
|
||||
// $Text = nl2br($Text);
|
||||
|
||||
// We'll emulate it.
|
||||
|
||||
$text = trim($text);
|
||||
$text = str_replace("\r\n", "\n", $text);
|
||||
|
||||
// Remove linefeeds inside of the table elements. See issue #6799
|
||||
$search = ["\n[th]", "[th]\n", " [th]", "\n[/th]", "[/th]\n", "[/th] ",
|
||||
"\n[td]", "[td]\n", " [td]", "\n[/td]", "[/td]\n", "[/td] ",
|
||||
"\n[tr]", "[tr]\n", " [tr]", "[tr] ", "\n[/tr]", "[/tr]\n", " [/tr]", "[/tr] ",
|
||||
"[table]\n", "[table] ", " [table]", "\n[/table]", " [/table]", "[/table] "];
|
||||
$replace = ["[th]", "[th]", "[th]", "[/th]", "[/th]", "[/th]",
|
||||
"[td]", "[td]", "[td]", "[/td]", "[/td]", "[/td]",
|
||||
"[tr]", "[tr]", "[tr]", "[tr]", "[/tr]", "[/tr]", "[/tr]", "[/tr]",
|
||||
"[table]", "[table]", "[table]", "[/table]", "[/table]", "[/table]"];
|
||||
do {
|
||||
$oldtext = $text;
|
||||
$text = str_replace($search, $replace, $text);
|
||||
} while ($oldtext != $text);
|
||||
|
||||
// Replace these here only once
|
||||
$search = ["\n[table]", "[/table]\n"];
|
||||
$replace = ["[table]", "[/table]"];
|
||||
$text = str_replace($search, $replace, $text);
|
||||
|
||||
// removing multiplicated newlines
|
||||
if (DI::config()->get('system', 'remove_multiplicated_lines')) {
|
||||
$search = ["\n\n\n", "\n ", " \n", "[/quote]\n\n", "\n[/quote]", "[/li]\n", "\n[li]", "\n[ul]", "[/ul]\n", "\n\n[share ", "[/attachment]\n",
|
||||
"\n[h1]", "[/h1]\n", "\n[h2]", "[/h2]\n", "\n[h3]", "[/h3]\n", "\n[h4]", "[/h4]\n", "\n[h5]", "[/h5]\n", "\n[h6]", "[/h6]\n"];
|
||||
$replace = ["\n\n", "\n", "\n", "[/quote]\n", "[/quote]", "[/li]", "[li]", "[ul]", "[/ul]", "\n[share ", "[/attachment]",
|
||||
"[h1]", "[/h1]", "[h2]", "[/h2]", "[h3]", "[/h3]", "[h4]", "[/h4]", "[h5]", "[/h5]", "[h6]", "[/h6]"];
|
||||
do {
|
||||
$oldtext = $text;
|
||||
$text = str_replace($search, $replace, $text);
|
||||
} while ($oldtext != $text);
|
||||
}
|
||||
|
||||
/// @todo Have a closer look at the different html modes
|
||||
// Handle attached links or videos
|
||||
if (in_array($simple_html, [9])) {
|
||||
$text = self::removeAttachment($text);
|
||||
} elseif (!in_array($simple_html, [0, 4])) {
|
||||
$text = self::removeAttachment($text, true);
|
||||
} else {
|
||||
$text = self::convertAttachment($text, $simple_html, $try_oembed);
|
||||
}
|
||||
|
||||
// leave open the posibility of [map=something]
|
||||
// this is replaced in Item::prepareBody() which has knowledge of the item location
|
||||
if (strpos($text, '[/map]') !== false) {
|
||||
$text = preg_replace_callback(
|
||||
"/\[map\](.*?)\[\/map\]/ism",
|
||||
function ($match) use ($simple_html) {
|
||||
return str_replace($match[0], '<p class="map">' . Map::byLocation($match[1], $simple_html) . '</p>', $match[0]);
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($text, '[map=') !== false) {
|
||||
$text = preg_replace_callback(
|
||||
"/\[map=(.*?)\]/ism",
|
||||
function ($match) use ($simple_html) {
|
||||
return str_replace($match[0], '<p class="map">' . Map::byCoordinates(str_replace('/', ' ', $match[1]), $simple_html) . '</p>', $match[0]);
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($text, '[map]') !== false) {
|
||||
$text = preg_replace("/\[map\]/", '<p class="map"></p>', $text);
|
||||
}
|
||||
|
||||
// Check for headers
|
||||
$text = preg_replace("(\[h1\](.*?)\[\/h1\])ism", '<h1>$1</h1>', $text);
|
||||
$text = preg_replace("(\[h2\](.*?)\[\/h2\])ism", '<h2>$1</h2>', $text);
|
||||
$text = preg_replace("(\[h3\](.*?)\[\/h3\])ism", '<h3>$1</h3>', $text);
|
||||
$text = preg_replace("(\[h4\](.*?)\[\/h4\])ism", '<h4>$1</h4>', $text);
|
||||
$text = preg_replace("(\[h5\](.*?)\[\/h5\])ism", '<h5>$1</h5>', $text);
|
||||
$text = preg_replace("(\[h6\](.*?)\[\/h6\])ism", '<h6>$1</h6>', $text);
|
||||
|
||||
// Check for paragraph
|
||||
$text = preg_replace("(\[p\](.*?)\[\/p\])ism", '<p>$1</p>', $text);
|
||||
|
||||
// Check for bold text
|
||||
$text = preg_replace("(\[b\](.*?)\[\/b\])ism", '<strong>$1</strong>', $text);
|
||||
|
||||
// Check for Italics text
|
||||
$text = preg_replace("(\[i\](.*?)\[\/i\])ism", '<em>$1</em>', $text);
|
||||
|
||||
// Check for Underline text
|
||||
$text = preg_replace("(\[u\](.*?)\[\/u\])ism", '<u>$1</u>', $text);
|
||||
|
||||
// Check for strike-through text
|
||||
$text = preg_replace("(\[s\](.*?)\[\/s\])ism", '<s>$1</s>', $text);
|
||||
|
||||
// Check for over-line text
|
||||
$text = preg_replace("(\[o\](.*?)\[\/o\])ism", '<span class="overline">$1</span>', $text);
|
||||
|
||||
// Check for colored text
|
||||
$text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])ism", "<span style=\"color: $1;\">$2</span>", $text);
|
||||
|
||||
// Check for sized text
|
||||
// [size=50] --> font-size: 50px (with the unit).
|
||||
if ($simple_html != 3) {
|
||||
$text = preg_replace("(\[size=(\d*?)\](.*?)\[\/size\])ism", "<span style=\"font-size: $1px; line-height: initial;\">$2</span>", $text);
|
||||
$text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism", "<span style=\"font-size: $1; line-height: initial;\">$2</span>", $text);
|
||||
} else {
|
||||
// Issue 2199: Diaspora doesn't interpret the construct above, nor the <small> or <big> element
|
||||
$text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism", "$2", $text);
|
||||
}
|
||||
|
||||
|
||||
// Check for centered text
|
||||
$text = preg_replace("(\[center\](.*?)\[\/center\])ism", "<div style=\"text-align:center;\">$1</div>", $text);
|
||||
|
||||
// Check for list text
|
||||
$text = str_replace("[*]", "<li>", $text);
|
||||
|
||||
// Check for style sheet commands
|
||||
$text = preg_replace_callback(
|
||||
"(\[style=(.*?)\](.*?)\[\/style\])ism",
|
||||
function ($match) {
|
||||
return "<span style=\"" . HTML::sanitizeCSS($match[1]) . ";\">" . $match[2] . "</span>";
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
// Check for CSS classes
|
||||
$text = preg_replace_callback(
|
||||
"(\[class=(.*?)\](.*?)\[\/class\])ism",
|
||||
function ($match) {
|
||||
return "<span class=\"" . HTML::sanitizeCSS($match[1]) . "\">" . $match[2] . "</span>";
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
// handle nested lists
|
||||
$endlessloop = 0;
|
||||
|
||||
while ((((strpos($text, "[/list]") !== false) && (strpos($text, "[list") !== false)) ||
|
||||
((strpos($text, "[/ol]") !== false) && (strpos($text, "[ol]") !== false)) ||
|
||||
((strpos($text, "[/ul]") !== false) && (strpos($text, "[ul]") !== false)) ||
|
||||
((strpos($text, "[/li]") !== false) && (strpos($text, "[li]") !== false))) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[list\](.*?)\[\/list\]/ism", '<ul class="listbullet" style="list-style-type: circle;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '<ul class="listnone" style="list-style-type: none;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[list=1\](.*?)\[\/list\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[list=((?-i)i)\](.*?)\[\/list\]/ism", '<ul class="listlowerroman" style="list-style-type: lower-roman;">$2</ul>', $text);
|
||||
$text = preg_replace("/\[list=((?-i)I)\](.*?)\[\/list\]/ism", '<ul class="listupperroman" style="list-style-type: upper-roman;">$2</ul>', $text);
|
||||
$text = preg_replace("/\[list=((?-i)a)\](.*?)\[\/list\]/ism", '<ul class="listloweralpha" style="list-style-type: lower-alpha;">$2</ul>', $text);
|
||||
$text = preg_replace("/\[list=((?-i)A)\](.*?)\[\/list\]/ism", '<ul class="listupperalpha" style="list-style-type: upper-alpha;">$2</ul>', $text);
|
||||
$text = preg_replace("/\[ul\](.*?)\[\/ul\]/ism", '<ul class="listbullet" style="list-style-type: circle;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '<li>$1</li>', $text);
|
||||
}
|
||||
|
||||
$text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '<th>$1</th>', $text);
|
||||
$text = preg_replace("/\[td\](.*?)\[\/td\]/sm", '<td>$1</td>', $text);
|
||||
$text = preg_replace("/\[tr\](.*?)\[\/tr\]/sm", '<tr>$1</tr>', $text);
|
||||
$text = preg_replace("/\[table\](.*?)\[\/table\]/sm", '<table>$1</table>', $text);
|
||||
|
||||
$text = preg_replace("/\[table border=1\](.*?)\[\/table\]/sm", '<table border="1" >$1</table>', $text);
|
||||
$text = preg_replace("/\[table border=0\](.*?)\[\/table\]/sm", '<table border="0" >$1</table>', $text);
|
||||
|
||||
$text = str_replace('[hr]', '<hr />', $text);
|
||||
|
||||
if (!$for_plaintext) {
|
||||
$escaped = [];
|
||||
|
||||
// Escaping BBCodes susceptible to contain rogue URL we don'' want the autolinker to catch
|
||||
$text = preg_replace_callback('#\[(url|img|audio|video|youtube|vimeo|share|attachment|iframe|bookmark).+?\[/\1\]#ism',
|
||||
function ($matches) use (&$escaped) {
|
||||
$return = '{escaped-' . count($escaped) . '}';
|
||||
$escaped[] = $matches[0];
|
||||
|
||||
return $return;
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
// Autolinker for isolated URLs
|
||||
$text = preg_replace(Strings::autoLinkRegEx(), '[url]$1[/url]', $text);
|
||||
|
||||
// Restoring escaped blocks
|
||||
$text = preg_replace_callback('/{escaped-([0-9]+)}/iU',
|
||||
function ($matches) use ($escaped) {
|
||||
return $escaped[intval($matches[1])] ?? $matches[0];
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
// This is actually executed in Item::prepareBody()
|
||||
|
||||
$nosmile = strpos($text, '[nosmile]') !== false;
|
||||
$text = str_replace('[nosmile]', '', $text);
|
||||
|
||||
// Check for font change text
|
||||
$text = preg_replace("/\[font=(.*?)\](.*?)\[\/font\]/sm", "<span style=\"font-family: $1;\">$2</span>", $text);
|
||||
|
||||
// Declare the format for [spoiler] layout
|
||||
$SpoilerLayout = '<details class="spoiler"><summary>' . DI::l10n()->t('Click to open/close') . '</summary>$1</details>';
|
||||
|
||||
// Check for [spoiler] text
|
||||
// handle nested quotes
|
||||
$endlessloop = 0;
|
||||
while ((strpos($text, "[/spoiler]") !== false) && (strpos($text, "[spoiler]") !== false) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[spoiler\](.*?)\[\/spoiler\]/ism", $SpoilerLayout, $text);
|
||||
}
|
||||
|
||||
// Check for [spoiler=Title] text
|
||||
|
||||
// handle nested quotes
|
||||
$endlessloop = 0;
|
||||
while ((strpos($text, "[/spoiler]")!== false) && (strpos($text, "[spoiler=") !== false) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[spoiler=[\"\']*(.*?)[\"\']*\](.*?)\[\/spoiler\]/ism",
|
||||
'<details class="spoiler"><summary>$1</summary>$2</details>',
|
||||
$text);
|
||||
}
|
||||
|
||||
// Declare the format for [quote] layout
|
||||
$QuoteLayout = '<blockquote>$1</blockquote>';
|
||||
|
||||
// Check for [quote] text
|
||||
// handle nested quotes
|
||||
$endlessloop = 0;
|
||||
while ((strpos($text, "[/quote]") !== false) && (strpos($text, "[quote]") !== false) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[quote\](.*?)\[\/quote\]/ism", "$QuoteLayout", $text);
|
||||
}
|
||||
|
||||
// Check for [quote=Author] text
|
||||
|
||||
$t_wrote = DI::l10n()->t('$1 wrote:');
|
||||
|
||||
// handle nested quotes
|
||||
$endlessloop = 0;
|
||||
while ((strpos($text, "[/quote]")!== false) && (strpos($text, "[quote=") !== false) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism",
|
||||
"<p><strong class=".'"author"'.">" . $t_wrote . "</strong></p><blockquote>$2</blockquote>",
|
||||
$text);
|
||||
}
|
||||
|
||||
|
||||
// [img=widthxheight]image source[/img]
|
||||
$text = preg_replace_callback(
|
||||
"/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism",
|
||||
function ($matches) use ($simple_html) {
|
||||
if (strpos($matches[3], "data:image/") === 0) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
$matches[3] = self::proxyUrl($matches[3], $simple_html);
|
||||
return "[img=" . $matches[1] . "x" . $matches[2] . "]" . $matches[3] . "[/img]";
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
$text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '<img src="$3" style="width: $1px;" >', $text);
|
||||
$text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$3" style="width: $1px;" >', $text);
|
||||
|
||||
$text = preg_replace_callback("/\[img\=(.*?)\](.*?)\[\/img\]/ism",
|
||||
function ($matches) use ($simple_html) {
|
||||
$matches[1] = self::proxyUrl($matches[1], $simple_html);
|
||||
$matches[2] = htmlspecialchars($matches[2], ENT_COMPAT);
|
||||
return '<img src="' . $matches[1] . '" alt="' . $matches[2] . '" title="' . $matches[2] . '">';
|
||||
},
|
||||
$text);
|
||||
|
||||
// Images
|
||||
// [img]pathtoimage[/img]
|
||||
$text = preg_replace_callback(
|
||||
"/\[img\](.*?)\[\/img\]/ism",
|
||||
function ($matches) use ($simple_html) {
|
||||
if (strpos($matches[1], "data:image/") === 0) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
$matches[1] = self::proxyUrl($matches[1], $simple_html);
|
||||
return "[img]" . $matches[1] . "[/img]";
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
$text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img src="$1" alt="' . DI::l10n()->t('Image/photo') . '" />', $text);
|
||||
$text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '<img src="$1" alt="' . DI::l10n()->t('Image/photo') . '" />', $text);
|
||||
|
||||
$text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism", '<br/><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . DI::l10n()->t('Encrypted content') . '" /><br />', $text);
|
||||
$text = preg_replace("/\[crypt(.*?)\](.*?)\[\/crypt\]/ism", '<br/><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . '$1' . ' ' . DI::l10n()->t('Encrypted content') . '" /><br />', $text);
|
||||
//$Text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism", '<br/><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . '$1' . ' ' . DI::l10n()->t('Encrypted content') . '" /><br />', $Text);
|
||||
|
||||
// Simplify "video" element
|
||||
$text = preg_replace('(\[video.*?\ssrc\s?=\s?([^\s\]]+).*?\].*?\[/video\])ism', '[video]$1[/video]', $text);
|
||||
|
||||
// Try to Oembed
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace("/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4).*?)\[\/video\]/ism", '<video src="$1" controls="controls" width="' . $a->videowidth . '" height="' . $a->videoheight . '" loop="true"><a href="$1">$1</a></video>', $text);
|
||||
$text = preg_replace("/\[audio\](.*?)\[\/audio\]/ism", '<audio src="$1" controls="controls"><a href="$1">$1</a></audio>', $text);
|
||||
|
||||
$text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", $try_oembed_callback, $text);
|
||||
$text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", $try_oembed_callback, $text);
|
||||
} else {
|
||||
$text = preg_replace("/\[video\](.*?)\[\/video\]/ism",
|
||||
'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', $text);
|
||||
$text = preg_replace("/\[audio\](.*?)\[\/audio\]/ism",
|
||||
'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', $text);
|
||||
}
|
||||
|
||||
// html5 video and audio
|
||||
|
||||
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<iframe src="$1" width="' . $a->videowidth . '" height="' . $a->videoheight . '"><a href="$1">$1</a></iframe>', $text);
|
||||
} else {
|
||||
$text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1">$1</a>', $text);
|
||||
}
|
||||
|
||||
// Youtube extensions
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace_callback("/\[youtube\](https?:\/\/www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", $try_oembed_callback, $text);
|
||||
$text = preg_replace_callback("/\[youtube\](www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", $try_oembed_callback, $text);
|
||||
$text = preg_replace_callback("/\[youtube\](https?:\/\/youtu.be\/.*?)\[\/youtube\]/ism", $try_oembed_callback, $text);
|
||||
}
|
||||
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '<iframe width="' . $a->videowidth . '" height="' . $a->videoheight . '" src="https://www.youtube.com/embed/$1" frameborder="0" ></iframe>', $text);
|
||||
} else {
|
||||
$text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism",
|
||||
'<a href="https://www.youtube.com/watch?v=$1" target="_blank" rel="noopener noreferrer">https://www.youtube.com/watch?v=$1</a>', $text);
|
||||
}
|
||||
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace_callback("/\[vimeo\](https?:\/\/player.vimeo.com\/video\/[0-9]+).*?\[\/vimeo\]/ism", $try_oembed_callback, $text);
|
||||
$text = preg_replace_callback("/\[vimeo\](https?:\/\/vimeo.com\/[0-9]+).*?\[\/vimeo\]/ism", $try_oembed_callback, $text);
|
||||
}
|
||||
|
||||
$text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
|
||||
$text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
|
||||
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '<iframe width="' . $a->videowidth . '" height="' . $a->videoheight . '" src="https://player.vimeo.com/video/$1" frameborder="0" ></iframe>', $text);
|
||||
} else {
|
||||
$text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism",
|
||||
'<a href="https://vimeo.com/$1" target="_blank" rel="noopener noreferrer">https://vimeo.com/$1</a>', $text);
|
||||
}
|
||||
|
||||
// oembed tag
|
||||
$text = OEmbed::BBCode2HTML($text);
|
||||
|
||||
// Avoid triple linefeeds through oembed
|
||||
$text = str_replace("<br style='clear:left'></span><br /><br />", "<br style='clear:left'></span><br />", $text);
|
||||
|
||||
// If we found an event earlier, strip out all the event code and replace with a reformatted version.
|
||||
// Replace the event-start section with the entire formatted event. The other bbcode is stripped.
|
||||
// Summary (e.g. title) is required, earlier revisions only required description (in addition to
|
||||
// start which is always required). Allow desc with a missing summary for compatibility.
|
||||
|
||||
if ((!empty($ev['desc']) || !empty($ev['summary'])) && !empty($ev['start'])) {
|
||||
$sub = Event::getHTML($ev, $simple_html);
|
||||
|
||||
$text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-start\](.*?)\[\/event\-start\]/ism", $sub, $text);
|
||||
$text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-location\](.*?)\[\/event\-location\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-adjust\](.*?)\[\/event\-adjust\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-id\](.*?)\[\/event\-id\]/ism", '', $text);
|
||||
}
|
||||
|
||||
// Replace non graphical smilies for external posts
|
||||
if (!$nosmile && !$for_plaintext) {
|
||||
$text = Smilies::replace($text);
|
||||
}
|
||||
|
||||
if (!$for_plaintext) {
|
||||
if (in_array($simple_html, [7, 9])) {
|
||||
$text = preg_replace_callback("/\[url\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text);
|
||||
$text = preg_replace_callback("/\[url\=(.*?)\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text);
|
||||
}
|
||||
} else {
|
||||
$text = preg_replace("(\[url\](.*?)\[\/url\])ism", " $1 ", $text);
|
||||
$text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", 'self::removePictureLinksCallback', $text);
|
||||
}
|
||||
|
||||
$text = str_replace(["\r","\n"], ['<br />', '<br />'], $text);
|
||||
|
||||
// Remove all hashtag addresses
|
||||
if ($simple_html && !in_array($simple_html, [3, 7, 9])) {
|
||||
$text = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', $text);
|
||||
} elseif ($simple_html == 3) {
|
||||
// The ! is converted to @ since Diaspora only understands the @
|
||||
$text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
|
||||
'@<a href="$2">$3</a>',
|
||||
$text);
|
||||
} elseif (in_array($simple_html, [7, 9])) {
|
||||
$text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
|
||||
'$1<span class="vcard"><a href="$2" class="url u-url mention" title="$3"><span class="fn nickname mention">$3</span></a></span>',
|
||||
$text);
|
||||
} elseif (!$simple_html) {
|
||||
$text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
|
||||
'$1<a href="$2" class="userinfo mention" title="$3">$3</a>',
|
||||
$text);
|
||||
}
|
||||
|
||||
// Bookmarks in red - will be converted to bookmarks in friendica
|
||||
$text = preg_replace("/#\^\[url\](.*?)\[\/url\]/ism", '[bookmark=$1]$1[/bookmark]', $text);
|
||||
$text = preg_replace("/#\^\[url\=(.*?)\](.*?)\[\/url\]/ism", '[bookmark=$1]$2[/bookmark]', $text);
|
||||
$text = preg_replace("/#\[url\=.*?\]\^\[\/url\]\[url\=(.*?)\](.*?)\[\/url\]/i",
|
||||
"[bookmark=$1]$2[/bookmark]", $text);
|
||||
|
||||
if (in_array($simple_html, [2, 6, 7, 8])) {
|
||||
$text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", "self::expandLinksCallback", $text);
|
||||
//$Text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $Text);
|
||||
$text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]',$text);
|
||||
}
|
||||
|
||||
if ($simple_html == 5) {
|
||||
$text = preg_replace("/[^#@!]\[url\=(.*?)\](.*?)\[\/url\]/ism", '[url]$1[/url]', $text);
|
||||
}
|
||||
|
||||
// Perform URL Search
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace_callback("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $try_oembed_callback, $text);
|
||||
}
|
||||
|
||||
if ($simple_html == 5) {
|
||||
$text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", '[url]$1[/url]', $text);
|
||||
} else {
|
||||
$text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", '[url=$1]$2[/url]', $text);
|
||||
}
|
||||
|
||||
// Handle Diaspora posts
|
||||
$text = preg_replace_callback(
|
||||
"&\[url=/?posts/([^\[\]]*)\](.*)\[\/url\]&Usi",
|
||||
function ($match) {
|
||||
return "[url=" . DI::baseUrl() . "/display/" . $match[1] . "]" . $match[2] . "[/url]";
|
||||
}, $text
|
||||
);
|
||||
|
||||
$text = preg_replace_callback(
|
||||
"&\[url=/people\?q\=(.*)\](.*)\[\/url\]&Usi",
|
||||
function ($match) {
|
||||
return "[url=" . DI::baseUrl() . "/search?search=%40" . $match[1] . "]" . $match[2] . "[/url]";
|
||||
}, $text
|
||||
);
|
||||
|
||||
// Server independent link to posts and comments
|
||||
// See issue: https://github.com/diaspora/diaspora_federation/issues/75
|
||||
$expression = "=diaspora://.*?/post/([0-9A-Za-z\-_@.:]{15,254}[0-9A-Za-z])=ism";
|
||||
$text = preg_replace($expression, DI::baseUrl()."/display/$1", $text);
|
||||
|
||||
/* Tag conversion
|
||||
* Supports:
|
||||
* - #[url=<anything>]<term>[/url]
|
||||
* - [url=<anything>]#<term>[/url]
|
||||
*/
|
||||
$text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function($matches) {
|
||||
return '#<a href="'
|
||||
. DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
|
||||
. '" class="tag" rel="tag" title="' . XML::escape($matches[1]) . '">'
|
||||
. XML::escape($matches[1])
|
||||
. '</a>';
|
||||
}, $text);
|
||||
|
||||
// We need no target="_blank" rel="noopener noreferrer" for local links
|
||||
// convert links start with DI::baseUrl() as local link without the target="_blank" rel="noopener noreferrer" attribute
|
||||
$escapedBaseUrl = preg_quote(DI::baseUrl(), '/');
|
||||
$text = preg_replace("/\[url\](".$escapedBaseUrl.".*?)\[\/url\]/ism", '<a href="$1">$1</a>', $text);
|
||||
$text = preg_replace("/\[url\=(".$escapedBaseUrl.".*?)\](.*?)\[\/url\]/ism", '<a href="$1">$2</a>', $text);
|
||||
|
||||
$text = preg_replace("/\[url\](.*?)\[\/url\]/ism", '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', $text);
|
||||
$text = preg_replace("/\[url\=(.*?)\](.*?)\[\/url\]/ism", '<a href="$1" target="_blank" rel="noopener noreferrer">$2</a>', $text);
|
||||
|
||||
// Red compatibility, though the link can't be authenticated on Friendica
|
||||
$text = preg_replace("/\[zrl\=(.*?)\](.*?)\[\/zrl\]/ism", '<a href="$1" target="_blank" rel="noopener noreferrer">$2</a>', $text);
|
||||
|
||||
|
||||
// we may need to restrict this further if it picks up too many strays
|
||||
// link acct:user@host to a webfinger profile redirector
|
||||
|
||||
$text = preg_replace('/acct:([^@]+)@((?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63})/', '<a href="' . DI::baseUrl() . '/acctlink?addr=$1@$2" target="extlink">acct:$1@$2</a>', $text);
|
||||
|
||||
// Perform MAIL Search
|
||||
$text = preg_replace("/\[mail\](.*?)\[\/mail\]/", '<a href="mailto:$1">$1</a>', $text);
|
||||
$text = preg_replace("/\[mail\=(.*?)\](.*?)\[\/mail\]/", '<a href="mailto:$1">$2</a>', $text);
|
||||
|
||||
// Unhide all [noparse] contained bbtags unspacefying them
|
||||
// and triming the [noparse] tag.
|
||||
|
||||
$text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'self::unescapeNoparseCallback', $text);
|
||||
$text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'self::unescapeNoparseCallback', $text);
|
||||
$text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'self::unescapeNoparseCallback', $text);
|
||||
|
||||
/// @todo What is the meaning of these lines?
|
||||
$text = preg_replace('/\[\&\;([#a-z0-9]+)\;\]/', '&$1;', $text);
|
||||
$text = preg_replace('/\&\#039\;/', '\'', $text);
|
||||
|
||||
// Currently deactivated, it made problems with " inside of alt texts.
|
||||
//$text = preg_replace('/\"\;/', '"', $text);
|
||||
|
||||
// fix any escaped ampersands that may have been converted into links
|
||||
$text = preg_replace('/\<([^>]*?)(src|href)=(.*?)\&\;(.*?)\>/ism', '<$1$2=$3&$4>', $text);
|
||||
|
||||
// sanitizes src attributes (http and redir URLs for displaying in a web page, cid used for inline images in emails)
|
||||
$allowed_src_protocols = ['//', 'http://', 'https://', 'redir/', 'cid:'];
|
||||
|
||||
array_walk($allowed_src_protocols, function(&$value) { $value = preg_quote($value, '#');});
|
||||
|
||||
$text = preg_replace('#<([^>]*?)(src)="(?!' . implode('|', $allowed_src_protocols) . ')(.*?)"(.*?)>#ism',
|
||||
'<$1$2=""$4 data-original-src="$3" class="invalid-src" title="' . DI::l10n()->t('Invalid source protocol') . '">', $text);
|
||||
|
||||
// sanitize href attributes (only whitelisted protocols URLs)
|
||||
// default value for backward compatibility
|
||||
$allowed_link_protocols = DI::config()->get('system', 'allowed_link_protocols', []);
|
||||
|
||||
// Always allowed protocol even if config isn't set or not including it
|
||||
$allowed_link_protocols[] = '//';
|
||||
$allowed_link_protocols[] = 'http://';
|
||||
$allowed_link_protocols[] = 'https://';
|
||||
$allowed_link_protocols[] = 'redir/';
|
||||
|
||||
array_walk($allowed_link_protocols, function(&$value) { $value = preg_quote($value, '#');});
|
||||
|
||||
$regex = '#<([^>]*?)(href)="(?!' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism';
|
||||
$text = preg_replace($regex, '<$1$2="javascript:void(0)"$4 data-original-href="$3" class="invalid-href" title="' . DI::l10n()->t('Invalid link protocol') . '">', $text);
|
||||
|
||||
// Shared content
|
||||
$text = self::convertShare(
|
||||
$text,
|
||||
function (array $attributes, array $author_contact, $content, $is_quote_share) use ($simple_html) {
|
||||
return self::convertShareCallback($attributes, $author_contact, $content, $is_quote_share, $simple_html);
|
||||
}
|
||||
);
|
||||
|
||||
if ($saved_image) {
|
||||
$text = self::interpolateSavedImagesIntoItemBody($text, $saved_image);
|
||||
}
|
||||
|
||||
// Restore code blocks
|
||||
$text = preg_replace_callback('/#codeblock-([0-9]+)#/iU',
|
||||
function ($matches) use ($codeblocks) {
|
||||
$return = $matches[0];
|
||||
if (isset($codeblocks[intval($matches[1])])) {
|
||||
$return = $codeblocks[$matches[1]];
|
||||
}
|
||||
return $return;
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
// Clean up the HTML by loading and saving the HTML with the DOM.
|
||||
// Bad structured html can break a whole page.
|
||||
// For performance reasons do it only with activated item cache or at export.
|
||||
if (!$try_oembed || (get_itemcachepath() != '')) {
|
||||
$doc = new DOMDocument();
|
||||
$doc->preserveWhiteSpace = false;
|
||||
|
||||
$text = mb_convert_encoding($text, 'HTML-ENTITIES', "UTF-8");
|
||||
|
||||
$doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">';
|
||||
$encoding = '<?xml encoding="UTF-8">';
|
||||
@$doc->loadHTML($encoding . $doctype . '<html><body>' . $text . '</body></html>');
|
||||
$doc->encoding = 'UTF-8';
|
||||
$text = $doc->saveHTML();
|
||||
$text = str_replace(['<html><body>', '</body></html>', $doctype, $encoding], ['', '', '', ''], $text);
|
||||
|
||||
$text = str_replace('<br></li>', '</li>', $text);
|
||||
|
||||
//$Text = mb_convert_encoding($Text, "UTF-8", 'HTML-ENTITIES');
|
||||
}
|
||||
|
||||
// Clean up some useless linebreaks in lists
|
||||
//$Text = str_replace('<br /><ul', '<ul ', $Text);
|
||||
//$Text = str_replace('</ul><br />', '</ul>', $Text);
|
||||
//$Text = str_replace('</li><br />', '</li>', $Text);
|
||||
//$Text = str_replace('<br /><li>', '<li>', $Text);
|
||||
//$Text = str_replace('<br /><ul', '<ul ', $Text);
|
||||
|
||||
Hook::callAll('bbcode', $text);
|
||||
|
||||
return trim($text);
|
||||
$a = DI::app();
|
||||
|
||||
$text = self::performWithEscapedTags($text, ['code'], function ($text) use ($try_oembed, $simple_html, $for_plaintext, $a) {
|
||||
$text = self::performWithEscapedTags($text, ['noparse', 'nobb', 'pre'], function ($text) use ($try_oembed, $simple_html, $for_plaintext, $a) {
|
||||
/*
|
||||
* preg_match_callback function to replace potential Oembed tags with Oembed content
|
||||
*
|
||||
* $match[0] = [tag]$url[/tag] or [tag=$url]$title[/tag]
|
||||
* $match[1] = $url
|
||||
* $match[2] = $title or absent
|
||||
*/
|
||||
$try_oembed_callback = function ($match)
|
||||
{
|
||||
$url = $match[1];
|
||||
$title = $match[2] ?? null;
|
||||
|
||||
try {
|
||||
$return = OEmbed::getHTML($url, $title);
|
||||
} catch (Exception $ex) {
|
||||
$return = $match[0];
|
||||
}
|
||||
|
||||
return $return;
|
||||
};
|
||||
|
||||
// Remove the abstract element. It is a non visible element.
|
||||
$text = self::stripAbstract($text);
|
||||
|
||||
// Line ending normalisation
|
||||
$text = str_replace("\r\n", "\n", $text);
|
||||
|
||||
// Move new lines outside of tags
|
||||
$text = preg_replace("#\[(\w*)](\n*)#ism", '$2[$1]', $text);
|
||||
$text = preg_replace("#(\n*)\[/(\w*)]#ism", '[/$2]$1', $text);
|
||||
|
||||
// Extract the private images which use data urls since preg has issues with
|
||||
// large data sizes. Stash them away while we do bbcode conversion, and then put them back
|
||||
// in after we've done all the regex matching. We cannot use any preg functions to do this.
|
||||
|
||||
$extracted = self::extractImagesFromItemBody($text);
|
||||
$text = $extracted['body'];
|
||||
$saved_image = $extracted['images'];
|
||||
|
||||
// If we find any event code, turn it into an event.
|
||||
// After we're finished processing the bbcode we'll
|
||||
// replace all of the event code with a reformatted version.
|
||||
|
||||
$ev = Event::fromBBCode($text);
|
||||
|
||||
// Replace any html brackets with HTML Entities to prevent executing HTML or script
|
||||
// Don't use strip_tags here because it breaks [url] search by replacing & with amp
|
||||
|
||||
$text = str_replace("<", "<", $text);
|
||||
$text = str_replace(">", ">", $text);
|
||||
|
||||
// remove some newlines before the general conversion
|
||||
$text = preg_replace("/\s?\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "[share$1]$2[/share]", $text);
|
||||
$text = preg_replace("/\s?\[quote(.*?)\]\s?(.*?)\s?\[\/quote\]\s?/ism", "[quote$1]$2[/quote]", $text);
|
||||
|
||||
// when the content is meant exporting to other systems then remove the avatar picture since this doesn't really look good on these systems
|
||||
if (!$try_oembed) {
|
||||
$text = preg_replace("/\[share(.*?)avatar\s?=\s?'.*?'\s?(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "\n[share$1$2]$3[/share]", $text);
|
||||
}
|
||||
|
||||
// Remove linefeeds inside of the table elements. See issue #6799
|
||||
$search = ["\n[th]", "[th]\n", " [th]", "\n[/th]", "[/th]\n", "[/th] ",
|
||||
"\n[td]", "[td]\n", " [td]", "\n[/td]", "[/td]\n", "[/td] ",
|
||||
"\n[tr]", "[tr]\n", " [tr]", "[tr] ", "\n[/tr]", "[/tr]\n", " [/tr]", "[/tr] ",
|
||||
"[table]\n", "[table] ", " [table]", "\n[/table]", " [/table]", "[/table] "];
|
||||
$replace = ["[th]", "[th]", "[th]", "[/th]", "[/th]", "[/th]",
|
||||
"[td]", "[td]", "[td]", "[/td]", "[/td]", "[/td]",
|
||||
"[tr]", "[tr]", "[tr]", "[tr]", "[/tr]", "[/tr]", "[/tr]", "[/tr]",
|
||||
"[table]", "[table]", "[table]", "[/table]", "[/table]", "[/table]"];
|
||||
do {
|
||||
$oldtext = $text;
|
||||
$text = str_replace($search, $replace, $text);
|
||||
} while ($oldtext != $text);
|
||||
|
||||
// Replace these here only once
|
||||
$search = ["\n[table]", "[/table]\n"];
|
||||
$replace = ["[table]", "[/table]"];
|
||||
$text = str_replace($search, $replace, $text);
|
||||
|
||||
// Trim new lines regardless of the system.remove_multiplicated_lines config value
|
||||
$text = trim($text, "\n");
|
||||
|
||||
// removing multiplicated newlines
|
||||
if (DI::config()->get('system', 'remove_multiplicated_lines')) {
|
||||
$search = ["\n\n\n", "\n ", " \n", "[/quote]\n\n", "\n[/quote]", "[/li]\n", "\n[li]", "\n[*]", "\n[ul]", "[/ul]\n", "\n\n[share ", "[/attachment]\n",
|
||||
"\n[h1]", "[/h1]\n", "\n[h2]", "[/h2]\n", "\n[h3]", "[/h3]\n", "\n[h4]", "[/h4]\n", "\n[h5]", "[/h5]\n", "\n[h6]", "[/h6]\n"];
|
||||
$replace = ["\n\n", "\n", "\n", "[/quote]\n", "[/quote]", "[/li]", "[li]", "[*]", "[ul]", "[/ul]", "\n[share ", "[/attachment]",
|
||||
"[h1]", "[/h1]", "[h2]", "[/h2]", "[h3]", "[/h3]", "[h4]", "[/h4]", "[h5]", "[/h5]", "[h6]", "[/h6]"];
|
||||
do {
|
||||
$oldtext = $text;
|
||||
$text = str_replace($search, $replace, $text);
|
||||
} while ($oldtext != $text);
|
||||
}
|
||||
|
||||
// Add HTML new lines
|
||||
$text = str_replace("\n", '<br>', $text);
|
||||
|
||||
/// @todo Have a closer look at the different html modes
|
||||
// Handle attached links or videos
|
||||
if ($simple_html == self::ACTIVITYPUB) {
|
||||
$text = self::removeAttachment($text);
|
||||
} elseif (!in_array($simple_html, [self::INTERNAL, self::CONNECTORS])) {
|
||||
$text = self::removeAttachment($text, true);
|
||||
} else {
|
||||
$text = self::convertAttachment($text, $simple_html, $try_oembed);
|
||||
}
|
||||
|
||||
$nosmile = strpos($text, '[nosmile]') !== false;
|
||||
$text = str_replace('[nosmile]', '', $text);
|
||||
|
||||
// Replace non graphical smilies for external posts
|
||||
if (!$nosmile && !$for_plaintext) {
|
||||
$text = self::performWithEscapedTags($text, ['img'], function ($text) {
|
||||
return Smilies::replace($text);
|
||||
});
|
||||
}
|
||||
|
||||
// leave open the posibility of [map=something]
|
||||
// this is replaced in Item::prepareBody() which has knowledge of the item location
|
||||
if (strpos($text, '[/map]') !== false) {
|
||||
$text = preg_replace_callback(
|
||||
"/\[map\](.*?)\[\/map\]/ism",
|
||||
function ($match) use ($simple_html) {
|
||||
return str_replace($match[0], '<p class="map">' . Map::byLocation($match[1], $simple_html) . '</p>', $match[0]);
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($text, '[map=') !== false) {
|
||||
$text = preg_replace_callback(
|
||||
"/\[map=(.*?)\]/ism",
|
||||
function ($match) use ($simple_html) {
|
||||
return str_replace($match[0], '<p class="map">' . Map::byCoordinates(str_replace('/', ' ', $match[1]), $simple_html) . '</p>', $match[0]);
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($text, '[map]') !== false) {
|
||||
$text = preg_replace("/\[map\]/", '<p class="map"></p>', $text);
|
||||
}
|
||||
|
||||
// Check for headers
|
||||
$text = preg_replace("(\[h1\](.*?)\[\/h1\])ism", '<h1>$1</h1>', $text);
|
||||
$text = preg_replace("(\[h2\](.*?)\[\/h2\])ism", '<h2>$1</h2>', $text);
|
||||
$text = preg_replace("(\[h3\](.*?)\[\/h3\])ism", '<h3>$1</h3>', $text);
|
||||
$text = preg_replace("(\[h4\](.*?)\[\/h4\])ism", '<h4>$1</h4>', $text);
|
||||
$text = preg_replace("(\[h5\](.*?)\[\/h5\])ism", '<h5>$1</h5>', $text);
|
||||
$text = preg_replace("(\[h6\](.*?)\[\/h6\])ism", '<h6>$1</h6>', $text);
|
||||
|
||||
// Check for paragraph
|
||||
$text = preg_replace("(\[p\](.*?)\[\/p\])ism", '<p>$1</p>', $text);
|
||||
|
||||
// Check for bold text
|
||||
$text = preg_replace("(\[b\](.*?)\[\/b\])ism", '<strong>$1</strong>', $text);
|
||||
|
||||
// Check for Italics text
|
||||
$text = preg_replace("(\[i\](.*?)\[\/i\])ism", '<em>$1</em>', $text);
|
||||
|
||||
// Check for Underline text
|
||||
$text = preg_replace("(\[u\](.*?)\[\/u\])ism", '<u>$1</u>', $text);
|
||||
|
||||
// Check for strike-through text
|
||||
$text = preg_replace("(\[s\](.*?)\[\/s\])ism", '<s>$1</s>', $text);
|
||||
|
||||
// Check for over-line text
|
||||
$text = preg_replace("(\[o\](.*?)\[\/o\])ism", '<span class="overline">$1</span>', $text);
|
||||
|
||||
// Check for colored text
|
||||
$text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])ism", "<span style=\"color: $1;\">$2</span>", $text);
|
||||
|
||||
// Check for sized text
|
||||
// [size=50] --> font-size: 50px (with the unit).
|
||||
if ($simple_html != self::DIASPORA) {
|
||||
$text = preg_replace("(\[size=(\d*?)\](.*?)\[\/size\])ism", '<span style="font-size:$1px;line-height:normal;">$2</span>', $text);
|
||||
$text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism", '<span style="font-size:$1;line-height:normal;">$2</span>', $text);
|
||||
} else {
|
||||
// Issue 2199: Diaspora doesn't interpret the construct above, nor the <small> or <big> element
|
||||
$text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism", "$2", $text);
|
||||
}
|
||||
|
||||
|
||||
// Check for centered text
|
||||
$text = preg_replace("(\[center\](.*?)\[\/center\])ism", '<div style="text-align:center;">$1</div>', $text);
|
||||
|
||||
// Check for list text
|
||||
$text = str_replace("[*]", "<li>", $text);
|
||||
|
||||
// Check for style sheet commands
|
||||
$text = preg_replace("(\[style=(.*?)\](.*?)\[\/style\])ism", '<span style="$1">$2</span>', $text);
|
||||
|
||||
// Check for CSS classes
|
||||
$text = preg_replace("(\[class=(.*?)\](.*?)\[\/class\])ism", '<span class="$1">$2</span>', $text);
|
||||
|
||||
// handle nested lists
|
||||
$endlessloop = 0;
|
||||
|
||||
while ((((strpos($text, "[/list]") !== false) && (strpos($text, "[list") !== false)) ||
|
||||
((strpos($text, "[/ol]") !== false) && (strpos($text, "[ol]") !== false)) ||
|
||||
((strpos($text, "[/ul]") !== false) && (strpos($text, "[ul]") !== false)) ||
|
||||
((strpos($text, "[/li]") !== false) && (strpos($text, "[li]") !== false))) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[list\](.*?)\[\/list\]/ism", '<ul class="listbullet" style="list-style-type: circle;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '<ul class="listnone" style="list-style-type: none;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[list=1\](.*?)\[\/list\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[list=((?-i)i)\](.*?)\[\/list\]/ism", '<ul class="listlowerroman" style="list-style-type: lower-roman;">$2</ul>', $text);
|
||||
$text = preg_replace("/\[list=((?-i)I)\](.*?)\[\/list\]/ism", '<ul class="listupperroman" style="list-style-type: upper-roman;">$2</ul>', $text);
|
||||
$text = preg_replace("/\[list=((?-i)a)\](.*?)\[\/list\]/ism", '<ul class="listloweralpha" style="list-style-type: lower-alpha;">$2</ul>', $text);
|
||||
$text = preg_replace("/\[list=((?-i)A)\](.*?)\[\/list\]/ism", '<ul class="listupperalpha" style="list-style-type: upper-alpha;">$2</ul>', $text);
|
||||
$text = preg_replace("/\[ul\](.*?)\[\/ul\]/ism", '<ul class="listbullet" style="list-style-type: circle;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>', $text);
|
||||
$text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '<li>$1</li>', $text);
|
||||
}
|
||||
|
||||
$text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '<th>$1</th>', $text);
|
||||
$text = preg_replace("/\[td\](.*?)\[\/td\]/sm", '<td>$1</td>', $text);
|
||||
$text = preg_replace("/\[tr\](.*?)\[\/tr\]/sm", '<tr>$1</tr>', $text);
|
||||
$text = preg_replace("/\[table\](.*?)\[\/table\]/sm", '<table>$1</table>', $text);
|
||||
|
||||
$text = preg_replace("/\[table border=1\](.*?)\[\/table\]/sm", '<table border="1" >$1</table>', $text);
|
||||
$text = preg_replace("/\[table border=0\](.*?)\[\/table\]/sm", '<table border="0" >$1</table>', $text);
|
||||
|
||||
$text = str_replace('[hr]', '<hr />', $text);
|
||||
|
||||
if (!$for_plaintext) {
|
||||
$text = self::performWithEscapedTags($text, ['url', 'img', 'audio', 'video', 'youtube', 'vimeo', 'share', 'attachment', 'iframe', 'bookmark'], function ($text) {
|
||||
return preg_replace(Strings::autoLinkRegEx(), '[url]$1[/url]', $text);
|
||||
});
|
||||
}
|
||||
|
||||
// Check for font change text
|
||||
$text = preg_replace("/\[font=(.*?)\](.*?)\[\/font\]/sm", "<span style=\"font-family: $1;\">$2</span>", $text);
|
||||
|
||||
// Declare the format for [spoiler] layout
|
||||
$SpoilerLayout = '<details class="spoiler"><summary>' . DI::l10n()->t('Click to open/close') . '</summary>$1</details>';
|
||||
|
||||
// Check for [spoiler] text
|
||||
// handle nested quotes
|
||||
$endlessloop = 0;
|
||||
while ((strpos($text, "[/spoiler]") !== false) && (strpos($text, "[spoiler]") !== false) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[spoiler\](.*?)\[\/spoiler\]/ism", $SpoilerLayout, $text);
|
||||
}
|
||||
|
||||
// Check for [spoiler=Title] text
|
||||
|
||||
// handle nested quotes
|
||||
$endlessloop = 0;
|
||||
while ((strpos($text, "[/spoiler]")!== false) && (strpos($text, "[spoiler=") !== false) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[spoiler=[\"\']*(.*?)[\"\']*\](.*?)\[\/spoiler\]/ism",
|
||||
'<details class="spoiler"><summary>$1</summary>$2</details>',
|
||||
$text);
|
||||
}
|
||||
|
||||
// Declare the format for [quote] layout
|
||||
$QuoteLayout = '<blockquote>$1</blockquote>';
|
||||
|
||||
// Check for [quote] text
|
||||
// handle nested quotes
|
||||
$endlessloop = 0;
|
||||
while ((strpos($text, "[/quote]") !== false) && (strpos($text, "[quote]") !== false) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[quote\](.*?)\[\/quote\]/ism", "$QuoteLayout", $text);
|
||||
}
|
||||
|
||||
// Check for [quote=Author] text
|
||||
|
||||
$t_wrote = DI::l10n()->t('$1 wrote:');
|
||||
|
||||
// handle nested quotes
|
||||
$endlessloop = 0;
|
||||
while ((strpos($text, "[/quote]")!== false) && (strpos($text, "[quote=") !== false) && (++$endlessloop < 20)) {
|
||||
$text = preg_replace("/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism",
|
||||
"<p><strong class=".'"author"'.">" . $t_wrote . "</strong></p><blockquote>$2</blockquote>",
|
||||
$text);
|
||||
}
|
||||
|
||||
|
||||
// [img=widthxheight]image source[/img]
|
||||
$text = preg_replace_callback(
|
||||
"/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism",
|
||||
function ($matches) use ($simple_html) {
|
||||
if (strpos($matches[3], "data:image/") === 0) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
$matches[3] = self::proxyUrl($matches[3], $simple_html);
|
||||
return "[img=" . $matches[1] . "x" . $matches[2] . "]" . $matches[3] . "[/img]";
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
$text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '<img src="$3" style="width: $1px;" >', $text);
|
||||
$text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$3" style="width: $1px;" >', $text);
|
||||
|
||||
$text = preg_replace_callback("/\[img\=(.*?)\](.*?)\[\/img\]/ism",
|
||||
function ($matches) use ($simple_html) {
|
||||
$matches[1] = self::proxyUrl($matches[1], $simple_html);
|
||||
$matches[2] = htmlspecialchars($matches[2], ENT_COMPAT);
|
||||
return '<img src="' . $matches[1] . '" alt="' . $matches[2] . '" title="' . $matches[2] . '">';
|
||||
},
|
||||
$text);
|
||||
|
||||
// Images
|
||||
// [img]pathtoimage[/img]
|
||||
$text = preg_replace_callback(
|
||||
"/\[img\](.*?)\[\/img\]/ism",
|
||||
function ($matches) use ($simple_html) {
|
||||
if (strpos($matches[1], "data:image/") === 0) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
$matches[1] = self::proxyUrl($matches[1], $simple_html);
|
||||
return "[img]" . $matches[1] . "[/img]";
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
$text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img src="$1" alt="' . DI::l10n()->t('Image/photo') . '" />', $text);
|
||||
$text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '<img src="$1" alt="' . DI::l10n()->t('Image/photo') . '" />', $text);
|
||||
|
||||
$text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism", '<br><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . DI::l10n()->t('Encrypted content') . '" /><br>', $text);
|
||||
$text = preg_replace("/\[crypt(.*?)\](.*?)\[\/crypt\]/ism", '<br><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . '$1' . ' ' . DI::l10n()->t('Encrypted content') . '" /><br>', $text);
|
||||
//$Text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism", '<br><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . '$1' . ' ' . DI::l10n()->t('Encrypted content') . '" /><br>', $Text);
|
||||
|
||||
// Simplify "video" element
|
||||
$text = preg_replace('(\[video.*?\ssrc\s?=\s?([^\s\]]+).*?\].*?\[/video\])ism', '[video]$1[/video]', $text);
|
||||
|
||||
if ($try_oembed) {
|
||||
// html5 video and audio
|
||||
$text = preg_replace("/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4).*?)\[\/video\]/ism",
|
||||
'<video src="$1" controls width="' . $a->videowidth . '" height="' . $a->videoheight . '" loop="true"><a href="$1">$1</a></video>', $text);
|
||||
$text = preg_replace("/\[video\](.*?)\[\/video\]/ism",
|
||||
'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', $text);
|
||||
$text = preg_replace("/\[audio\](.*?)\[\/audio\]/ism", '<audio src="$1" controls><a href="$1">$1</a></audio>', $text);
|
||||
|
||||
$text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", $try_oembed_callback, $text);
|
||||
$text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", $try_oembed_callback, $text);
|
||||
} else {
|
||||
$text = preg_replace("/\[video\](.*?)\[\/video\]/ism",
|
||||
'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', $text);
|
||||
$text = preg_replace("/\[audio\](.*?)\[\/audio\]/ism",
|
||||
'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', $text);
|
||||
}
|
||||
|
||||
// Backward compatibility, [iframe] support has been removed in version 2020.12
|
||||
$text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1">$1</a>', $text);
|
||||
|
||||
// Youtube extensions
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace_callback("/\[youtube\](https?:\/\/www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", $try_oembed_callback, $text);
|
||||
$text = preg_replace_callback("/\[youtube\](www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", $try_oembed_callback, $text);
|
||||
$text = preg_replace_callback("/\[youtube\](https?:\/\/youtu.be\/.*?)\[\/youtube\]/ism", $try_oembed_callback, $text);
|
||||
}
|
||||
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '<iframe width="' . $a->videowidth . '" height="' . $a->videoheight . '" src="https://www.youtube.com/embed/$1" frameborder="0" ></iframe>', $text);
|
||||
} else {
|
||||
$text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism",
|
||||
'<a href="https://www.youtube.com/watch?v=$1" target="_blank" rel="noopener noreferrer">https://www.youtube.com/watch?v=$1</a>', $text);
|
||||
}
|
||||
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace_callback("/\[vimeo\](https?:\/\/player.vimeo.com\/video\/[0-9]+).*?\[\/vimeo\]/ism", $try_oembed_callback, $text);
|
||||
$text = preg_replace_callback("/\[vimeo\](https?:\/\/vimeo.com\/[0-9]+).*?\[\/vimeo\]/ism", $try_oembed_callback, $text);
|
||||
}
|
||||
|
||||
$text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
|
||||
$text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
|
||||
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '<iframe width="' . $a->videowidth . '" height="' . $a->videoheight . '" src="https://player.vimeo.com/video/$1" frameborder="0" ></iframe>', $text);
|
||||
} else {
|
||||
$text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism",
|
||||
'<a href="https://vimeo.com/$1" target="_blank" rel="noopener noreferrer">https://vimeo.com/$1</a>', $text);
|
||||
}
|
||||
|
||||
// oembed tag
|
||||
$text = OEmbed::BBCode2HTML($text);
|
||||
|
||||
// Avoid triple linefeeds through oembed
|
||||
$text = str_replace("<br style='clear:left'></span><br><br>", "<br style='clear:left'></span><br>", $text);
|
||||
|
||||
// If we found an event earlier, strip out all the event code and replace with a reformatted version.
|
||||
// Replace the event-start section with the entire formatted event. The other bbcode is stripped.
|
||||
// Summary (e.g. title) is required, earlier revisions only required description (in addition to
|
||||
// start which is always required). Allow desc with a missing summary for compatibility.
|
||||
|
||||
if ((!empty($ev['desc']) || !empty($ev['summary'])) && !empty($ev['start'])) {
|
||||
$sub = Event::getHTML($ev, $simple_html);
|
||||
|
||||
$text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-start\](.*?)\[\/event\-start\]/ism", $sub, $text);
|
||||
$text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-location\](.*?)\[\/event\-location\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-adjust\](.*?)\[\/event\-adjust\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-id\](.*?)\[\/event\-id\]/ism", '', $text);
|
||||
}
|
||||
|
||||
if (!$for_plaintext && DI::config()->get('system', 'big_emojis') && ($simple_html != self::DIASPORA)) {
|
||||
$conv = html_entity_decode(str_replace([' ', "\n", "\r"], '', $text));
|
||||
// Emojis are always 4 byte Unicode characters
|
||||
if (!empty($conv) && (strlen($conv) / mb_strlen($conv) == 4)) {
|
||||
$text = '<span style="font-size: xx-large; line-height: normal;">' . $text . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
if (!$for_plaintext) {
|
||||
if (in_array($simple_html, [self::OSTATUS, self::ACTIVITYPUB])) {
|
||||
$text = preg_replace_callback("/\[url\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text);
|
||||
$text = preg_replace_callback("/\[url\=(.*?)\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text);
|
||||
}
|
||||
} else {
|
||||
$text = preg_replace("(\[url\](.*?)\[\/url\])ism", " $1 ", $text);
|
||||
$text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", 'self::removePictureLinksCallback', $text);
|
||||
}
|
||||
|
||||
// Remove all hashtag addresses
|
||||
if ($simple_html && !in_array($simple_html, [self::DIASPORA, self::OSTATUS, self::ACTIVITYPUB])) {
|
||||
$text = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', $text);
|
||||
} elseif ($simple_html == self::DIASPORA) {
|
||||
// The ! is converted to @ since Diaspora only understands the @
|
||||
$text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
|
||||
'@<a href="$2">$3</a>',
|
||||
$text);
|
||||
} elseif (in_array($simple_html, [self::OSTATUS, self::ACTIVITYPUB])) {
|
||||
$text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
|
||||
'$1<span class="vcard"><a href="$2" class="url u-url mention" title="$3"><span class="fn nickname mention">$3</span></a></span>',
|
||||
$text);
|
||||
} elseif (!$simple_html) {
|
||||
$text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
|
||||
'$1<a href="$2" class="userinfo mention" title="$3">$3</a>',
|
||||
$text);
|
||||
}
|
||||
|
||||
// Bookmarks in red - will be converted to bookmarks in friendica
|
||||
$text = preg_replace("/#\^\[url\](.*?)\[\/url\]/ism", '[bookmark=$1]$1[/bookmark]', $text);
|
||||
$text = preg_replace("/#\^\[url\=(.*?)\](.*?)\[\/url\]/ism", '[bookmark=$1]$2[/bookmark]', $text);
|
||||
$text = preg_replace("/#\[url\=.*?\]\^\[\/url\]\[url\=(.*?)\](.*?)\[\/url\]/i",
|
||||
"[bookmark=$1]$2[/bookmark]", $text);
|
||||
|
||||
if (in_array($simple_html, [self::API, self::OSTATUS, self::TWITTER])) {
|
||||
$text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", "self::expandLinksCallback", $text);
|
||||
//$Text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $Text);
|
||||
$text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]',$text);
|
||||
}
|
||||
|
||||
// Perform URL Search
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace_callback("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $try_oembed_callback, $text);
|
||||
}
|
||||
|
||||
$text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", '[url=$1]$2[/url]', $text);
|
||||
|
||||
// Handle Diaspora posts
|
||||
$text = preg_replace_callback(
|
||||
"&\[url=/?posts/([^\[\]]*)\](.*)\[\/url\]&Usi",
|
||||
function ($match) {
|
||||
return "[url=" . DI::baseUrl() . "/display/" . $match[1] . "]" . $match[2] . "[/url]";
|
||||
}, $text
|
||||
);
|
||||
|
||||
$text = preg_replace_callback(
|
||||
"&\[url=/people\?q\=(.*)\](.*)\[\/url\]&Usi",
|
||||
function ($match) {
|
||||
return "[url=" . DI::baseUrl() . "/search?search=%40" . $match[1] . "]" . $match[2] . "[/url]";
|
||||
}, $text
|
||||
);
|
||||
|
||||
// Server independent link to posts and comments
|
||||
// See issue: https://github.com/diaspora/diaspora_federation/issues/75
|
||||
$expression = "=diaspora://.*?/post/([0-9A-Za-z\-_@.:]{15,254}[0-9A-Za-z])=ism";
|
||||
$text = preg_replace($expression, DI::baseUrl()."/display/$1", $text);
|
||||
|
||||
/* Tag conversion
|
||||
* Supports:
|
||||
* - #[url=<anything>]<term>[/url]
|
||||
* - [url=<anything>]#<term>[/url]
|
||||
*/
|
||||
$text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function($matches) use ($simple_html) {
|
||||
if ($simple_html == BBCode::ACTIVITYPUB) {
|
||||
return '<a href="' . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
|
||||
. '" data-tag="' . XML::escape($matches[1]) . '" rel="tag ugc">#'
|
||||
. XML::escape($matches[1]) . '</a>';
|
||||
} else {
|
||||
return '#<a href="' . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
|
||||
. '" class="tag" rel="tag" title="' . XML::escape($matches[1]) . '">'
|
||||
. XML::escape($matches[1]) . '</a>';
|
||||
}
|
||||
}, $text);
|
||||
|
||||
// We need no target="_blank" rel="noopener noreferrer" for local links
|
||||
// convert links start with DI::baseUrl() as local link without the target="_blank" rel="noopener noreferrer" attribute
|
||||
$escapedBaseUrl = preg_quote(DI::baseUrl(), '/');
|
||||
$text = preg_replace("/\[url\](".$escapedBaseUrl.".*?)\[\/url\]/ism", '<a href="$1">$1</a>', $text);
|
||||
$text = preg_replace("/\[url\=(".$escapedBaseUrl.".*?)\](.*?)\[\/url\]/ism", '<a href="$1">$2</a>', $text);
|
||||
|
||||
$text = preg_replace("/\[url\](.*?)\[\/url\]/ism", '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', $text);
|
||||
$text = preg_replace("/\[url\=(.*?)\](.*?)\[\/url\]/ism", '<a href="$1" target="_blank" rel="noopener noreferrer">$2</a>', $text);
|
||||
|
||||
// Red compatibility, though the link can't be authenticated on Friendica
|
||||
$text = preg_replace("/\[zrl\=(.*?)\](.*?)\[\/zrl\]/ism", '<a href="$1" target="_blank" rel="noopener noreferrer">$2</a>', $text);
|
||||
|
||||
|
||||
// we may need to restrict this further if it picks up too many strays
|
||||
// link acct:user@host to a webfinger profile redirector
|
||||
|
||||
$text = preg_replace('/acct:([^@]+)@((?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63})/', '<a href="' . DI::baseUrl() . '/acctlink?addr=$1@$2" target="extlink">acct:$1@$2</a>', $text);
|
||||
|
||||
// Perform MAIL Search
|
||||
$text = preg_replace("/\[mail\](.*?)\[\/mail\]/", '<a href="mailto:$1">$1</a>', $text);
|
||||
$text = preg_replace("/\[mail\=(.*?)\](.*?)\[\/mail\]/", '<a href="mailto:$1">$2</a>', $text);
|
||||
|
||||
/// @todo What is the meaning of these lines?
|
||||
$text = preg_replace('/\[\&\;([#a-z0-9]+)\;\]/', '&$1;', $text);
|
||||
$text = preg_replace('/\&\#039\;/', '\'', $text);
|
||||
|
||||
// Currently deactivated, it made problems with " inside of alt texts.
|
||||
//$text = preg_replace('/\"\;/', '"', $text);
|
||||
|
||||
// fix any escaped ampersands that may have been converted into links
|
||||
$text = preg_replace('/\<([^>]*?)(src|href)=(.*?)\&\;(.*?)\>/ism', '<$1$2=$3&$4>', $text);
|
||||
|
||||
// sanitizes src attributes (http and redir URLs for displaying in a web page, cid used for inline images in emails)
|
||||
$allowed_src_protocols = ['//', 'http://', 'https://', 'redir/', 'cid:'];
|
||||
|
||||
array_walk($allowed_src_protocols, function(&$value) { $value = preg_quote($value, '#');});
|
||||
|
||||
$text = preg_replace('#<([^>]*?)(src)="(?!' . implode('|', $allowed_src_protocols) . ')(.*?)"(.*?)>#ism',
|
||||
'<$1$2=""$4 data-original-src="$3" class="invalid-src" title="' . DI::l10n()->t('Invalid source protocol') . '">', $text);
|
||||
|
||||
// sanitize href attributes (only allowlisted protocols URLs)
|
||||
// default value for backward compatibility
|
||||
$allowed_link_protocols = DI::config()->get('system', 'allowed_link_protocols', []);
|
||||
|
||||
// Always allowed protocol even if config isn't set or not including it
|
||||
$allowed_link_protocols[] = '//';
|
||||
$allowed_link_protocols[] = 'http://';
|
||||
$allowed_link_protocols[] = 'https://';
|
||||
$allowed_link_protocols[] = 'redir/';
|
||||
|
||||
array_walk($allowed_link_protocols, function(&$value) { $value = preg_quote($value, '#');});
|
||||
|
||||
$regex = '#<([^>]*?)(href)="(?!' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism';
|
||||
$text = preg_replace($regex, '<$1$2="javascript:void(0)"$4 data-original-href="$3" class="invalid-href" title="' . DI::l10n()->t('Invalid link protocol') . '">', $text);
|
||||
|
||||
// Shared content
|
||||
$text = self::convertShare(
|
||||
$text,
|
||||
function (array $attributes, array $author_contact, $content, $is_quote_share) use ($simple_html) {
|
||||
return self::convertShareCallback($attributes, $author_contact, $content, $is_quote_share, $simple_html);
|
||||
}
|
||||
);
|
||||
|
||||
$text = self::interpolateSavedImagesIntoItemBody($text, $saved_image);
|
||||
|
||||
return $text;
|
||||
}); // Escaped noparse, nobb, pre
|
||||
|
||||
// Remove escaping tags and replace new lines that remain
|
||||
$text = preg_replace_callback('/\[(noparse|nobb)](.*?)\[\/\1]/ism', function ($match) {
|
||||
return str_replace("\n", "<br>", $match[2]);
|
||||
}, $text);
|
||||
|
||||
// Additionally, [pre] tags preserve spaces
|
||||
$text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", function ($match) {
|
||||
return str_replace([' ', "\n"], [' ', "<br>"], htmlentities($match[1], ENT_NOQUOTES,'UTF-8'));
|
||||
}, $text);
|
||||
|
||||
return $text;
|
||||
}); // Escaped code
|
||||
|
||||
$text = preg_replace_callback("#\[code(?:=([^\]]*))?\](.*?)\[\/code\]#ism",
|
||||
function ($matches) {
|
||||
if (strpos($matches[2], "\n") !== false) {
|
||||
$return = '<pre><code class="language-' . trim($matches[1]) . '">' . htmlentities(trim($matches[2], "\n\r"), ENT_NOQUOTES, 'UTF-8') . '</code></pre>';
|
||||
} else {
|
||||
$return = '<code>' . htmlentities($matches[2], ENT_NOQUOTES, 'UTF-8') . '</code>';
|
||||
}
|
||||
|
||||
return $return;
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
$config = \HTMLPurifier_HTML5Config::createDefault();
|
||||
$config->set('HTML.Doctype', 'HTML5');
|
||||
$config->set('HTML.SafeIframe', true);
|
||||
$config->set('URI.SafeIframeRegexp', '%^(?:
|
||||
https://www.youtube.com/embed/
|
||||
|
|
||||
https://player.vimeo.com/video/
|
||||
|
|
||||
' . DI::baseUrl() . '/oembed/ # Has to change with the source in Content\Oembed::iframe
|
||||
)%xi');
|
||||
$config->set('Attr.AllowedRel', [
|
||||
'noreferrer' => true,
|
||||
'noopener' => true,
|
||||
]);
|
||||
$config->set('Attr.AllowedFrameTargets', [
|
||||
'_blank' => true,
|
||||
]);
|
||||
|
||||
$HTMLPurifier = new \HTMLPurifier($config);
|
||||
$text = $HTMLPurifier->purify($text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1990,12 +1955,7 @@ class BBCode
|
|||
*/
|
||||
private static function bbCodeMention2DiasporaCallback($match)
|
||||
{
|
||||
$contact = Contact::getDetailsByURL($match[3]);
|
||||
|
||||
if (empty($contact['addr'])) {
|
||||
$contact = Probe::uri($match[3]);
|
||||
}
|
||||
|
||||
$contact = Contact::getByURL($match[3], false, ['addr']);
|
||||
if (empty($contact['addr'])) {
|
||||
return $match[0];
|
||||
}
|
||||
|
|
@ -2040,7 +2000,7 @@ class BBCode
|
|||
|
||||
// Convert it to HTML - don't try oembed
|
||||
if ($for_diaspora) {
|
||||
$text = self::convert($text, false, 3);
|
||||
$text = self::convert($text, false, self::DIASPORA);
|
||||
|
||||
// Add all tags that maybe were removed
|
||||
if (preg_match_all("/#\[url\=([$url_search_string]*)\](.*?)\[\/url\]/ism", $original_text, $tags)) {
|
||||
|
|
@ -2054,7 +2014,7 @@ class BBCode
|
|||
$text = $text . " " . $tagline;
|
||||
}
|
||||
} else {
|
||||
$text = self::convert($text, false, 4);
|
||||
$text = self::convert($text, false, self::CONNECTORS);
|
||||
}
|
||||
|
||||
// If a link is followed by a quote then there should be a newline before it
|
||||
|
|
@ -2066,7 +2026,7 @@ class BBCode
|
|||
// Now convert HTML to Markdown
|
||||
$text = HTML::toMarkdown($text);
|
||||
|
||||
DI::profiler()->saveTimestamp($stamp1, "parser", System::callstack());
|
||||
DI::profiler()->saveTimestamp($stamp1, "parser");
|
||||
|
||||
// Libertree has a problem with escaped hashtags.
|
||||
$text = str_replace(['\#'], ['#'], $text);
|
||||
|
|
@ -2106,63 +2066,152 @@ class BBCode
|
|||
{
|
||||
$ret = [];
|
||||
|
||||
// Convert hashtag links to hashtags
|
||||
$string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2 ', $string);
|
||||
BBCode::performWithEscapedTags($string, ['noparse', 'pre', 'code'], function ($string) use (&$ret) {
|
||||
// Convert hashtag links to hashtags
|
||||
$string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2 ', $string);
|
||||
|
||||
// ignore anything in a code block
|
||||
$string = preg_replace('/\[code.*?\].*?\[\/code\]/sm', '', $string);
|
||||
// Force line feeds at bbtags
|
||||
$string = str_replace(['[', ']'], ["\n[", "]\n"], $string);
|
||||
|
||||
// Force line feeds at bbtags
|
||||
$string = str_replace(['[', ']'], ["\n[", "]\n"], $string);
|
||||
// ignore anything in a bbtag
|
||||
$string = preg_replace('/\[(.*?)\]/sm', '', $string);
|
||||
|
||||
// ignore anything in a bbtag
|
||||
$string = preg_replace('/\[(.*?)\]/sm', '', $string);
|
||||
// Match full names against @tags including the space between first and last
|
||||
// We will look these up afterward to see if they are full names or not recognisable.
|
||||
|
||||
// Match full names against @tags including the space between first and last
|
||||
// We will look these up afterward to see if they are full names or not recognisable.
|
||||
if (preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/', $string, $matches)) {
|
||||
foreach ($matches[1] as $match) {
|
||||
if (strstr($match, ']')) {
|
||||
// we might be inside a bbcode color tag - leave it alone
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/', $string, $matches)) {
|
||||
foreach ($matches[1] as $match) {
|
||||
if (strstr($match, ']')) {
|
||||
// we might be inside a bbcode color tag - leave it alone
|
||||
continue;
|
||||
if (substr($match, -1, 1) === '.') {
|
||||
$ret[] = substr($match, 0, -1);
|
||||
} else {
|
||||
$ret[] = $match;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise pull out single word tags. These can be @nickname, @first_last
|
||||
// and #hash tags.
|
||||
|
||||
if (preg_match_all('/([!#@][^\^ \x0D\x0A,;:?\']*[^\^ \x0D\x0A,;:?!\'.])/', $string, $matches)) {
|
||||
foreach ($matches[1] as $match) {
|
||||
if (strstr($match, ']')) {
|
||||
// we might be inside a bbcode color tag - leave it alone
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore strictly numeric tags like #1
|
||||
if ((strpos($match, '#') === 0) && ctype_digit(substr($match, 1))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// try not to catch url fragments
|
||||
if (strpos($string, $match) && preg_match('/[a-zA-z0-9\/]/', substr($string, strpos($string, $match) - 1, 1))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($match, -1, 1) === '.') {
|
||||
$ret[] = substr($match, 0, -1);
|
||||
} else {
|
||||
$ret[] = $match;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Otherwise pull out single word tags. These can be @nickname, @first_last
|
||||
// and #hash tags.
|
||||
return array_unique($ret);
|
||||
}
|
||||
|
||||
if (preg_match_all('/([!#@][^\^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/', $string, $matches)) {
|
||||
foreach ($matches[1] as $match) {
|
||||
if (strstr($match, ']')) {
|
||||
// we might be inside a bbcode color tag - leave it alone
|
||||
/**
|
||||
* Perform a custom function on a text after having escaped blocks enclosed in the provided tag list.
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $tagList A list of tag names, e.g ['noparse', 'nobb', 'pre']
|
||||
* @param callable $callback
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*@see Strings::performWithEscapedBlocks
|
||||
*
|
||||
*/
|
||||
public static function performWithEscapedTags(string $text, array $tagList, callable $callback)
|
||||
{
|
||||
$tagList = array_map('preg_quote', $tagList);
|
||||
|
||||
return Strings::performWithEscapedBlocks($text, '#\[(?:' . implode('|', $tagList) . ').*?\[/(?:' . implode('|', $tagList) . ')]#ism', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces mentions in the provided message body for the provided user and network if any
|
||||
*
|
||||
* @param $body
|
||||
* @param $profile_uid
|
||||
* @param $network
|
||||
* @return string
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function setMentions($body, $profile_uid = 0, $network = '')
|
||||
{
|
||||
BBCode::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network) {
|
||||
$tags = BBCode::getTags($body);
|
||||
|
||||
$tagged = [];
|
||||
$inform = '';
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$tag_type = substr($tag, 0, 1);
|
||||
|
||||
if ($tag_type == Tag::TAG_CHARACTER[Tag::HASHTAG]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($match, -1, 1) === '.') {
|
||||
$match = substr($match,0,-1);
|
||||
/*
|
||||
* If we already tagged 'Robert Johnson', don't try and tag 'Robert'.
|
||||
* Robert Johnson should be first in the $tags array
|
||||
*/
|
||||
foreach ($tagged as $nextTag) {
|
||||
if (stristr($nextTag, $tag . ' ')) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// ignore strictly numeric tags like #1
|
||||
if ((strpos($match, '#') === 0) && ctype_digit(substr($match, 1))) {
|
||||
continue;
|
||||
}
|
||||
$success = Item::replaceTag($body, $inform, $profile_uid, $tag, $network);
|
||||
|
||||
// try not to catch url fragments
|
||||
if (strpos($string, $match) && preg_match('/[a-zA-z0-9\/]/', substr($string, strpos($string, $match) - 1, 1))) {
|
||||
continue;
|
||||
if ($success['replaced']) {
|
||||
$tagged[] = $tag;
|
||||
}
|
||||
$ret[] = $match;
|
||||
}
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $author Author display name
|
||||
* @param string $profile Author profile URL
|
||||
* @param string $avatar Author profile picture URL
|
||||
* @param string $link Post source URL
|
||||
* @param string $posted Post created date
|
||||
* @param string|null $guid Post guid (if any)
|
||||
* @return string
|
||||
* @TODO Rewrite to handle over whole record array
|
||||
*/
|
||||
public static function getShareOpeningTag(string $author, string $profile, string $avatar, string $link, string $posted, string $guid = null)
|
||||
{
|
||||
$header = "[share author='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $author) .
|
||||
"' profile='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $profile) .
|
||||
"' avatar='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $avatar) .
|
||||
"' link='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $link) .
|
||||
"' posted='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $posted);
|
||||
|
||||
if ($guid) {
|
||||
$header .= "' guid='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $guid);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
$header .= "']";
|
||||
|
||||
return $header;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,37 +26,16 @@ use DOMXPath;
|
|||
use Friendica\Content\Widget\ContactBlock;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Search;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\XML;
|
||||
use League\HTMLToMarkdown\HtmlConverter;
|
||||
|
||||
class HTML
|
||||
{
|
||||
public static function sanitizeCSS($input)
|
||||
{
|
||||
$cleaned = "";
|
||||
|
||||
$input = strtolower($input);
|
||||
|
||||
for ($i = 0; $i < strlen($input); $i++) {
|
||||
$char = substr($input, $i, 1);
|
||||
|
||||
if (($char >= "a") && ($char <= "z")) {
|
||||
$cleaned .= $char;
|
||||
}
|
||||
|
||||
if (!(strpos(" #;:0123456789-_.%", $char) === false)) {
|
||||
$cleaned .= $char;
|
||||
}
|
||||
}
|
||||
|
||||
return $cleaned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search all instances of a specific HTML tag node in the provided DOM document and replaces them with BBCode text nodes.
|
||||
*
|
||||
|
|
@ -166,252 +145,243 @@ class HTML
|
|||
{
|
||||
$message = str_replace("\r", "", $message);
|
||||
|
||||
// Removing code blocks before the whitespace removal processing below
|
||||
$codeblocks = [];
|
||||
$message = Strings::performWithEscapedBlocks($message, '#<pre><code.*</code></pre>#iUs', function ($message) {
|
||||
$message = str_replace(
|
||||
[
|
||||
"<li><p>",
|
||||
"</p></li>",
|
||||
],
|
||||
[
|
||||
"<li>",
|
||||
"</li>",
|
||||
],
|
||||
$message
|
||||
);
|
||||
|
||||
// remove namespaces
|
||||
$message = preg_replace('=<(\w+):(.+?)>=', '<removeme>', $message);
|
||||
$message = preg_replace('=</(\w+):(.+?)>=', '</removeme>', $message);
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc->preserveWhiteSpace = false;
|
||||
|
||||
$message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8");
|
||||
|
||||
@$doc->loadHTML($message, LIBXML_HTML_NODEFDTD);
|
||||
|
||||
XML::deleteNode($doc, 'style');
|
||||
XML::deleteNode($doc, 'head');
|
||||
XML::deleteNode($doc, 'title');
|
||||
XML::deleteNode($doc, 'meta');
|
||||
XML::deleteNode($doc, 'xml');
|
||||
XML::deleteNode($doc, 'removeme');
|
||||
|
||||
$xpath = new DomXPath($doc);
|
||||
$list = $xpath->query("//pre");
|
||||
foreach ($list as $node) {
|
||||
// Ensure to escape unescaped & - they will otherwise raise a warning
|
||||
$safe_value = preg_replace('/&(?!\w+;)/', '&', $node->nodeValue);
|
||||
$node->nodeValue = str_replace("\n", "\r", $safe_value);
|
||||
}
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
$message = str_replace(["\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"], ["<", ">", "<br />", " ", ""], $message);
|
||||
$message = preg_replace('= [\s]*=i', " ", $message);
|
||||
|
||||
if (empty($message)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@$doc->loadHTML($message, LIBXML_HTML_NODEFDTD);
|
||||
|
||||
self::tagToBBCode($doc, 'html', [], "", "");
|
||||
self::tagToBBCode($doc, 'body', [], "", "");
|
||||
|
||||
// Outlook-Quote - Variant 1
|
||||
self::tagToBBCode($doc, 'p', ['class' => 'MsoNormal', 'style' => 'margin-left:35.4pt'], '[quote]', '[/quote]');
|
||||
|
||||
// Outlook-Quote - Variant 2
|
||||
self::tagToBBCode(
|
||||
$doc,
|
||||
'div',
|
||||
['style' => 'border:none;border-left:solid blue 1.5pt;padding:0cm 0cm 0cm 4.0pt'],
|
||||
'[quote]',
|
||||
'[/quote]'
|
||||
);
|
||||
|
||||
// MyBB-Stuff
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'text-decoration: underline;'], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-style: italic;'], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-weight: bold;'], '[b]', '[/b]');
|
||||
|
||||
/* self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[font=$1][size=$2][color=$3]', '[/color][/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[size=$1][color=$2]', '[/color][/size]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(.+)/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'color'=>'/(.+)/'), '[font=$1][color=$3]', '[/color][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/'), '[font=$1]', '[/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/'), '[size=$1]', '[/size]');
|
||||
self::node2BBCode($doc, 'font', array('color'=>'/(.+)/'), '[color=$1]', '[/color]');
|
||||
*/
|
||||
// Untested
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*font-family:\s*(.+?)[,;].*color:\s*(.+?)[,;].*/'), '[size=$1][font=$2][color=$3]', '[/color][/font][/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(\d+)[,;].*/'), '[size=$1]', '[/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*/'), '[size=$1]', '[/size]');
|
||||
|
||||
self::tagToBBCode($doc, 'span', ['style' => '/.*color:\s*(.+?)[,;].*/'], '[color="$1"]', '[/color]');
|
||||
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)pt.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)px.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
// Importing the classes - interesting for importing of posts from third party networks that were exported from friendica
|
||||
// Test
|
||||
//self::node2BBCode($doc, 'span', array('class'=>'/([\w ]+)/'), '[class=$1]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-link'], '[class=type-link]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-video'], '[class=type-video]', '[/class]');
|
||||
|
||||
self::tagToBBCode($doc, 'strong', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'em', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'b', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'i', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'u', [], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 's', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'del', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'strike', [], '[s]', '[/s]');
|
||||
|
||||
self::tagToBBCode($doc, 'big', [], "[size=large]", "[/size]");
|
||||
self::tagToBBCode($doc, 'small', [], "[size=small]", "[/size]");
|
||||
|
||||
self::tagToBBCode($doc, 'blockquote', [], '[quote]', '[/quote]');
|
||||
|
||||
self::tagToBBCode($doc, 'br', [], "\n", '');
|
||||
|
||||
self::tagToBBCode($doc, 'p', ['class' => 'MsoNormal'], "\n", "");
|
||||
self::tagToBBCode($doc, 'div', ['class' => 'MsoNormal'], "\r", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
self::tagToBBCode($doc, 'pre', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'div', [], "\r", "\r");
|
||||
self::tagToBBCode($doc, 'p', [], "\n", "\n");
|
||||
|
||||
self::tagToBBCode($doc, 'ul', [], "[list]", "[/list]");
|
||||
self::tagToBBCode($doc, 'ol', [], "[list=1]", "[/list]");
|
||||
self::tagToBBCode($doc, 'li', [], "[*]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'hr', [], "[hr]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'table', [], "[table]", "[/table]");
|
||||
self::tagToBBCode($doc, 'th', [], "[th]", "[/th]");
|
||||
self::tagToBBCode($doc, 'tr', [], "[tr]", "[/tr]");
|
||||
self::tagToBBCode($doc, 'td', [], "[td]", "[/td]");
|
||||
|
||||
self::tagToBBCode($doc, 'h1', [], "[h1]", "[/h1]");
|
||||
self::tagToBBCode($doc, 'h2', [], "[h2]", "[/h2]");
|
||||
self::tagToBBCode($doc, 'h3', [], "[h3]", "[/h3]");
|
||||
self::tagToBBCode($doc, 'h4', [], "[h4]", "[/h4]");
|
||||
self::tagToBBCode($doc, 'h5', [], "[h5]", "[/h5]");
|
||||
self::tagToBBCode($doc, 'h6', [], "[h6]", "[/h6]");
|
||||
|
||||
self::tagToBBCode($doc, 'a', ['href' => '/mailto:(.+)/'], '[mail=$1]', '[/mail]');
|
||||
self::tagToBBCode($doc, 'a', ['href' => '/(.+)/'], '[url=$1]', '[/url]');
|
||||
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'alt' => '/(.+)/'], '[img=$1]$2', '[/img]', true);
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'], '[img=$2x$3]$1', '[/img]', true);
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], '[img]$1', '[/img]', true);
|
||||
|
||||
|
||||
self::tagToBBCode($doc, 'video', ['src' => '/(.+)/'], '[video]$1', '[/video]', true);
|
||||
self::tagToBBCode($doc, 'audio', ['src' => '/(.+)/'], '[audio]$1', '[/audio]', true);
|
||||
// Backward compatibility, [iframe] support has been removed in version 2020.12
|
||||
self::tagToBBCode($doc, 'iframe', ['src' => '/(.+)/'], '[url]$1', '[/url]', true);
|
||||
|
||||
self::tagToBBCode($doc, 'key', [], '[code]', '[/code]');
|
||||
self::tagToBBCode($doc, 'code', [], '[code]', '[/code]');
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
|
||||
// I'm removing something really disturbing
|
||||
// Don't know exactly what it is
|
||||
$message = str_replace(chr(194) . chr(160), ' ', $message);
|
||||
|
||||
$message = str_replace(" ", " ", $message);
|
||||
|
||||
// removing multiple DIVs
|
||||
$message = preg_replace('=\r *\r=i', "\n", $message);
|
||||
$message = str_replace("\r", "\n", $message);
|
||||
|
||||
Hook::callAll('html2bbcode', $message);
|
||||
|
||||
$message = strip_tags($message);
|
||||
|
||||
$message = html_entity_decode($message, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// remove quotes if they don't make sense
|
||||
$message = preg_replace('=\[/quote\][\s]*\[quote\]=i', "\n", $message);
|
||||
|
||||
$message = preg_replace('=\[quote\]\s*=i', "[quote]", $message);
|
||||
$message = preg_replace('=\s*\[/quote\]=i', "[/quote]", $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n \n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n\n\n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace(
|
||||
[
|
||||
"[/size]\n\n",
|
||||
"\n[hr]",
|
||||
"[hr]\n",
|
||||
"\n[list",
|
||||
"[/list]\n",
|
||||
"\n[/",
|
||||
"[list]\n",
|
||||
"[list=1]\n",
|
||||
"\n[*]"],
|
||||
[
|
||||
"[/size]\n",
|
||||
"[hr]",
|
||||
"[hr]",
|
||||
"[list",
|
||||
"[/list]",
|
||||
"[/",
|
||||
"[list]",
|
||||
"[list=1]",
|
||||
"[*]"],
|
||||
$message
|
||||
);
|
||||
} while ($message != $oldmessage);
|
||||
|
||||
$message = str_replace(
|
||||
['[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'],
|
||||
['[b]', '[/b]', '[i]', '[/i]'],
|
||||
$message
|
||||
);
|
||||
|
||||
// Handling Yahoo style of mails
|
||||
$message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message);
|
||||
|
||||
return $message;
|
||||
});
|
||||
|
||||
$message = preg_replace_callback(
|
||||
'#<pre><code(?: class="language-([^"]*)")?>(.*)</code></pre>#iUs',
|
||||
function ($matches) use (&$codeblocks) {
|
||||
$return = '[codeblock-' . count($codeblocks) . ']';
|
||||
|
||||
function ($matches) {
|
||||
$prefix = '[code]';
|
||||
if ($matches[1] != '') {
|
||||
$prefix = '[code=' . $matches[1] . ']';
|
||||
}
|
||||
|
||||
$codeblocks[] = $prefix . PHP_EOL . trim($matches[2]) . PHP_EOL . '[/code]';
|
||||
return $return;
|
||||
},
|
||||
$message
|
||||
);
|
||||
|
||||
$message = str_replace(
|
||||
[
|
||||
"<li><p>",
|
||||
"</p></li>",
|
||||
],
|
||||
[
|
||||
"<li>",
|
||||
"</li>",
|
||||
],
|
||||
$message
|
||||
);
|
||||
|
||||
// remove namespaces
|
||||
$message = preg_replace('=<(\w+):(.+?)>=', '<removeme>', $message);
|
||||
$message = preg_replace('=</(\w+):(.+?)>=', '</removeme>', $message);
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc->preserveWhiteSpace = false;
|
||||
|
||||
$message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8");
|
||||
|
||||
@$doc->loadHTML($message, LIBXML_HTML_NODEFDTD);
|
||||
|
||||
XML::deleteNode($doc, 'style');
|
||||
XML::deleteNode($doc, 'head');
|
||||
XML::deleteNode($doc, 'title');
|
||||
XML::deleteNode($doc, 'meta');
|
||||
XML::deleteNode($doc, 'xml');
|
||||
XML::deleteNode($doc, 'removeme');
|
||||
|
||||
$xpath = new DomXPath($doc);
|
||||
$list = $xpath->query("//pre");
|
||||
foreach ($list as $node) {
|
||||
// Ensure to escape unescaped & - they will otherwise raise a warning
|
||||
$safe_value = preg_replace('/&(?!\w+;)/', '&', $node->nodeValue);
|
||||
$node->nodeValue = str_replace("\n", "\r", $safe_value);
|
||||
}
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
$message = str_replace(["\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"], ["<", ">", "<br />", " ", ""], $message);
|
||||
$message = preg_replace('= [\s]*=i', " ", $message);
|
||||
|
||||
@$doc->loadHTML($message, LIBXML_HTML_NODEFDTD);
|
||||
|
||||
self::tagToBBCode($doc, 'html', [], "", "");
|
||||
self::tagToBBCode($doc, 'body', [], "", "");
|
||||
|
||||
// Outlook-Quote - Variant 1
|
||||
self::tagToBBCode($doc, 'p', ['class' => 'MsoNormal', 'style' => 'margin-left:35.4pt'], '[quote]', '[/quote]');
|
||||
|
||||
// Outlook-Quote - Variant 2
|
||||
self::tagToBBCode(
|
||||
$doc,
|
||||
'div',
|
||||
['style' => 'border:none;border-left:solid blue 1.5pt;padding:0cm 0cm 0cm 4.0pt'],
|
||||
'[quote]',
|
||||
'[/quote]'
|
||||
);
|
||||
|
||||
// MyBB-Stuff
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'text-decoration: underline;'], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-style: italic;'], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-weight: bold;'], '[b]', '[/b]');
|
||||
|
||||
/* self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[font=$1][size=$2][color=$3]', '[/color][/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[size=$1][color=$2]', '[/color][/size]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(.+)/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'color'=>'/(.+)/'), '[font=$1][color=$3]', '[/color][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/'), '[font=$1]', '[/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/'), '[size=$1]', '[/size]');
|
||||
self::node2BBCode($doc, 'font', array('color'=>'/(.+)/'), '[color=$1]', '[/color]');
|
||||
*/
|
||||
// Untested
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*font-family:\s*(.+?)[,;].*color:\s*(.+?)[,;].*/'), '[size=$1][font=$2][color=$3]', '[/color][/font][/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(\d+)[,;].*/'), '[size=$1]', '[/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*/'), '[size=$1]', '[/size]');
|
||||
|
||||
self::tagToBBCode($doc, 'span', ['style' => '/.*color:\s*(.+?)[,;].*/'], '[color="$1"]', '[/color]');
|
||||
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)pt.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)px.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
// Importing the classes - interesting for importing of posts from third party networks that were exported from friendica
|
||||
// Test
|
||||
//self::node2BBCode($doc, 'span', array('class'=>'/([\w ]+)/'), '[class=$1]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-link'], '[class=type-link]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-video'], '[class=type-video]', '[/class]');
|
||||
|
||||
self::tagToBBCode($doc, 'strong', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'em', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'b', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'i', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'u', [], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 's', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'del', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'strike', [], '[s]', '[/s]');
|
||||
|
||||
self::tagToBBCode($doc, 'big', [], "[size=large]", "[/size]");
|
||||
self::tagToBBCode($doc, 'small', [], "[size=small]", "[/size]");
|
||||
|
||||
self::tagToBBCode($doc, 'blockquote', [], '[quote]', '[/quote]');
|
||||
|
||||
self::tagToBBCode($doc, 'br', [], "\n", '');
|
||||
|
||||
self::tagToBBCode($doc, 'p', ['class' => 'MsoNormal'], "\n", "");
|
||||
self::tagToBBCode($doc, 'div', ['class' => 'MsoNormal'], "\r", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
self::tagToBBCode($doc, 'pre', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'div', [], "\r", "\r");
|
||||
self::tagToBBCode($doc, 'p', [], "\n", "\n");
|
||||
|
||||
self::tagToBBCode($doc, 'ul', [], "[list]", "[/list]");
|
||||
self::tagToBBCode($doc, 'ol', [], "[list=1]", "[/list]");
|
||||
self::tagToBBCode($doc, 'li', [], "[*]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'hr', [], "[hr]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'table', [], "[table]", "[/table]");
|
||||
self::tagToBBCode($doc, 'th', [], "[th]", "[/th]");
|
||||
self::tagToBBCode($doc, 'tr', [], "[tr]", "[/tr]");
|
||||
self::tagToBBCode($doc, 'td', [], "[td]", "[/td]");
|
||||
|
||||
self::tagToBBCode($doc, 'h1', [], "[h1]", "[/h1]");
|
||||
self::tagToBBCode($doc, 'h2', [], "[h2]", "[/h2]");
|
||||
self::tagToBBCode($doc, 'h3', [], "[h3]", "[/h3]");
|
||||
self::tagToBBCode($doc, 'h4', [], "[h4]", "[/h4]");
|
||||
self::tagToBBCode($doc, 'h5', [], "[h5]", "[/h5]");
|
||||
self::tagToBBCode($doc, 'h6', [], "[h6]", "[/h6]");
|
||||
|
||||
self::tagToBBCode($doc, 'a', ['href' => '/mailto:(.+)/'], '[mail=$1]', '[/mail]');
|
||||
self::tagToBBCode($doc, 'a', ['href' => '/(.+)/'], '[url=$1]', '[/url]');
|
||||
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'alt' => '/(.+)/'], '[img=$1]$2', '[/img]', true);
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'], '[img=$2x$3]$1', '[/img]', true);
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], '[img]$1', '[/img]', true);
|
||||
|
||||
|
||||
self::tagToBBCode($doc, 'video', ['src' => '/(.+)/'], '[video]$1', '[/video]', true);
|
||||
self::tagToBBCode($doc, 'audio', ['src' => '/(.+)/'], '[audio]$1', '[/audio]', true);
|
||||
self::tagToBBCode($doc, 'iframe', ['src' => '/(.+)/'], '[iframe]$1', '[/iframe]', true);
|
||||
|
||||
self::tagToBBCode($doc, 'key', [], '[code]', '[/code]');
|
||||
self::tagToBBCode($doc, 'code', [], '[code]', '[/code]');
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
|
||||
// I'm removing something really disturbing
|
||||
// Don't know exactly what it is
|
||||
$message = str_replace(chr(194) . chr(160), ' ', $message);
|
||||
|
||||
$message = str_replace(" ", " ", $message);
|
||||
|
||||
// removing multiple DIVs
|
||||
$message = preg_replace('=\r *\r=i', "\n", $message);
|
||||
$message = str_replace("\r", "\n", $message);
|
||||
|
||||
Hook::callAll('html2bbcode', $message);
|
||||
|
||||
$message = strip_tags($message);
|
||||
|
||||
$message = html_entity_decode($message, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// remove quotes if they don't make sense
|
||||
$message = preg_replace('=\[/quote\][\s]*\[quote\]=i', "\n", $message);
|
||||
|
||||
$message = preg_replace('=\[quote\]\s*=i', "[quote]", $message);
|
||||
$message = preg_replace('=\s*\[/quote\]=i', "[/quote]", $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n \n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n\n\n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace(
|
||||
[
|
||||
"[/size]\n\n",
|
||||
"\n[hr]",
|
||||
"[hr]\n",
|
||||
"\n[list",
|
||||
"[/list]\n",
|
||||
"\n[/",
|
||||
"[list]\n",
|
||||
"[list=1]\n",
|
||||
"\n[*]"],
|
||||
[
|
||||
"[/size]\n",
|
||||
"[hr]",
|
||||
"[hr]",
|
||||
"[list",
|
||||
"[/list]",
|
||||
"[/",
|
||||
"[list]",
|
||||
"[list=1]",
|
||||
"[*]"],
|
||||
$message
|
||||
);
|
||||
} while ($message != $oldmessage);
|
||||
|
||||
$message = str_replace(
|
||||
['[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'],
|
||||
['[b]', '[/b]', '[i]', '[/i]'],
|
||||
$message
|
||||
);
|
||||
|
||||
// Handling Yahoo style of mails
|
||||
$message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message);
|
||||
|
||||
// Restore code blocks
|
||||
$message = preg_replace_callback(
|
||||
'#\[codeblock-([0-9]+)\]#iU',
|
||||
function ($matches) use ($codeblocks) {
|
||||
$return = '';
|
||||
if (isset($codeblocks[intval($matches[1])])) {
|
||||
$return = $codeblocks[$matches[1]];
|
||||
}
|
||||
return $return;
|
||||
return $prefix . PHP_EOL . trim($matches[2]) . PHP_EOL . '[/code]';
|
||||
},
|
||||
$message
|
||||
);
|
||||
|
|
@ -665,6 +635,7 @@ class HTML
|
|||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], ' ', ' ');
|
||||
}
|
||||
|
||||
// Backward compatibility, [iframe] support has been removed in version 2020.12
|
||||
self::tagToBBCode($doc, 'iframe', ['src' => '/(.+)/'], ' $1 ', '');
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
|
|
@ -881,7 +852,7 @@ class HTML
|
|||
'$click' => $contact['click'] ?? '',
|
||||
'$class' => $class,
|
||||
'$url' => $url,
|
||||
'$photo' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB),
|
||||
'$photo' => Contact::getThumb($contact),
|
||||
'$name' => $contact['name'],
|
||||
'title' => $contact['name'] . ' [' . $contact['addr'] . ']',
|
||||
'$parkle' => $sparkle,
|
||||
|
|
@ -917,7 +888,7 @@ class HTML
|
|||
'$save_label' => $save_label,
|
||||
'$search_hint' => DI::l10n()->t('@name, !forum, #tags, content'),
|
||||
'$mode' => $mode,
|
||||
'$return_url' => urlencode('search?q=' . urlencode($s)),
|
||||
'$return_url' => urlencode(Search::getSearchPath($s)),
|
||||
];
|
||||
|
||||
if (!$aside) {
|
||||
|
|
|
|||
|
|
@ -35,20 +35,20 @@ class Markdown
|
|||
* compatibility with Diaspora in spite of the Markdown standard.
|
||||
*
|
||||
* @param string $text
|
||||
* @param bool $hardwrap
|
||||
* @param bool $hardwrap Enables line breaks on \n without two trailing spaces
|
||||
* @param string $baseuri Optional. Prepend anchor links with this URL
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function convert($text, $hardwrap = true) {
|
||||
public static function convert($text, $hardwrap = true, $baseuri = null) {
|
||||
$stamp1 = microtime(true);
|
||||
|
||||
$MarkdownParser = new MarkdownParser();
|
||||
$MarkdownParser->code_class_prefix = 'language-';
|
||||
$MarkdownParser->hard_wrap = $hardwrap;
|
||||
$MarkdownParser->hashtag_protection = true;
|
||||
$MarkdownParser->url_filter_func = function ($url) {
|
||||
if (strpos($url, '#') === 0) {
|
||||
$url = ltrim($_SERVER['REQUEST_URI'], '/') . $url;
|
||||
$MarkdownParser->url_filter_func = function ($url) use ($baseuri) {
|
||||
if (!empty($baseuri) && strpos($url, '#') === 0) {
|
||||
$url = ltrim($baseuri, '/') . $url;
|
||||
}
|
||||
return $url;
|
||||
};
|
||||
|
|
@ -57,7 +57,7 @@ class Markdown
|
|||
|
||||
$html = $MarkdownParser->transform($text);
|
||||
|
||||
DI::profiler()->saveTimestamp($stamp1, "parser", System::callstack());
|
||||
DI::profiler()->saveTimestamp($stamp1, "parser");
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
|
@ -83,7 +83,7 @@ class Markdown
|
|||
return '';
|
||||
}
|
||||
|
||||
$data = Contact::getDetailsByAddr($matches[3]);
|
||||
$data = Contact::getByURL($matches[3]);
|
||||
|
||||
if (empty($data)) {
|
||||
return '';
|
||||
|
|
|
|||
|
|
@ -24,18 +24,13 @@ namespace Friendica\Content;
|
|||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\FileTag;
|
||||
use Friendica\Model\GContact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Temporal;
|
||||
|
||||
class Widget
|
||||
|
|
@ -269,10 +264,6 @@ class Widget
|
|||
return '';
|
||||
}
|
||||
|
||||
if (!Feature::isEnabled(local_user(), 'networks')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$extra_sql = self::unavailableNetworks();
|
||||
|
||||
$r = DBA::p("SELECT DISTINCT(`network`) FROM `contact` WHERE `uid` = ? AND NOT `deleted` AND `network` != '' $extra_sql ORDER BY `network`",
|
||||
|
|
@ -379,80 +370,59 @@ class Widget
|
|||
}
|
||||
|
||||
/**
|
||||
* Return common friends visitor widget
|
||||
* Show a random selection of five common contacts between the visitor and the viewed profile user.
|
||||
*
|
||||
* @param string $profile_uid uid
|
||||
* @param int $uid Viewed profile user ID
|
||||
* @param string $nickname Viewed profile user nickname
|
||||
* @return string|void
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function commonFriendsVisitor($profile_uid)
|
||||
public static function commonFriendsVisitor(int $uid, string $nickname)
|
||||
{
|
||||
if (local_user() == $profile_uid) {
|
||||
return;
|
||||
if (local_user() == $uid) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$zcid = 0;
|
||||
|
||||
$cid = Session::getRemoteContactID($profile_uid);
|
||||
|
||||
if (!$cid) {
|
||||
if (Profile::getMyURL()) {
|
||||
$contact = DBA::selectFirst('contact', ['id'],
|
||||
['nurl' => Strings::normaliseLink(Profile::getMyURL()), 'uid' => $profile_uid]);
|
||||
if (DBA::isResult($contact)) {
|
||||
$cid = $contact['id'];
|
||||
} else {
|
||||
$gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => Strings::normaliseLink(Profile::getMyURL())]);
|
||||
if (DBA::isResult($gcontact)) {
|
||||
$zcid = $gcontact['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$visitorPCid = local_user() ? Contact::getPublicIdByUserId(local_user()) : remote_user();
|
||||
if (!$visitorPCid) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($cid == 0 && $zcid == 0) {
|
||||
return;
|
||||
$localPCid = Contact::getPublicIdByUserId($uid);
|
||||
|
||||
$condition = [
|
||||
'NOT `self` AND NOT `blocked` AND NOT `hidden` AND `id` != ?',
|
||||
$localPCid,
|
||||
];
|
||||
|
||||
$total = Contact\Relation::countCommon($localPCid, $visitorPCid, $condition);
|
||||
if (!$total) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($cid) {
|
||||
$t = GContact::countCommonFriends($profile_uid, $cid);
|
||||
} else {
|
||||
$t = GContact::countCommonFriendsZcid($profile_uid, $zcid);
|
||||
}
|
||||
|
||||
if (!$t) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($cid) {
|
||||
$r = GContact::commonFriends($profile_uid, $cid, 0, 5, true);
|
||||
} else {
|
||||
$r = GContact::commonFriendsZcid($profile_uid, $zcid, 0, 5, true);
|
||||
}
|
||||
|
||||
if (!DBA::isResult($r)) {
|
||||
return;
|
||||
$commonContacts = Contact\Relation::listCommon($localPCid, $visitorPCid, $condition, 0, 5, true);
|
||||
if (!DBA::isResult($commonContacts)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$entries = [];
|
||||
foreach ($r as $rr) {
|
||||
$entry = [
|
||||
'url' => Contact::magicLink($rr['url']),
|
||||
'name' => $rr['name'],
|
||||
'photo' => ProxyUtils::proxifyUrl($rr['photo'], false, ProxyUtils::SIZE_THUMB),
|
||||
foreach ($commonContacts as $contact) {
|
||||
$entries[] = [
|
||||
'url' => Contact::magicLink($contact['url']),
|
||||
'name' => $contact['name'],
|
||||
'photo' => Contact::getThumb($contact),
|
||||
];
|
||||
$entries[] = $entry;
|
||||
}
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('widget/remote_friends_common.tpl');
|
||||
return Renderer::replaceMacros($tpl, [
|
||||
'$desc' => DI::l10n()->tt("%d contact in common", "%d contacts in common", $t),
|
||||
'$desc' => DI::l10n()->tt("%d contact in common", "%d contacts in common", $total),
|
||||
'$base' => DI::baseUrl(),
|
||||
'$uid' => $profile_uid,
|
||||
'$cid' => (($cid) ? $cid : '0'),
|
||||
'$linkmore' => (($t > 5) ? 'true' : ''),
|
||||
'$nickname' => $nickname,
|
||||
'$linkmore' => $total > 5 ? 'true' : '',
|
||||
'$more' => DI::l10n()->t('show more'),
|
||||
'$items' => $entries
|
||||
'$contacts' => $entries
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -475,7 +445,7 @@ class Widget
|
|||
}
|
||||
|
||||
if (Feature::isEnabled($uid, 'tagadelic')) {
|
||||
$owner_id = Contact::getIdForURL($a->profile['url'], 0, true);
|
||||
$owner_id = Contact::getIdForURL($a->profile['url'], 0, false);
|
||||
|
||||
if (!$owner_id) {
|
||||
return '';
|
||||
|
|
@ -497,10 +467,6 @@ class Widget
|
|||
{
|
||||
$o = '';
|
||||
|
||||
if (!Feature::isEnabled($uid, 'archives')) {
|
||||
return $o;
|
||||
}
|
||||
|
||||
$visible_years = DI::pConfig()->get($uid, 'system', 'archive_visible_years', 5);
|
||||
|
||||
/* arrange the list in years */
|
||||
|
|
@ -550,9 +516,31 @@ class Widget
|
|||
'$cutoff' => $cutoff,
|
||||
'$url' => $url,
|
||||
'$dates' => $ret,
|
||||
'$showless' => DI::l10n()->t('show less'),
|
||||
'$showmore' => DI::l10n()->t('show more')
|
||||
]);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the account types sidebar
|
||||
* The account type value is added as a parameter to the url
|
||||
*
|
||||
* @param string $base Basepath
|
||||
* @param int $accounttype Acount type
|
||||
* @return string
|
||||
*/
|
||||
public static function accounttypes(string $base, $accounttype)
|
||||
{
|
||||
$accounts = [
|
||||
['ref' => 'person', 'name' => DI::l10n()->t('Persons')],
|
||||
['ref' => 'organisation', 'name' => DI::l10n()->t('Organisations')],
|
||||
['ref' => 'news', 'name' => DI::l10n()->t('News')],
|
||||
['ref' => 'community', 'name' => DI::l10n()->t('Forums')],
|
||||
];
|
||||
|
||||
return self::filter('accounttype', DI::l10n()->t('Account Types'), '',
|
||||
DI::l10n()->t('All'), $base, $accounts, $accounttype);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,22 +54,6 @@ class CalendarExport
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's a kind of profile page (intval($owner_uid)) return if the user not logged in and
|
||||
* export feature isn't enabled.
|
||||
*/
|
||||
/*
|
||||
* Cal logged in user (test permission at foreign profile page).
|
||||
* If the $owner uid is available we know it is part of one of the profile pages (like /cal).
|
||||
* So we have to test if if it's the own profile page of the logged in user
|
||||
* or a foreign one. For foreign profile pages we need to check if the feature
|
||||
* for exporting the cal is enabled (otherwise the widget would appear for logged in users
|
||||
* on foreigen profile pages even if the widget is disabled).
|
||||
*/
|
||||
if (local_user() != $owner_uid && !Feature::isEnabled($owner_uid, "export_calendar")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// $a->data is only available if the profile page is visited. If the visited page is not part
|
||||
// of the profile page it should be the personal /events page. So we can use $a->user.
|
||||
$user = ($a->data['user']['nickname'] ?? '') ?: $a->user['nickname'];
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class ContactBlock
|
|||
'pending' => false,
|
||||
'hidden' => false,
|
||||
'archive' => false,
|
||||
'failed' => false,
|
||||
'network' => [Protocol::DFRN, Protocol::ACTIVITYPUB, Protocol::OSTATUS, Protocol::DIASPORA, Protocol::FEED],
|
||||
]);
|
||||
|
||||
|
|
@ -98,7 +99,7 @@ class ContactBlock
|
|||
$contact_ids[] = $contact["id"];
|
||||
}
|
||||
|
||||
$contacts_stmt = DBA::select('contact', ['id', 'uid', 'addr', 'url', 'name', 'thumb', 'network'], ['id' => $contact_ids]);
|
||||
$contacts_stmt = DBA::select('contact', ['id', 'uid', 'addr', 'url', 'name', 'thumb', 'avatar', 'network'], ['id' => $contact_ids]);
|
||||
|
||||
if (DBA::isResult($contacts_stmt)) {
|
||||
$contacts_title = DI::l10n()->tt('%d Contact', '%d Contacts', $total);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
namespace Friendica\Content\Widget;
|
||||
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Search;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
|
||||
|
|
@ -35,32 +36,32 @@ class SavedSearches
|
|||
*/
|
||||
public static function getHTML($return_url, $search = '')
|
||||
{
|
||||
$o = '';
|
||||
|
||||
$saved = [];
|
||||
$saved_searches = DBA::select('search', ['id', 'term'], ['uid' => local_user()]);
|
||||
if (DBA::isResult($saved_searches)) {
|
||||
$saved = [];
|
||||
foreach ($saved_searches as $saved_search) {
|
||||
$saved[] = [
|
||||
'id' => $saved_search['id'],
|
||||
'term' => $saved_search['term'],
|
||||
'encodedterm' => urlencode($saved_search['term']),
|
||||
'delete' => DI::l10n()->t('Remove term'),
|
||||
'selected' => $search == $saved_search['term'],
|
||||
];
|
||||
}
|
||||
while ($saved_search = DBA::fetch($saved_searches)) {
|
||||
$saved[] = [
|
||||
'id' => $saved_search['id'],
|
||||
'term' => $saved_search['term'],
|
||||
'encodedterm' => urlencode($saved_search['term']),
|
||||
'searchpath' => Search::getSearchPath($saved_search['term']),
|
||||
'delete' => DI::l10n()->t('Remove term'),
|
||||
'selected' => $search == $saved_search['term'],
|
||||
];
|
||||
}
|
||||
DBA::close($saved_searches);
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('widget/saved_searches.tpl');
|
||||
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$title' => DI::l10n()->t('Saved Searches'),
|
||||
'$add' => '',
|
||||
'$searchbox' => '',
|
||||
'$saved' => $saved,
|
||||
'$return_url' => urlencode($return_url),
|
||||
]);
|
||||
if (empty($saved)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $o;
|
||||
$tpl = Renderer::getMarkupTemplate('widget/saved_searches.tpl');
|
||||
|
||||
return Renderer::replaceMacros($tpl, [
|
||||
'$title' => DI::l10n()->t('Saved Searches'),
|
||||
'$add' => '',
|
||||
'$searchbox' => '',
|
||||
'$saved' => $saved,
|
||||
'$return_url' => urlencode($return_url),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
100
src/Core/ACL.php
|
|
@ -33,75 +33,73 @@ use Friendica\Model\Group;
|
|||
class ACL
|
||||
{
|
||||
/**
|
||||
* Returns a select input tag with all the contact of the local user
|
||||
* Returns a select input tag for private message recipient
|
||||
*
|
||||
* @param string $selname Name attribute of the select input tag
|
||||
* @param string $selclass Class attribute of the select input tag
|
||||
* @param array $preselected Contact IDs that should be already selected
|
||||
* @param int $size Length of the select box
|
||||
* @param int $tabindex Select input tag tabindex attribute
|
||||
* @param int $selected Existing recipien contact ID
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getMessageContactSelectHTML($selname, $selclass, array $preselected = [], $size = 4, $tabindex = null)
|
||||
public static function getMessageContactSelectHTML(int $selected = null)
|
||||
{
|
||||
$a = DI::app();
|
||||
|
||||
$o = '';
|
||||
|
||||
$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'));
|
||||
|
||||
// When used for private messages, we limit correspondence to mutual DFRN/Friendica friends and the selector
|
||||
// to one recipient. By default our selector allows multiple selects amongst all contacts.
|
||||
$sql_extra = sprintf(" AND `rel` = %d ", intval(Contact::FRIEND));
|
||||
$sql_extra .= sprintf(" AND `network` IN ('%s' , '%s') ", Protocol::DFRN, Protocol::DIASPORA);
|
||||
$condition = [
|
||||
'uid' => local_user(),
|
||||
'self' => false,
|
||||
'blocked' => false,
|
||||
'pending' => false,
|
||||
'archive' => false,
|
||||
'deleted' => false,
|
||||
'rel' => [Contact::FOLLOWER, Contact::SHARING, Contact::FRIEND],
|
||||
'network' => Protocol::FEDERATED,
|
||||
];
|
||||
|
||||
$tabindex_attr = !empty($tabindex) ? ' tabindex="' . intval($tabindex) . '"' : '';
|
||||
|
||||
$hidepreselected = '';
|
||||
if ($preselected) {
|
||||
$sql_extra .= " AND `id` IN (" . implode(",", $preselected) . ")";
|
||||
$hidepreselected = ' style="display: none;"';
|
||||
}
|
||||
|
||||
$o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"$size\"$tabindex_attr$hidepreselected>\r\n";
|
||||
|
||||
$stmt = DBA::p("SELECT `id`, `name`, `url`, `network` FROM `contact`
|
||||
WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND NOT `deleted` AND `notify` != ''
|
||||
$sql_extra
|
||||
ORDER BY `name` ASC ", intval(local_user())
|
||||
$contacts = Contact::selectToArray(
|
||||
['id', 'name', 'addr', 'micro'],
|
||||
DBA::mergeConditions($condition, ["`notify` != ''"])
|
||||
);
|
||||
|
||||
$contacts = DBA::toArray($stmt);
|
||||
|
||||
$arr = ['contact' => $contacts, 'entry' => $o];
|
||||
|
||||
// e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
|
||||
Hook::callAll(DI::module()->getName() . '_pre_' . $selname, $arr);
|
||||
Hook::callAll(DI::module()->getName() . '_pre_recipient', $arr);
|
||||
|
||||
$receiverlist = [];
|
||||
$tpl = Renderer::getMarkupTemplate('acl/message_recipient.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$contacts' => $contacts,
|
||||
'$selected' => $selected,
|
||||
]);
|
||||
|
||||
if (DBA::isResult($contacts)) {
|
||||
foreach ($contacts as $contact) {
|
||||
if (in_array($contact['id'], $preselected)) {
|
||||
$selected = ' selected="selected"';
|
||||
} else {
|
||||
$selected = '';
|
||||
}
|
||||
Hook::callAll(DI::module()->getName() . '_post_recipient', $o);
|
||||
|
||||
$trimmed = Protocol::formatMention($contact['url'], $contact['name']);
|
||||
return $o;
|
||||
}
|
||||
|
||||
$receiverlist[] = $trimmed;
|
||||
/**
|
||||
* Returns a minimal ACL block for self-only permissions
|
||||
*
|
||||
* @param int $localUserId
|
||||
* @param string $explanation
|
||||
* @return string
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function getSelfOnlyHTML(int $localUserId, string $explanation)
|
||||
{
|
||||
$selfPublicContactId = Contact::getPublicIdByUserId($localUserId);
|
||||
|
||||
$o .= "<option value=\"{$contact['id']}\"$selected title=\"{$contact['name']}|{$contact['url']}\" >$trimmed</option>\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</select>' . PHP_EOL;
|
||||
|
||||
if ($preselected) {
|
||||
$o .= implode(', ', $receiverlist);
|
||||
}
|
||||
|
||||
Hook::callAll(DI::module()->getName() . '_post_' . $selname, $o);
|
||||
$tpl = Renderer::getMarkupTemplate('acl/self_only.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$selfPublicContactId' => $selfPublicContactId,
|
||||
'$explanation' => $explanation,
|
||||
]);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
|
@ -303,7 +301,7 @@ class ACL
|
|||
'emailcc' => $form_prefix ? $form_prefix . '[emailcc]' : 'emailcc',
|
||||
];
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('acl_selector.tpl');
|
||||
$tpl = Renderer::getMarkupTemplate('acl/full_selector.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$public_title' => DI::l10n()->t('Public'),
|
||||
'$public_desc' => DI::l10n()->t('This content will be shown to all your followers and can be seen in the community pages and by anyone with its link.'),
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ class Addon
|
|||
$func();
|
||||
}
|
||||
|
||||
DBA::delete('hook', ['file' => 'addon/' . $addon . '/' . $addon . '.php']);
|
||||
Hook::delete(['file' => 'addon/' . $addon . '/' . $addon . '.php']);
|
||||
|
||||
unset(self::$addons[array_search($addon, self::$addons)]);
|
||||
}
|
||||
|
|
@ -163,29 +163,21 @@ class Addon
|
|||
if (function_exists($addon . '_install')) {
|
||||
$func = $addon . '_install';
|
||||
$func(DI::app());
|
||||
|
||||
$addon_admin = (function_exists($addon . "_addon_admin") ? 1 : 0);
|
||||
|
||||
DBA::insert('addon', ['name' => $addon, 'installed' => true,
|
||||
'timestamp' => $t, 'plugin_admin' => $addon_admin]);
|
||||
|
||||
// we can add the following with the previous SQL
|
||||
// once most site tables have been updated.
|
||||
// This way the system won't fall over dead during the update.
|
||||
|
||||
if (file_exists('addon/' . $addon . '/.hidden')) {
|
||||
DBA::update('addon', ['hidden' => true], ['name' => $addon]);
|
||||
}
|
||||
|
||||
if (!self::isEnabled($addon)) {
|
||||
self::$addons[] = $addon;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Logger::error("Addon {addon}: {action} failed", ['action' => 'install', 'addon' => $addon]);
|
||||
return false;
|
||||
}
|
||||
|
||||
DBA::insert('addon', [
|
||||
'name' => $addon,
|
||||
'installed' => true,
|
||||
'timestamp' => $t,
|
||||
'plugin_admin' => function_exists($addon . '_addon_admin'),
|
||||
'hidden' => file_exists('addon/' . $addon . '/.hidden')
|
||||
]);
|
||||
|
||||
if (!self::isEnabled($addon)) {
|
||||
self::$addons[] = $addon;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -204,17 +196,9 @@ class Addon
|
|||
}
|
||||
|
||||
Logger::notice("Addon {addon}: {action}", ['action' => 'reload', 'addon' => $addon['name']]);
|
||||
@include_once($fname);
|
||||
|
||||
if (function_exists($addonname . '_uninstall')) {
|
||||
$func = $addonname . '_uninstall';
|
||||
$func(DI::app());
|
||||
}
|
||||
if (function_exists($addonname . '_install')) {
|
||||
$func = $addonname . '_install';
|
||||
$func(DI::app());
|
||||
}
|
||||
DBA::update('addon', ['timestamp' => $t], ['id' => $addon['id']]);
|
||||
self::uninstall($fname);
|
||||
self::install($fname);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,8 +221,6 @@ class Addon
|
|||
*/
|
||||
public static function getInfo($addon)
|
||||
{
|
||||
$a = DI::app();
|
||||
|
||||
$addon = Strings::sanitizeFilePathItem($addon);
|
||||
|
||||
$info = [
|
||||
|
|
@ -256,7 +238,7 @@ class Addon
|
|||
|
||||
$stamp1 = microtime(true);
|
||||
$f = file_get_contents("addon/$addon/$addon.php");
|
||||
DI::profiler()->saveTimestamp($stamp1, "file", System::callstack());
|
||||
DI::profiler()->saveTimestamp($stamp1, "file");
|
||||
|
||||
$r = preg_match("|/\*.*\*/|msU", $f, $m);
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class ProfilerCache implements ICache, IMemoryCache
|
|||
|
||||
$return = $this->cache->getAllKeys($prefix);
|
||||
|
||||
$this->profiler->saveTimestamp($time, 'cache', System::callstack());
|
||||
$this->profiler->saveTimestamp($time, 'cache');
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ class ProfilerCache implements ICache, IMemoryCache
|
|||
|
||||
$return = $this->cache->get($key);
|
||||
|
||||
$this->profiler->saveTimestamp($time, 'cache', System::callstack());
|
||||
$this->profiler->saveTimestamp($time, 'cache');
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ class ProfilerCache implements ICache, IMemoryCache
|
|||
|
||||
$return = $this->cache->set($key, $value, $ttl);
|
||||
|
||||
$this->profiler->saveTimestamp($time, 'cache', System::callstack());
|
||||
$this->profiler->saveTimestamp($time, 'cache');
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ class ProfilerCache implements ICache, IMemoryCache
|
|||
|
||||
$return = $this->cache->delete($key);
|
||||
|
||||
$this->profiler->saveTimestamp($time, 'cache', System::callstack());
|
||||
$this->profiler->saveTimestamp($time, 'cache');
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
|
@ -112,7 +112,7 @@ class ProfilerCache implements ICache, IMemoryCache
|
|||
|
||||
$return = $this->cache->clear($outdated);
|
||||
|
||||
$this->profiler->saveTimestamp($time, 'cache', System::callstack());
|
||||
$this->profiler->saveTimestamp($time, 'cache');
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ class ProfilerCache implements ICache, IMemoryCache
|
|||
|
||||
$return = $this->cache->add($key, $value, $ttl);
|
||||
|
||||
$this->profiler->saveTimestamp($time, 'cache', System::callstack());
|
||||
$this->profiler->saveTimestamp($time, 'cache');
|
||||
|
||||
return $return;
|
||||
} else {
|
||||
|
|
@ -145,7 +145,7 @@ class ProfilerCache implements ICache, IMemoryCache
|
|||
|
||||
$return = $this->cache->compareSet($key, $oldValue, $newValue, $ttl);
|
||||
|
||||
$this->profiler->saveTimestamp($time, 'cache', System::callstack());
|
||||
$this->profiler->saveTimestamp($time, 'cache');
|
||||
|
||||
return $return;
|
||||
} else {
|
||||
|
|
@ -163,7 +163,7 @@ class ProfilerCache implements ICache, IMemoryCache
|
|||
|
||||
$return = $this->cache->compareDelete($key, $value);
|
||||
|
||||
$this->profiler->saveTimestamp($time, 'cache', System::callstack());
|
||||
$this->profiler->saveTimestamp($time, 'cache');
|
||||
|
||||
return $return;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -139,7 +139,9 @@ class RedisCache extends BaseCache implements IMemoryCache
|
|||
public function delete($key)
|
||||
{
|
||||
$cachekey = $this->getCacheKey($key);
|
||||
return ($this->redis->del($cachekey) > 0);
|
||||
$this->redis->del($cachekey);
|
||||
// Redis doesn't have an error state for del()
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -30,11 +30,28 @@ use ParagonIE\HiddenString\HiddenString;
|
|||
*/
|
||||
class Cache
|
||||
{
|
||||
/** @var int Indicates that the cache entry is set by file - Low Priority */
|
||||
const SOURCE_FILE = 0;
|
||||
/** @var int Indicates that the cache entry is set by the DB config table - Middle Priority */
|
||||
const SOURCE_DB = 1;
|
||||
/** @var int Indicates that the cache entry is set by a server environment variable - High Priority */
|
||||
const SOURCE_ENV = 3;
|
||||
/** @var int Indicates that the cache entry is fixed and must not be changed */
|
||||
const SOURCE_FIX = 4;
|
||||
|
||||
/** @var int Default value for a config source */
|
||||
const SOURCE_DEFAULT = self::SOURCE_FILE;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var int[][]
|
||||
*/
|
||||
private $source = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
|
@ -43,11 +60,12 @@ class Cache
|
|||
/**
|
||||
* @param array $config A initial config array
|
||||
* @param bool $hidePasswordOutput True, if cache variables should take extra care of password values
|
||||
* @param int $source Sets a source of the initial config values
|
||||
*/
|
||||
public function __construct(array $config = [], bool $hidePasswordOutput = true)
|
||||
public function __construct(array $config = [], bool $hidePasswordOutput = true, $source = self::SOURCE_DEFAULT)
|
||||
{
|
||||
$this->hidePasswordOutput = $hidePasswordOutput;
|
||||
$this->load($config);
|
||||
$this->load($config, $source);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -55,9 +73,9 @@ class Cache
|
|||
* Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
|
||||
*
|
||||
* @param array $config
|
||||
* @param bool $overwrite Force value overwrite if the config key already exists
|
||||
* @param int $source Indicates the source of the config entry
|
||||
*/
|
||||
public function load(array $config, bool $overwrite = false)
|
||||
public function load(array $config, int $source = self::SOURCE_DEFAULT)
|
||||
{
|
||||
$categories = array_keys($config);
|
||||
|
||||
|
|
@ -68,11 +86,7 @@ class Cache
|
|||
foreach ($keys as $key) {
|
||||
$value = $config[$category][$key];
|
||||
if (isset($value)) {
|
||||
if ($overwrite) {
|
||||
$this->set($category, $key, $value);
|
||||
} else {
|
||||
$this->setDefault($category, $key, $value);
|
||||
}
|
||||
$this->set($category, $key, $value, $source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -91,49 +105,45 @@ class Cache
|
|||
{
|
||||
if (isset($this->config[$cat][$key])) {
|
||||
return $this->config[$cat][$key];
|
||||
} elseif (!isset($key) && isset($this->config[$cat])) {
|
||||
} else if (!isset($key) && isset($this->config[$cat])) {
|
||||
return $this->config[$cat];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a default value in the config cache. Ignores already existing keys.
|
||||
*
|
||||
* @param string $cat Config category
|
||||
* @param string $key Config key
|
||||
* @param mixed $value Default value to set
|
||||
*/
|
||||
private function setDefault(string $cat, string $key, $value)
|
||||
{
|
||||
if (!isset($this->config[$cat][$key])) {
|
||||
$this->set($cat, $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the config cache. Accepts raw output from the config table
|
||||
*
|
||||
* @param string $cat Config category
|
||||
* @param string $key Config key
|
||||
* @param mixed $value Value to set
|
||||
* @param string $cat Config category
|
||||
* @param string $key Config key
|
||||
* @param mixed $value Value to set
|
||||
* @param int $source The source of the current config key
|
||||
*
|
||||
* @return bool True, if the value is set
|
||||
*/
|
||||
public function set(string $cat, string $key, $value)
|
||||
public function set(string $cat, string $key, $value, $source = self::SOURCE_DEFAULT)
|
||||
{
|
||||
if (!isset($this->config[$cat])) {
|
||||
$this->config[$cat] = [];
|
||||
$this->source[$cat] = [];
|
||||
}
|
||||
|
||||
if (isset($this->source[$cat][$key]) &&
|
||||
$source < $this->source[$cat][$key]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->hidePasswordOutput &&
|
||||
$key == 'password' &&
|
||||
is_string($value)) {
|
||||
$key == 'password' &&
|
||||
is_string($value)) {
|
||||
$this->config[$cat][$key] = new HiddenString((string)$value);
|
||||
} else {
|
||||
$this->config[$cat][$key] = $value;
|
||||
}
|
||||
|
||||
$this->source[$cat][$key] = $source;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -149,8 +159,10 @@ class Cache
|
|||
{
|
||||
if (isset($this->config[$cat][$key])) {
|
||||
unset($this->config[$cat][$key]);
|
||||
unset($this->source[$cat][$key]);
|
||||
if (count($this->config[$cat]) == 0) {
|
||||
unset($this->config[$cat]);
|
||||
unset($this->source[$cat]);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class JitConfig extends BaseConfig
|
|||
}
|
||||
|
||||
// load the whole category out of the DB into the cache
|
||||
$this->configCache->load($config, true);
|
||||
$this->configCache->load($config, Cache::SOURCE_DB);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class PreloadConfig extends BaseConfig
|
|||
$this->config_loaded = true;
|
||||
|
||||
// load the whole category out of the DB into the cache
|
||||
$this->configCache->load($config, true);
|
||||
$this->configCache->load($config, Cache::SOURCE_DB);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ Commands:
|
|||
docbloxerrorchecker Check the file tree for DocBlox errors
|
||||
extract Generate translation string file for the Friendica project (deprecated)
|
||||
globalcommunityblock Block remote profile from interacting with this node
|
||||
globalcommunitysilence Silence remote profile from global community page
|
||||
globalcommunitysilence Silence a profile from the global community page
|
||||
archivecontact Archive a contact when you know that it isn't existing anymore
|
||||
help Show help about a command, e.g (bin/console help config)
|
||||
autoinstall Starts automatic installation of friendica based on values from htconfig.php
|
||||
|
|
@ -64,6 +64,7 @@ Commands:
|
|||
postupdate Execute pending post update scripts (can last days)
|
||||
serverblock Manage blocked servers
|
||||
storage Manage storage backend
|
||||
relay Manage ActivityPub relay servers
|
||||
|
||||
Options:
|
||||
-h|--help|-? Show help information
|
||||
|
|
@ -92,6 +93,8 @@ HELP;
|
|||
'postupdate' => Friendica\Console\PostUpdate::class,
|
||||
'serverblock' => Friendica\Console\ServerBlock::class,
|
||||
'storage' => Friendica\Console\Storage::class,
|
||||
'relay' => Friendica\Console\Relay::class,
|
||||
'fixapdeliveryworkertaskparameters' => Friendica\Console\FixAPDeliveryWorkerTaskParameters::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -99,9 +99,7 @@ class Hook
|
|||
return true;
|
||||
}
|
||||
|
||||
$result = DBA::insert('hook', ['hook' => $hook, 'file' => $file, 'function' => $function, 'priority' => $priority]);
|
||||
|
||||
return $result;
|
||||
return self::insert(['hook' => $hook, 'file' => $file, 'function' => $function, 'priority' => $priority]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -119,10 +117,10 @@ class Hook
|
|||
|
||||
// This here is only needed for fixing a problem that existed on the develop branch
|
||||
$condition = ['hook' => $hook, 'file' => $file, 'function' => $function];
|
||||
DBA::delete('hook', $condition);
|
||||
self::delete($condition);
|
||||
|
||||
$condition = ['hook' => $hook, 'file' => $relative_file, 'function' => $function];
|
||||
$result = DBA::delete('hook', $condition);
|
||||
$result = self::delete($condition);
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
@ -220,7 +218,7 @@ class Hook
|
|||
} else {
|
||||
// remove orphan hooks
|
||||
$condition = ['hook' => $name, 'file' => $hook[0], 'function' => $hook[1]];
|
||||
DBA::delete('hook', $condition, ['cascade' => false]);
|
||||
self::delete($condition, ['cascade' => false]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,4 +243,45 @@ class Hook
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes one or more hook records
|
||||
*
|
||||
* We have to clear the cached routerDispatchData because addons can provide routes
|
||||
*
|
||||
* @param array $condition
|
||||
* @param array $options
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function delete(array $condition, array $options = [])
|
||||
{
|
||||
$result = DBA::delete('hook', $condition, $options);
|
||||
|
||||
if ($result) {
|
||||
DI::cache()->delete('routerDispatchData');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a hook record
|
||||
*
|
||||
* We have to clear the cached routerDispatchData because addons can provide routes
|
||||
*
|
||||
* @param array $condition
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function insert(array $condition)
|
||||
{
|
||||
$result = DBA::insert('hook', $condition);
|
||||
|
||||
if ($result) {
|
||||
DI::cache()->delete('routerDispatchData');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ use Friendica\Database\Database;
|
|||
use Friendica\Database\DBStructure;
|
||||
use Friendica\DI;
|
||||
use Friendica\Util\Images;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
/**
|
||||
|
|
@ -197,7 +196,7 @@ class Installer
|
|||
|
||||
if ($result) {
|
||||
$txt = DI::l10n()->t('You may need to import the file "database.sql" manually using phpmyadmin or mysql.') . EOL;
|
||||
$txt .= DI::l10n()->t('Please see the file "INSTALL.txt".');
|
||||
$txt .= DI::l10n()->t('Please see the file "doc/INSTALL.md".');
|
||||
|
||||
$this->addCheck($txt, false, true, htmlentities($result, ENT_COMPAT, 'UTF-8'));
|
||||
|
||||
|
|
@ -259,7 +258,7 @@ class Installer
|
|||
$help = "";
|
||||
if (!$passed) {
|
||||
$help .= DI::l10n()->t('Could not find a command line version of PHP in the web server PATH.') . EOL;
|
||||
$help .= DI::l10n()->t("If you don't have a command line version of PHP installed on your server, you will not be able to run the background processing. See <a href='https://github.com/friendica/friendica/blob/master/doc/Install.md#set-up-the-worker'>'Setup the worker'</a>") . EOL;
|
||||
$help .= DI::l10n()->t("If you don't have a command line version of PHP installed on your server, you will not be able to run the background processing. See <a href='https://github.com/friendica/friendica/blob/stable/doc/Install.md#set-up-the-worker'>'Setup the worker'</a>") . EOL;
|
||||
$help .= EOL . EOL;
|
||||
$tpl = Renderer::getMarkupTemplate('field_input.tpl');
|
||||
/// @todo Separate backend Installer class and presentation layer/view
|
||||
|
|
@ -464,6 +463,13 @@ class Installer
|
|||
);
|
||||
$returnVal = $returnVal ? $status : false;
|
||||
|
||||
$status = $this->checkFunction('proc_open',
|
||||
DI::l10n()->t('Program execution functions'),
|
||||
DI::l10n()->t('Error: Program execution functions required but not enabled.'),
|
||||
true
|
||||
);
|
||||
$returnVal = $returnVal ? $status : false;
|
||||
|
||||
$status = $this->checkFunction('json_encode',
|
||||
DI::l10n()->t('JSON PHP module'),
|
||||
DI::l10n()->t('Error: JSON PHP module required but not installed.'),
|
||||
|
|
@ -548,11 +554,11 @@ class Installer
|
|||
$help = "";
|
||||
$error_msg = "";
|
||||
if (function_exists('curl_init')) {
|
||||
$fetchResult = Network::fetchUrlFull($baseurl . "/install/testrewrite");
|
||||
$fetchResult = DI::httpRequest()->fetchFull($baseurl . "/install/testrewrite");
|
||||
|
||||
$url = Strings::normaliseLink($baseurl . "/install/testrewrite");
|
||||
if ($fetchResult->getReturnCode() != 204) {
|
||||
$fetchResult = Network::fetchUrlFull($url);
|
||||
$fetchResult = DI::httpRequest()->fetchFull($url);
|
||||
}
|
||||
|
||||
if ($fetchResult->getReturnCode() != 204) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ namespace Friendica\Core;
|
|||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Config\IConfig;
|
||||
use Friendica\Model;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
|
|
@ -56,12 +57,59 @@ class Process
|
|||
*/
|
||||
private $basePath;
|
||||
|
||||
public function __construct(LoggerInterface $logger, App\Mode $mode, IConfig $config, string $basepath)
|
||||
/** @var Model\Process */
|
||||
private $processModel;
|
||||
|
||||
/**
|
||||
* The Process ID of this process
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $pid;
|
||||
|
||||
public function __construct(LoggerInterface $logger, App\Mode $mode, IConfig $config, Model\Process $processModel, string $basepath, int $pid)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->mode = $mode;
|
||||
$this->config = $config;
|
||||
$this->logger = $logger;
|
||||
$this->mode = $mode;
|
||||
$this->config = $config;
|
||||
$this->basePath = $basepath;
|
||||
$this->processModel = $processModel;
|
||||
$this->pid = $pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the process id
|
||||
*
|
||||
* @param integer $pid
|
||||
* @return void
|
||||
*/
|
||||
public function setPid(int $pid)
|
||||
{
|
||||
$this->pid = $pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log active processes into the "process" table
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
|
||||
|
||||
$command = basename($trace[0]['file']);
|
||||
|
||||
$this->processModel->deleteInactive();
|
||||
$this->processModel->insert($command, $this->pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the active process from the "process" table
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function end()
|
||||
{
|
||||
return $this->processModel->deleteByPid($this->pid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -142,7 +190,7 @@ class Process
|
|||
$reached = ($free < $min_memory);
|
||||
|
||||
if ($reached) {
|
||||
$this->logger->debug('Minimal memory reached.', ['free' => $free, 'memtotal' => $meminfo['MemTotal'], 'limit' => $min_memory]);
|
||||
$this->logger->warning('Minimal memory reached.', ['free' => $free, 'memtotal' => $meminfo['MemTotal'], 'limit' => $min_memory]);
|
||||
}
|
||||
|
||||
return $reached;
|
||||
|
|
@ -172,7 +220,7 @@ class Process
|
|||
$load = System::currentLoad();
|
||||
if ($load) {
|
||||
if (intval($load) > $maxsysload) {
|
||||
$this->logger->info('system load for process too high.', ['load' => $load, 'process' => $process, 'maxsysload' => $maxsysload]);
|
||||
$this->logger->warning('system load for process too high.', ['load' => $load, 'process' => $process, 'maxsysload' => $maxsysload]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -188,6 +236,7 @@ class Process
|
|||
public function run($command, $args)
|
||||
{
|
||||
if (!function_exists('proc_open')) {
|
||||
$this->logger->warning('"proc_open" not available - quitting');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -205,6 +254,7 @@ class Process
|
|||
}
|
||||
|
||||
if ($this->isMinMemoryReached()) {
|
||||
$this->logger->warning('Memory limit reached - quitting');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -214,9 +264,11 @@ class Process
|
|||
$resource = proc_open($cmdline . ' &', [], $foo, $this->basePath);
|
||||
}
|
||||
if (!is_resource($resource)) {
|
||||
$this->logger->debug('We got no resource for command.', ['cmd' => $cmdline]);
|
||||
$this->logger->warning('We got no resource for command.', ['command' => $cmdline]);
|
||||
return;
|
||||
}
|
||||
proc_close($resource);
|
||||
|
||||
$this->logger->info('Executed "proc_open"', ['command' => $cmdline, 'callstack' => System::callstack(10)]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
namespace Friendica\Core;
|
||||
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\DI;
|
||||
|
||||
/**
|
||||
* Manage compatibility with federated networks
|
||||
|
|
@ -91,7 +91,6 @@ class Protocol
|
|||
* @param string $profile_url
|
||||
* @param array $matches preg_match return array: [0] => Full match [1] => hostname [2] => username
|
||||
* @return string
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function matchByProfileUrl($profile_url, &$matches = [])
|
||||
{
|
||||
|
|
@ -123,7 +122,7 @@ class Protocol
|
|||
if (preg_match('=https?://(.*)/user/(.*)=ism', $profile_url, $matches)) {
|
||||
$statusnet_host = $matches[1];
|
||||
$statusnet_user = $matches[2];
|
||||
$UserData = Network::fetchUrl('http://' . $statusnet_host . '/api/users/show.json?user_id=' . $statusnet_user);
|
||||
$UserData = DI::httpRequest()->fetch('http://' . $statusnet_host . '/api/users/show.json?user_id=' . $statusnet_user);
|
||||
$user = json_decode($UserData);
|
||||
if ($user) {
|
||||
$matches[2] = $user->screen_name;
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ namespace Friendica\Core;
|
|||
|
||||
use Exception;
|
||||
use Friendica\DI;
|
||||
use Friendica\Render\FriendicaSmarty;
|
||||
use Friendica\Render\ITemplateEngine;
|
||||
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||
use Friendica\Render\TemplateEngine;
|
||||
|
||||
/**
|
||||
* This class handles Renderer related functions.
|
||||
|
|
@ -51,7 +51,6 @@ class Renderer
|
|||
'sourcename' => '',
|
||||
'videowidth' => 425,
|
||||
'videoheight' => 350,
|
||||
'force_max_items' => 0,
|
||||
'stylesheet' => '',
|
||||
'template_engine' => 'smarty3',
|
||||
];
|
||||
|
|
@ -66,31 +65,33 @@ class Renderer
|
|||
];
|
||||
|
||||
/**
|
||||
* This is our template processor
|
||||
* Returns the rendered template output from the template string and variables
|
||||
*
|
||||
* @param string|FriendicaSmarty $s The string requiring macro substitution or an instance of FriendicaSmarty
|
||||
* @param array $vars Key value pairs (search => replace)
|
||||
*
|
||||
* @return string substituted string
|
||||
* @throws Exception
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @return string
|
||||
* @throws InternalServerErrorException
|
||||
*/
|
||||
public static function replaceMacros($s, array $vars = [])
|
||||
public static function replaceMacros(string $template, array $vars = [])
|
||||
{
|
||||
$stamp1 = microtime(true);
|
||||
|
||||
// pass $baseurl to all templates if it isn't set
|
||||
$vars = array_merge(['$baseurl' => DI::baseUrl()->get()], $vars);
|
||||
$vars = array_merge(['$baseurl' => DI::baseUrl()->get(), '$APP' => DI::app()], $vars);
|
||||
|
||||
$t = self::getTemplateEngine();
|
||||
|
||||
try {
|
||||
$output = $t->replaceMacros($s, $vars);
|
||||
$output = $t->replaceMacros($template, $vars);
|
||||
} catch (Exception $e) {
|
||||
echo "<pre><b>" . __FUNCTION__ . "</b>: " . $e->getMessage() . "</pre>";
|
||||
exit();
|
||||
DI::logger()->critical($e->getMessage(), ['template' => $template, 'vars' => $vars]);
|
||||
$message = is_site_admin() ?
|
||||
$e->getMessage() :
|
||||
DI::l10n()->t('Friendica can\'t display this page at the moment, please contact the administrator.');
|
||||
throw new InternalServerErrorException($message);
|
||||
}
|
||||
|
||||
DI::profiler()->saveTimestamp($stamp1, "rendering", System::callstack());
|
||||
DI::profiler()->saveTimestamp($stamp1, "rendering");
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
|
@ -98,25 +99,28 @@ class Renderer
|
|||
/**
|
||||
* Load a given template $s
|
||||
*
|
||||
* @param string $s Template to load.
|
||||
* @param string $file Template to load.
|
||||
* @param string $subDir Subdirectory (Optional)
|
||||
*
|
||||
* @return string template.
|
||||
* @throws Exception
|
||||
* @throws InternalServerErrorException
|
||||
*/
|
||||
public static function getMarkupTemplate($s, $subDir = '')
|
||||
public static function getMarkupTemplate($file, $subDir = '')
|
||||
{
|
||||
$stamp1 = microtime(true);
|
||||
$t = self::getTemplateEngine();
|
||||
|
||||
try {
|
||||
$template = $t->getTemplateFile($s, $subDir);
|
||||
$template = $t->getTemplateFile($file, $subDir);
|
||||
} catch (Exception $e) {
|
||||
echo "<pre><b>" . __FUNCTION__ . "</b>: " . $e->getMessage() . "</pre>";
|
||||
exit();
|
||||
DI::logger()->critical($e->getMessage(), ['file' => $file, 'subDir' => $subDir]);
|
||||
$message = is_site_admin() ?
|
||||
$e->getMessage() :
|
||||
DI::l10n()->t('Friendica can\'t display this page at the moment, please contact the administrator.');
|
||||
throw new InternalServerErrorException($message);
|
||||
}
|
||||
|
||||
DI::profiler()->saveTimestamp($stamp1, "file", System::callstack());
|
||||
DI::profiler()->saveTimestamp($stamp1, "file");
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
|
@ -125,18 +129,22 @@ class Renderer
|
|||
* Register template engine class
|
||||
*
|
||||
* @param string $class
|
||||
* @throws InternalServerErrorException
|
||||
*/
|
||||
public static function registerTemplateEngine($class)
|
||||
{
|
||||
$v = get_class_vars($class);
|
||||
|
||||
if (!empty($v['name']))
|
||||
{
|
||||
if (!empty($v['name'])) {
|
||||
$name = $v['name'];
|
||||
self::$template_engines[$name] = $class;
|
||||
} else {
|
||||
echo "template engine <tt>$class</tt> cannot be registered without a name.\n";
|
||||
die();
|
||||
$admin_message = DI::l10n()->t('template engine cannot be registered without a name.');
|
||||
DI::logger()->critical($admin_message, ['class' => $class]);
|
||||
$message = is_site_admin() ?
|
||||
$admin_message :
|
||||
DI::l10n()->t('Friendica can\'t display this page at the moment, please contact the administrator.');
|
||||
throw new InternalServerErrorException($message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +154,8 @@ class Renderer
|
|||
* If $name is not defined, return engine defined by theme,
|
||||
* or default
|
||||
*
|
||||
* @return ITemplateEngine Template Engine instance
|
||||
* @return TemplateEngine Template Engine instance
|
||||
* @throws InternalServerErrorException
|
||||
*/
|
||||
public static function getTemplateEngine()
|
||||
{
|
||||
|
|
@ -156,15 +165,20 @@ class Renderer
|
|||
if (isset(self::$template_engine_instance[$template_engine])) {
|
||||
return self::$template_engine_instance[$template_engine];
|
||||
} else {
|
||||
$a = DI::app();
|
||||
$class = self::$template_engines[$template_engine];
|
||||
$obj = new $class;
|
||||
$obj = new $class($a->getCurrentTheme(), $a->theme_info);
|
||||
self::$template_engine_instance[$template_engine] = $obj;
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
|
||||
echo "template engine <tt>$template_engine</tt> is not registered!\n";
|
||||
exit();
|
||||
$admin_message = DI::l10n()->t('template engine is not registered!');
|
||||
DI::logger()->critical($admin_message, ['template_engine' => $template_engine]);
|
||||
$message = is_site_admin() ?
|
||||
$admin_message :
|
||||
DI::l10n()->t('Friendica can\'t display this page at the moment, please contact the administrator.');
|
||||
throw new InternalServerErrorException($message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,12 +24,9 @@ namespace Friendica\Core;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GContact;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Network\Probe;
|
||||
use Friendica\Object\Search\ContactResult;
|
||||
use Friendica\Object\Search\ResultList;
|
||||
use Friendica\Protocol\PortableContact;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
|
|
@ -64,8 +61,7 @@ class Search
|
|||
if ((filter_var($user, FILTER_VALIDATE_EMAIL) && Network::isEmailDomainValid($user)) ||
|
||||
(substr(Strings::normaliseLink($user), 0, 7) == "http://")) {
|
||||
|
||||
/// @todo Possibly use "getIdForURL" instead?
|
||||
$user_data = Probe::uri($user);
|
||||
$user_data = Contact::getByURL($user);
|
||||
if (empty($user_data)) {
|
||||
return $emptyResultList;
|
||||
}
|
||||
|
|
@ -74,10 +70,7 @@ class Search
|
|||
return $emptyResultList;
|
||||
}
|
||||
|
||||
// Ensure that we do have a contact entry
|
||||
Contact::getIdForURL($user_data['url'] ?? '');
|
||||
|
||||
$contactDetails = Contact::getDetailsByURL($user_data['url'] ?? '', local_user());
|
||||
$contactDetails = Contact::getByURLForUser($user_data['url'] ?? '', local_user());
|
||||
|
||||
$result = new ContactResult(
|
||||
$user_data['name'] ?? '',
|
||||
|
|
@ -87,7 +80,7 @@ class Search
|
|||
$user_data['photo'] ?? '',
|
||||
$user_data['network'] ?? '',
|
||||
$contactDetails['id'] ?? 0,
|
||||
0,
|
||||
$user_data['id'] ?? 0,
|
||||
$user_data['tags'] ?? ''
|
||||
);
|
||||
|
||||
|
|
@ -100,7 +93,7 @@ class Search
|
|||
/**
|
||||
* Search in the global directory for occurrences of the search string
|
||||
*
|
||||
* @see https://github.com/friendica/friendica-directory/blob/master/docs/Protocol.md#search
|
||||
* @see https://github.com/friendica/friendica-directory/blob/stable/docs/Protocol.md#search
|
||||
*
|
||||
* @param string $search
|
||||
* @param int $type specific type of searching
|
||||
|
|
@ -129,7 +122,7 @@ class Search
|
|||
$searchUrl .= '&page=' . $page;
|
||||
}
|
||||
|
||||
$resultJson = Network::fetchUrl($searchUrl, false, 0, 'application/json');
|
||||
$resultJson = DI::httpRequest()->fetch($searchUrl, 0, 'application/json');
|
||||
|
||||
$results = json_decode($resultJson, true);
|
||||
|
||||
|
|
@ -143,7 +136,7 @@ class Search
|
|||
|
||||
foreach ($profiles as $profile) {
|
||||
$profile_url = $profile['url'] ?? '';
|
||||
$contactDetails = Contact::getDetailsByURL($profile_url, local_user());
|
||||
$contactDetails = Contact::getByURLForUser($profile_url, local_user());
|
||||
|
||||
$result = new ContactResult(
|
||||
$profile['name'] ?? '',
|
||||
|
|
@ -176,6 +169,8 @@ class Search
|
|||
*/
|
||||
public static function getContactsFromLocalDirectory($search, $type = self::TYPE_ALL, $start = 0, $itemPage = 80)
|
||||
{
|
||||
Logger::info('Searching', ['search' => $search, 'type' => $type, 'start' => $start, 'itempage' => $itemPage]);
|
||||
|
||||
$config = DI::config();
|
||||
|
||||
$diaspora = $config->get('system', 'diaspora_enabled') ? Protocol::DIASPORA : Protocol::DFRN;
|
||||
|
|
@ -183,18 +178,20 @@ class Search
|
|||
|
||||
$wildcard = Strings::escapeHtml('%' . $search . '%');
|
||||
|
||||
$count = DBA::count('gcontact', [
|
||||
'NOT `hide`
|
||||
$condition = [
|
||||
'NOT `unsearchable`
|
||||
AND `network` IN (?, ?, ?, ?)
|
||||
AND ((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`))
|
||||
AND NOT `failed` AND `uid` = ?
|
||||
AND (`url` LIKE ? OR `name` LIKE ? OR `location` LIKE ?
|
||||
OR `addr` LIKE ? OR `about` LIKE ? OR `keywords` LIKE ?)
|
||||
AND `community` = ?',
|
||||
Protocol::ACTIVITYPUB, Protocol::DFRN, $ostatus, $diaspora,
|
||||
AND `forum` = ?',
|
||||
Protocol::ACTIVITYPUB, Protocol::DFRN, $ostatus, $diaspora, 0,
|
||||
$wildcard, $wildcard, $wildcard,
|
||||
$wildcard, $wildcard, $wildcard,
|
||||
($type === self::TYPE_FORUM),
|
||||
]);
|
||||
];
|
||||
|
||||
$count = DBA::count('contact', $condition);
|
||||
|
||||
$resultList = new ResultList($start, $itemPage, $count);
|
||||
|
||||
|
|
@ -202,18 +199,7 @@ class Search
|
|||
return $resultList;
|
||||
}
|
||||
|
||||
$data = DBA::select('gcontact', ['nurl'], [
|
||||
'NOT `hide`
|
||||
AND `network` IN (?, ?, ?, ?)
|
||||
AND ((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`))
|
||||
AND (`url` LIKE ? OR `name` LIKE ? OR `location` LIKE ?
|
||||
OR `addr` LIKE ? OR `about` LIKE ? OR `keywords` LIKE ?)
|
||||
AND `community` = ?',
|
||||
Protocol::ACTIVITYPUB, Protocol::DFRN, $ostatus, $diaspora,
|
||||
$wildcard, $wildcard, $wildcard,
|
||||
$wildcard, $wildcard, $wildcard,
|
||||
($type === self::TYPE_FORUM),
|
||||
], [
|
||||
$data = DBA::select('contact', [], $condition, [
|
||||
'group_by' => ['nurl', 'updated'],
|
||||
'limit' => [$start, $itemPage],
|
||||
'order' => ['updated' => 'DESC']
|
||||
|
|
@ -223,21 +209,7 @@ class Search
|
|||
return $resultList;
|
||||
}
|
||||
|
||||
while ($row = DBA::fetch($data)) {
|
||||
$urlParts = parse_url($row["nurl"]);
|
||||
|
||||
// Ignore results that look strange.
|
||||
// For historic reasons the gcontact table does contain some garbage.
|
||||
if (!empty($urlParts['query']) || !empty($urlParts['fragment'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$contact = Contact::getDetailsByURL($row["nurl"], local_user());
|
||||
|
||||
if ($contact["name"] == "") {
|
||||
$contact["name"] = end(explode("/", $urlParts["path"]));
|
||||
}
|
||||
|
||||
while ($contact = DBA::fetch($data)) {
|
||||
$result = new ContactResult(
|
||||
$contact["name"],
|
||||
$contact["addr"],
|
||||
|
|
@ -245,8 +217,8 @@ class Search
|
|||
$contact["url"],
|
||||
$contact["photo"],
|
||||
$contact["network"],
|
||||
$contact["cid"],
|
||||
$contact["zid"],
|
||||
$contact["cid"] ?? 0,
|
||||
$contact["zid"] ?? 0,
|
||||
$contact["keywords"]
|
||||
);
|
||||
|
||||
|
|
@ -262,7 +234,7 @@ class Search
|
|||
}
|
||||
|
||||
/**
|
||||
* Searching for global contacts for autocompletion
|
||||
* Searching for contacts for autocompletion
|
||||
*
|
||||
* @param string $search Name or part of a name or nick
|
||||
* @param string $mode Search mode (e.g. "community")
|
||||
|
|
@ -270,8 +242,10 @@ class Search
|
|||
* @return array with the search results
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function searchGlobalContact($search, $mode, int $page = 1)
|
||||
public static function searchContact($search, $mode, int $page = 1)
|
||||
{
|
||||
Logger::info('Searching', ['search' => $search, 'mode' => $mode, 'page' => $page]);
|
||||
|
||||
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -287,10 +261,10 @@ class Search
|
|||
|
||||
// check if searching in the local global contact table is enabled
|
||||
if (DI::config()->get('system', 'poco_local_search')) {
|
||||
$return = GContact::searchByName($search, $mode);
|
||||
$return = Contact::searchByName($search, $mode);
|
||||
} else {
|
||||
$p = $page > 1 ? 'p=' . $page : '';
|
||||
$curlResult = Network::curl(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), false, ['accept_content' => 'application/json']);
|
||||
$curlResult = DI::httpRequest()->get(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), ['accept_content' => 'application/json']);
|
||||
if ($curlResult->isSuccess()) {
|
||||
$searchResult = json_decode($curlResult->getBody(), true);
|
||||
if (!empty($searchResult['profiles'])) {
|
||||
|
|
@ -311,4 +285,19 @@ class Search
|
|||
{
|
||||
return DI::config()->get('system', 'directory', self::DEFAULT_DIRECTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search path (either fulltext search or tag search)
|
||||
*
|
||||
* @param string $search
|
||||
* @return string search path
|
||||
*/
|
||||
public static function getSearchPath(string $search)
|
||||
{
|
||||
if (substr($search, 0, 1) == '#') {
|
||||
return 'search?tag=' . urlencode(substr($search, 1));
|
||||
} else {
|
||||
return 'search?q=' . urlencode($search);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,20 +65,28 @@ class Session
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns contact ID for given user ID
|
||||
* Return the user contact ID of a visitor for the given user ID they are visiting
|
||||
*
|
||||
* @param integer $uid User ID
|
||||
* @return integer Contact ID of visitor for given user ID
|
||||
* @return integer
|
||||
*/
|
||||
public static function getRemoteContactID($uid)
|
||||
{
|
||||
$session = DI::session();
|
||||
|
||||
if (empty($session->get('remote')[$uid])) {
|
||||
return 0;
|
||||
if (!empty($session->get('remote')[$uid])) {
|
||||
$remote = $session->get('remote')[$uid];
|
||||
} else {
|
||||
$remote = 0;
|
||||
}
|
||||
|
||||
return $session->get('remote')[$uid];
|
||||
$local_user = !empty($session->get('authenticated')) ? $session->get('uid') : 0;
|
||||
|
||||
if (empty($remote) && ($local_user != $uid) && !empty($my_address = $session->get('my_address'))) {
|
||||
$remote = Contact::getIdForURL($my_address, $uid, false);
|
||||
}
|
||||
|
||||
return $remote;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -111,7 +119,7 @@ class Session
|
|||
|
||||
$remote_contacts = DBA::select('contact', ['id', 'uid'], ['nurl' => Strings::normaliseLink($session->get('my_url')), 'rel' => [Contact::FOLLOWER, Contact::FRIEND], 'self' => false]);
|
||||
while ($contact = DBA::fetch($remote_contacts)) {
|
||||
if (($contact['uid'] == 0) || Contact::isBlockedByUser($contact['id'], $contact['uid'])) {
|
||||
if (($contact['uid'] == 0) || Contact\User::isBlocked($contact['id'], $contact['uid'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class Cache implements SessionHandlerInterface
|
|||
}
|
||||
|
||||
if (!$session_data) {
|
||||
return true;
|
||||
return $this->destroy($session_id);
|
||||
}
|
||||
|
||||
return $this->cache->set('session:' . $session_id, $session_data, Session::$expire);
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class Database implements SessionHandlerInterface
|
|||
}
|
||||
|
||||
if (!$session_data) {
|
||||
return true;
|
||||
return $this->destroy($session_id);
|
||||
}
|
||||
|
||||
$expire = time() + Session::$expire;
|
||||
|
|
|
|||
|
|
@ -33,32 +33,35 @@ class System
|
|||
/**
|
||||
* Returns a string with a callstack. Can be used for logging.
|
||||
*
|
||||
* @param integer $depth optional, default 4
|
||||
* @param integer $depth How many calls to include in the stacks after filtering
|
||||
* @param int $offset How many calls to shave off the top of the stack, for example if
|
||||
* this is called from a centralized method that isn't relevant to the callstack
|
||||
* @return string
|
||||
*/
|
||||
public static function callstack($depth = 4)
|
||||
public static function callstack(int $depth = 4, int $offset = 0)
|
||||
{
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
|
||||
// We remove the first two items from the list since they contain data that we don't need.
|
||||
array_shift($trace);
|
||||
array_shift($trace);
|
||||
// We remove at least the first two items from the list since they contain data that we don't need.
|
||||
$trace = array_slice($trace, 2 + $offset);
|
||||
|
||||
$callstack = [];
|
||||
$previous = ['class' => '', 'function' => ''];
|
||||
$previous = ['class' => '', 'function' => '', 'database' => false];
|
||||
|
||||
// The ignore list contains all functions that are only wrapper functions
|
||||
$ignore = ['fetchUrl', 'call_user_func_array'];
|
||||
$ignore = ['call_user_func_array'];
|
||||
|
||||
while ($func = array_pop($trace)) {
|
||||
if (!empty($func['class'])) {
|
||||
// Don't show multiple calls from the "dba" class to show the essential parts of the callstack
|
||||
if ((($previous['class'] != $func['class']) || ($func['class'] != 'Friendica\Database\DBA')) && ($previous['function'] != 'q')) {
|
||||
// Don't show multiple calls from the Database classes to show the essential parts of the callstack
|
||||
$func['database'] = in_array($func['class'], ['Friendica\Database\DBA', 'Friendica\Database\Database']);
|
||||
if (!$previous['database'] || !$func['database']) {
|
||||
$classparts = explode("\\", $func['class']);
|
||||
$callstack[] = array_pop($classparts).'::'.$func['function'];
|
||||
$previous = $func;
|
||||
}
|
||||
} elseif (!in_array($func['function'], $ignore)) {
|
||||
$func['database'] = ($func['function'] == 'q');
|
||||
$callstack[] = $func['function'];
|
||||
$func['class'] = '';
|
||||
$previous = $func;
|
||||
|
|
@ -134,12 +137,13 @@ class System
|
|||
* and adds an application/json HTTP header to the output.
|
||||
* After finishing the process is getting killed.
|
||||
*
|
||||
* @param mixed $x The input content.
|
||||
* @param string $content_type Type of the input (Default: 'application/json').
|
||||
* @param mixed $x The input content.
|
||||
* @param string $content_type Type of the input (Default: 'application/json').
|
||||
* @param integer $options JSON options
|
||||
*/
|
||||
public static function jsonExit($x, $content_type = 'application/json') {
|
||||
public static function jsonExit($x, $content_type = 'application/json', int $options = 0) {
|
||||
header("Content-type: $content_type");
|
||||
echo json_encode($x);
|
||||
echo json_encode($x, $options);
|
||||
exit();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class Theme
|
|||
|
||||
$stamp1 = microtime(true);
|
||||
$theme_file = file_get_contents("view/theme/$theme/theme.php");
|
||||
DI::profiler()->saveTimestamp($stamp1, "file", System::callstack());
|
||||
DI::profiler()->saveTimestamp($stamp1, "file");
|
||||
|
||||
$result = preg_match("|/\*.*\*/|msU", $theme_file, $matches);
|
||||
|
||||
|
|
@ -158,6 +158,8 @@ class Theme
|
|||
if (function_exists($func)) {
|
||||
$func();
|
||||
}
|
||||
|
||||
Hook::delete(['file' => "view/theme/$theme/theme.php"]);
|
||||
}
|
||||
|
||||
$allowed_themes = Theme::getAllowedList();
|
||||
|
|
|
|||