mirror of
https://github.com/friendica/friendica
synced 2024-12-23 15:20:17 +00:00
Merge remote-tracking branch 'upstream/develop' into update
This commit is contained in:
commit
4d0e6305dd
148 changed files with 25160 additions and 21008 deletions
63
.drone.yml
Normal file
63
.drone.yml
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: Check messages.po
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Run default Xgettext
|
||||||
|
image: friendicaci/transifex
|
||||||
|
commands:
|
||||||
|
- ./bin/run_xgettext.sh
|
||||||
|
|
||||||
|
- name: Check default
|
||||||
|
image: friendicaci/transifex
|
||||||
|
commands:
|
||||||
|
- /check-messages.sh
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: php7.3-lint
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Test
|
||||||
|
image: php:7.3
|
||||||
|
commands:
|
||||||
|
- ./bin/composer.phar run lint
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: php7.4-lint
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Test
|
||||||
|
image: php:7.4
|
||||||
|
commands:
|
||||||
|
- ./bin/composer.phar run lint
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: php8.0-lint
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Test
|
||||||
|
image: php:8.0
|
||||||
|
commands:
|
||||||
|
- ./bin/composer.phar run lint
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: php-cs check
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install dependencies
|
||||||
|
image: composer
|
||||||
|
commands:
|
||||||
|
- ./bin/composer.phar run cs:install
|
||||||
|
- name: Run coding standards check
|
||||||
|
image: friendicaci/php-cs
|
||||||
|
commands:
|
||||||
|
- export CHANGED_FILES="$(git diff --name-status ${DRONE_COMMIT_BEFORE}..${DRONE_COMMIT_AFTER} | grep ^A | cut -f2)"
|
||||||
|
- /check-php-cs.sh
|
42
.github/workflows/lint.yml
vendored
42
.github/workflows/lint.yml
vendored
|
@ -1,42 +0,0 @@
|
||||||
name: Lint
|
|
||||||
on: pull_request
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
php-linters:
|
|
||||||
name: php${{ matrix.php-versions }} lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
php-versions: ['7.3', '7.4', '8.0']
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Set up php${{ matrix.php-versions }}
|
|
||||||
uses: shivammathur/setup-php@master
|
|
||||||
with:
|
|
||||||
php-version: ${{ matrix.php-versions }}
|
|
||||||
coverage: none
|
|
||||||
- name: Lint
|
|
||||||
run: bin/composer.phar run lint
|
|
||||||
|
|
||||||
php-cs-fixer:
|
|
||||||
name: php-cs check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Get changed files
|
|
||||||
id: changes
|
|
||||||
uses: jitterbit/get-changed-files@v1
|
|
||||||
- name: Set up php
|
|
||||||
uses: shivammathur/setup-php@master
|
|
||||||
with:
|
|
||||||
php-version: 7.2
|
|
||||||
coverage: none
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bin/composer.phar run cs:install
|
|
||||||
- name: Run coding standards check
|
|
||||||
run: |
|
|
||||||
if ! echo "${{ steps.changes.outputs.added }}" | grep -qE "^(\\.php_cs(\\.dist)?|composer\\.lock)$"; then EXTRA_ARGS=$(printf -- '--path-mode=intersection\n--\n%s' "${{ steps.changes.outputs.added }}"); else EXTRA_ARGS=''; fi
|
|
||||||
bin/dev/php-cs-fixer/vendor/bin/php-cs-fixer fix --config=.php_cs.dist -v --diff --diff-format=udiff --dry-run --stop-on-violation --using-cache=no ${EXTRA_ARGS}
|
|
||||||
shell: bash
|
|
67
.github/workflows/releases.yml
vendored
Normal file
67
.github/workflows/releases.yml
vendored
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
name: Generate release packages
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Create release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup PHP, with composer and extensions
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
tools: pecl, composer:v1
|
||||||
|
|
||||||
|
- name: Retrieve version
|
||||||
|
id: release
|
||||||
|
run: echo "::set-output name=version::$(cat VERSION)"
|
||||||
|
|
||||||
|
- 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 --no-dev --optimize-autoloader
|
||||||
|
|
||||||
|
- name: Create artifact
|
||||||
|
id: artifact
|
||||||
|
run: |
|
||||||
|
ARTIFACT="friendica-full-${{ steps.release.outputs.version }}.tar.gz"
|
||||||
|
echo "::set-output name=name::${ARTIFACT}"
|
||||||
|
mkdir build
|
||||||
|
tar -cvjf build/${ARTIFACT} -T mods/release-list.txt
|
||||||
|
|
||||||
|
- name: SHA256
|
||||||
|
id: sha256
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ARTIFACT=${{ steps.artifact.outputs.name }}
|
||||||
|
sha256sum "${ARTIFACT}" > "${ARTIFACT}.sha256"
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: friendica
|
||||||
|
path: build/
|
||||||
|
|
||||||
|
- name: Upload to release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
build/${{ steps.artifact.outputs.name }}.tar.gz
|
||||||
|
build/${{ steps.artifact.outputs.name }}.tar.gz.sha256
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
30
.github/workflows/transifex.yml
vendored
30
.github/workflows/transifex.yml
vendored
|
@ -1,30 +0,0 @@
|
||||||
name: Transifex
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
messages:
|
|
||||||
name: Check messages.po changes
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Install gettext
|
|
||||||
run: sudo apt-get install gettext
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Run Xgettext
|
|
||||||
run: ./bin/run_xgettext.sh
|
|
||||||
|
|
||||||
- name: Check if messages.po needs an update
|
|
||||||
run: |
|
|
||||||
echo "::group::Check messages.po"
|
|
||||||
# Skip first 4 lines in possible diff, because they're header
|
|
||||||
# Skip all lines of the git diff starting with "@@" or comments or starting "POT-Creation-Date"
|
|
||||||
if [[ $(git diff -U0 ./view/lang/C/messages.po | awk '!/@@|-"POT-Creation-Date|+"POT-Creation-Date|-#|+#/{print }' | wc -l) > 4 ]]; then
|
|
||||||
echo "::error file=messages.po::messages.po is out of date"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "Nothing to update"
|
|
||||||
fi
|
|
||||||
echo "::endgroup::"
|
|
||||||
shell: bash
|
|
|
@ -61,7 +61,7 @@ KEYWORDS="-k -kt -ktt:1,2"
|
||||||
echo "Extract strings to $OUTFILE.."
|
echo "Extract strings to $OUTFILE.."
|
||||||
rm "$OUTFILE"; touch "$OUTFILE"
|
rm "$OUTFILE"; touch "$OUTFILE"
|
||||||
|
|
||||||
find_result=$(find "$FINDSTARTDIR" $FINDOPTS -name "*.php" -type f | sort)
|
find_result=$(find "$FINDSTARTDIR" $FINDOPTS -name "*.php" -type f | LC_ALL=C sort --stable)
|
||||||
|
|
||||||
total_files=$(wc -l <<< "${find_result}")
|
total_files=$(wc -l <<< "${find_result}")
|
||||||
|
|
||||||
|
|
10
composer.lock
generated
10
composer.lock
generated
|
@ -889,16 +889,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "level-2/dice",
|
"name": "level-2/dice",
|
||||||
"version": "4.0.2",
|
"version": "4.0.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Level-2/Dice.git",
|
"url": "https://github.com/Level-2/Dice.git",
|
||||||
"reference": "b9336d9200d0165c31e982374dc5d8d2552807bc"
|
"reference": "3e9a8548398c01e2527110c916a93f6efa17ac9c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Level-2/Dice/zipball/b9336d9200d0165c31e982374dc5d8d2552807bc",
|
"url": "https://api.github.com/repos/Level-2/Dice/zipball/3e9a8548398c01e2527110c916a93f6efa17ac9c",
|
||||||
"reference": "b9336d9200d0165c31e982374dc5d8d2552807bc",
|
"reference": "3e9a8548398c01e2527110c916a93f6efa17ac9c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -931,7 +931,7 @@
|
||||||
"di",
|
"di",
|
||||||
"ioc"
|
"ioc"
|
||||||
],
|
],
|
||||||
"time": "2020-01-28T13:47:49+00:00"
|
"time": "2021-04-20T14:06:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "lightopenid/lightopenid",
|
"name": "lightopenid/lightopenid",
|
||||||
|
|
61
database.sql
61
database.sql
|
@ -1,6 +1,6 @@
|
||||||
-- ------------------------------------------
|
-- ------------------------------------------
|
||||||
-- Friendica 2021.06-dev (Siberian Iris)
|
-- Friendica 2021.06-dev (Siberian Iris)
|
||||||
-- DB_UPDATE_VERSION 1414
|
-- DB_UPDATE_VERSION 1417
|
||||||
-- ------------------------------------------
|
-- ------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@ -364,6 +364,43 @@ CREATE TABLE IF NOT EXISTS `apcontact` (
|
||||||
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
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';
|
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='ActivityPub compatible contacts - used in the ActivityPub implementation';
|
||||||
|
|
||||||
|
--
|
||||||
|
-- TABLE application
|
||||||
|
--
|
||||||
|
CREATE TABLE IF NOT EXISTS `application` (
|
||||||
|
`id` int unsigned NOT NULL auto_increment COMMENT 'generated index',
|
||||||
|
`client_id` varchar(64) NOT NULL COMMENT '',
|
||||||
|
`client_secret` varchar(64) NOT NULL COMMENT '',
|
||||||
|
`name` varchar(255) NOT NULL COMMENT '',
|
||||||
|
`redirect_uri` varchar(255) NOT NULL COMMENT '',
|
||||||
|
`website` varchar(255) COMMENT '',
|
||||||
|
`scopes` varchar(255) COMMENT '',
|
||||||
|
`read` boolean COMMENT 'Read scope',
|
||||||
|
`write` boolean COMMENT 'Write scope',
|
||||||
|
`follow` boolean COMMENT 'Follow scope',
|
||||||
|
PRIMARY KEY(`id`),
|
||||||
|
UNIQUE INDEX `client_id` (`client_id`)
|
||||||
|
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth application';
|
||||||
|
|
||||||
|
--
|
||||||
|
-- TABLE application-token
|
||||||
|
--
|
||||||
|
CREATE TABLE IF NOT EXISTS `application-token` (
|
||||||
|
`application-id` int unsigned NOT NULL COMMENT '',
|
||||||
|
`uid` mediumint unsigned NOT NULL COMMENT 'Owner User id',
|
||||||
|
`code` varchar(64) NOT NULL COMMENT '',
|
||||||
|
`access_token` varchar(64) NOT NULL COMMENT '',
|
||||||
|
`created_at` datetime NOT NULL COMMENT 'creation time',
|
||||||
|
`scopes` varchar(255) COMMENT '',
|
||||||
|
`read` boolean COMMENT 'Read scope',
|
||||||
|
`write` boolean COMMENT 'Write scope',
|
||||||
|
`follow` boolean COMMENT 'Follow scope',
|
||||||
|
PRIMARY KEY(`application-id`,`uid`),
|
||||||
|
INDEX `uid_id` (`uid`,`application-id`),
|
||||||
|
FOREIGN KEY (`application-id`) REFERENCES `application` (`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 user token';
|
||||||
|
|
||||||
--
|
--
|
||||||
-- TABLE attach
|
-- TABLE attach
|
||||||
--
|
--
|
||||||
|
@ -1470,6 +1507,28 @@ CREATE TABLE IF NOT EXISTS `workerqueue` (
|
||||||
INDEX `done_pid_priority_created` (`done`,`pid`,`priority`,`created`)
|
INDEX `done_pid_priority_created` (`done`,`pid`,`priority`,`created`)
|
||||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Background tasks queue entries';
|
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Background tasks queue entries';
|
||||||
|
|
||||||
|
--
|
||||||
|
-- VIEW application-view
|
||||||
|
--
|
||||||
|
DROP VIEW IF EXISTS `application-view`;
|
||||||
|
CREATE VIEW `application-view` AS SELECT
|
||||||
|
`application`.`id` AS `id`,
|
||||||
|
`application-token`.`uid` AS `uid`,
|
||||||
|
`application`.`name` AS `name`,
|
||||||
|
`application`.`redirect_uri` AS `redirect_uri`,
|
||||||
|
`application`.`website` AS `website`,
|
||||||
|
`application`.`client_id` AS `client_id`,
|
||||||
|
`application`.`client_secret` AS `client_secret`,
|
||||||
|
`application-token`.`code` AS `code`,
|
||||||
|
`application-token`.`access_token` AS `access_token`,
|
||||||
|
`application-token`.`created_at` AS `created_at`,
|
||||||
|
`application-token`.`scopes` AS `scopes`,
|
||||||
|
`application-token`.`read` AS `read`,
|
||||||
|
`application-token`.`write` AS `write`,
|
||||||
|
`application-token`.`follow` AS `follow`
|
||||||
|
FROM `application-token`
|
||||||
|
INNER JOIN `application` ON `application-token`.`application-id` = `application`.`id`;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- VIEW post-user-view
|
-- VIEW post-user-view
|
||||||
--
|
--
|
||||||
|
|
|
@ -9,19 +9,43 @@ Friendica provides the following endpoints defined in [the official Mastodon API
|
||||||
|
|
||||||
Authentication is the same as described in [Using the APIs](help/api#Authentication).
|
Authentication is the same as described in [Using the APIs](help/api#Authentication).
|
||||||
|
|
||||||
|
## Clients
|
||||||
|
|
||||||
|
Supported mobile apps:
|
||||||
|
|
||||||
|
- Tusky
|
||||||
|
- Husky
|
||||||
|
- twitlatte
|
||||||
|
- AndStatus
|
||||||
|
- Twidere
|
||||||
|
- Subway Tooter
|
||||||
|
|
||||||
|
Unsupported mobile apps:
|
||||||
|
|
||||||
|
- [Mammut](https://github.com/jamiesanson/Mammut) There are problems with the token request, see issue https://github.com/jamiesanson/Mammut/issues/19
|
||||||
|
- [Fedilab](https://framagit.org/tom79/fedilab) Automatically uses the legacy API, see issue: https://framagit.org/tom79/fedilab/-/issues/520
|
||||||
|
|
||||||
## Entities
|
## Entities
|
||||||
|
|
||||||
These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/entities/).
|
These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/entities/).
|
||||||
|
|
||||||
## Implemented endpoints
|
## Implemented endpoints
|
||||||
|
|
||||||
- [`GET /api/v1//accounts/:id`](https://docs.joinmastodon.org/methods/accounts/#retrieve-information)
|
- [`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/accounts/:id/statuses`](https://docs.joinmastodon.org/methods/accounts/#retrieve-information)
|
||||||
|
- [`GET /api/v1/accounts/:id/followers`](https://docs.joinmastodon.org/methods/accounts/)
|
||||||
|
- [`GET /api/v1/accounts/:id/following`](https://docs.joinmastodon.org/methods/accounts/)
|
||||||
|
- [`GET /api/v1/accounts/:id/lists`](https://docs.joinmastodon.org/methods/accounts/)
|
||||||
|
- [`GET /api/v1/accounts/search`](https://docs.joinmastodon.org/methods/accounts)
|
||||||
|
- [`GET /api/v1/accounts/verify_credentials`](https://docs.joinmastodon.org/methods/accounts)
|
||||||
|
- [`GET /api/v1/blocks`](https://docs.joinmastodon.org/methods/accounts/blocks/)
|
||||||
|
- [`GET /api/v1/bookmarks`](https://docs.joinmastodon.org/methods/accounts/bookmarks/)
|
||||||
- [`GET /api/v1/custom_emojis`](https://docs.joinmastodon.org/methods/instance/custom_emojis/)
|
- [`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
|
- 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/directory`](https://docs.joinmastodon.org/methods/instance/directory/)
|
||||||
|
- [`GET /api/v1/favourites`](https://docs.joinmastodon.org/methods/accounts/favourites/)
|
||||||
- [`GET /api/v1/follow_requests`](https://docs.joinmastodon.org/methods/accounts/follow_requests#pending-follows)
|
- [`GET /api/v1/follow_requests`](https://docs.joinmastodon.org/methods/accounts/follow_requests#pending-follows)
|
||||||
- Returned IDs are specific to follow requests
|
- Returned IDs are specific to follow requests
|
||||||
- [`POST /api/v1/follow_requests/:id/authorize`](https://docs.joinmastodon.org/methods/accounts/follow_requests#accept-follow)
|
- [`POST /api/v1/follow_requests/:id/authorize`](https://docs.joinmastodon.org/methods/accounts/follow_requests#accept-follow)
|
||||||
|
@ -36,7 +60,23 @@ 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`](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/instance/peers`](https://docs.joinmastodon.org/methods/instance#list-of-connected-domains)
|
||||||
|
- [`GET /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/)
|
||||||
|
- [`GET /api/v1/lists/:id`](https://docs.joinmastodon.org/methods/timelines/lists/)
|
||||||
|
- [`GET /api/v1/lists/:id/accounts`](https://docs.joinmastodon.org/methods/timelines/lists/)
|
||||||
|
- [`GET /api/v1/media/:id`](https://docs.joinmastodon.org/methods/statuses/media/)
|
||||||
|
- [`GET /api/v1/mutes`](https://docs.joinmastodon.org/methods/accounts/mutes/)
|
||||||
|
- [`GET /api/v1/notifications`](https://docs.joinmastodon.org/methods/notifications/)
|
||||||
|
- [`GET /api/v1/notifications/:id`](https://docs.joinmastodon.org/methods/notifications/)
|
||||||
|
- [`GET /api/v1/preferences`](https://docs.joinmastodon.org/methods/accounts/preferences/)
|
||||||
|
- [`GET /api/v1/statuses/:id`](https://docs.joinmastodon.org/methods/statuses/)
|
||||||
|
- [`GET /api/v1/statuses/:id/context`](https://docs.joinmastodon.org/methods/statuses/)
|
||||||
|
- [`GET /api/v1/statuses/:id/reblogged_by`](https://docs.joinmastodon.org/methods/statuses/)
|
||||||
|
- [`GET /api/v1/statuses/:id/favourited_by`](https://docs.joinmastodon.org/methods/statuses/)
|
||||||
|
- [`GET /api/v1/suggestions`](https://docs.joinmastodon.org/methods/accounts/suggestions/)
|
||||||
|
- [`GET /api/v1/timelines/home`](https://docs.joinmastodon.org/methods/timelines/)
|
||||||
|
- [`GET /api/v1/timelines/list/:id`](https://docs.joinmastodon.org/methods/timelines/)
|
||||||
- [`GET /api/v1/timelines/public`](https://docs.joinmastodon.org/methods/timelines/)
|
- [`GET /api/v1/timelines/public`](https://docs.joinmastodon.org/methods/timelines/)
|
||||||
|
- [`GET /api/v1/timelines/tag/:hashtag`](https://docs.joinmastodon.org/methods/timelines/)
|
||||||
- [`GET /api/v1/trends`](https://docs.joinmastodon.org/methods/instance/trends/)
|
- [`GET /api/v1/trends`](https://docs.joinmastodon.org/methods/instance/trends/)
|
||||||
|
|
||||||
## Non-implemented endpoints
|
## Non-implemented endpoints
|
||||||
|
|
101
include/api.php
101
include/api.php
|
@ -175,6 +175,7 @@ function api_register_func($path, $func, $auth = false, $method = API_METHOD_ANY
|
||||||
* Simple Auth allow username in form of <pre>user@server</pre>, ignoring server part
|
* Simple Auth allow username in form of <pre>user@server</pre>, ignoring server part
|
||||||
*
|
*
|
||||||
* @param App $a App
|
* @param App $a App
|
||||||
|
* @param bool $do_login try to log in when not logged in, otherwise quit silently
|
||||||
* @throws ForbiddenException
|
* @throws ForbiddenException
|
||||||
* @throws InternalServerErrorException
|
* @throws InternalServerErrorException
|
||||||
* @throws UnauthorizedException
|
* @throws UnauthorizedException
|
||||||
|
@ -185,8 +186,10 @@ function api_register_func($path, $func, $auth = false, $method = API_METHOD_ANY
|
||||||
* 'authenticated' => return status,
|
* 'authenticated' => return status,
|
||||||
* 'user_record' => return authenticated user record
|
* 'user_record' => return authenticated user record
|
||||||
*/
|
*/
|
||||||
function api_login(App $a)
|
function api_login(App $a, bool $do_login = true)
|
||||||
{
|
{
|
||||||
|
$_SESSION["allow_api"] = false;
|
||||||
|
|
||||||
// workaround for HTTP-auth in CGI mode
|
// workaround for HTTP-auth in CGI mode
|
||||||
if (!empty($_SERVER['REDIRECT_REMOTE_USER'])) {
|
if (!empty($_SERVER['REDIRECT_REMOTE_USER'])) {
|
||||||
$userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6));
|
$userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6));
|
||||||
|
@ -216,6 +219,10 @@ function api_login(App $a)
|
||||||
Logger::warning(API_LOG_PREFIX . 'OAuth error', ['module' => 'api', 'action' => 'login', 'exception' => $e->getMessage()]);
|
Logger::warning(API_LOG_PREFIX . 'OAuth error', ['module' => 'api', 'action' => 'login', 'exception' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$do_login) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Logger::debug(API_LOG_PREFIX . 'failed', ['module' => 'api', 'action' => 'login', 'parameters' => $_SERVER]);
|
Logger::debug(API_LOG_PREFIX . 'failed', ['module' => 'api', 'action' => 'login', 'parameters' => $_SERVER]);
|
||||||
header('WWW-Authenticate: Basic realm="Friendica"');
|
header('WWW-Authenticate: Basic realm="Friendica"');
|
||||||
throw new UnauthorizedException("This API requires login");
|
throw new UnauthorizedException("This API requires login");
|
||||||
|
@ -247,7 +254,7 @@ function api_login(App $a)
|
||||||
*/
|
*/
|
||||||
Hook::callAll('authenticate', $addon_auth);
|
Hook::callAll('authenticate', $addon_auth);
|
||||||
|
|
||||||
if ($addon_auth['authenticated'] && count($addon_auth['user_record'])) {
|
if ($addon_auth['authenticated'] && !empty($addon_auth['user_record'])) {
|
||||||
$record = $addon_auth['user_record'];
|
$record = $addon_auth['user_record'];
|
||||||
} else {
|
} else {
|
||||||
$user_id = User::authenticate(trim($user), trim($password), true);
|
$user_id = User::authenticate(trim($user), trim($password), true);
|
||||||
|
@ -257,6 +264,9 @@ function api_login(App $a)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DBA::isResult($record)) {
|
if (!DBA::isResult($record)) {
|
||||||
|
if (!$do_login) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Logger::debug(API_LOG_PREFIX . 'failed', ['module' => 'api', 'action' => 'login', 'parameters' => $_SERVER]);
|
Logger::debug(API_LOG_PREFIX . 'failed', ['module' => 'api', 'action' => 'login', 'parameters' => $_SERVER]);
|
||||||
header('WWW-Authenticate: Basic realm="Friendica"');
|
header('WWW-Authenticate: Basic realm="Friendica"');
|
||||||
//header('HTTP/1.0 401 Unauthorized');
|
//header('HTTP/1.0 401 Unauthorized');
|
||||||
|
@ -1021,7 +1031,7 @@ function api_statuses_mediap($type)
|
||||||
|
|
||||||
$_REQUEST['profile_uid'] = api_user();
|
$_REQUEST['profile_uid'] = api_user();
|
||||||
$_REQUEST['api_source'] = true;
|
$_REQUEST['api_source'] = true;
|
||||||
$txt = requestdata('status');
|
$txt = requestdata('status') ?? '';
|
||||||
/// @TODO old-lost code?
|
/// @TODO old-lost code?
|
||||||
//$txt = urldecode(requestdata('status'));
|
//$txt = urldecode(requestdata('status'));
|
||||||
|
|
||||||
|
@ -1076,7 +1086,7 @@ function api_statuses_update($type)
|
||||||
|
|
||||||
// convert $_POST array items to the form we use for web posts.
|
// convert $_POST array items to the form we use for web posts.
|
||||||
if (requestdata('htmlstatus')) {
|
if (requestdata('htmlstatus')) {
|
||||||
$txt = requestdata('htmlstatus');
|
$txt = requestdata('htmlstatus') ?? '';
|
||||||
if ((strpos($txt, '<') !== false) || (strpos($txt, '>') !== false)) {
|
if ((strpos($txt, '<') !== false) || (strpos($txt, '>') !== false)) {
|
||||||
$txt = HTML::toBBCodeVideo($txt);
|
$txt = HTML::toBBCodeVideo($txt);
|
||||||
|
|
||||||
|
@ -1157,30 +1167,56 @@ function api_statuses_update($type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($_FILES['media'])) {
|
if (requestdata('media_ids')) {
|
||||||
|
$ids = explode(',', requestdata('media_ids') ?? '');
|
||||||
|
} elseif (!empty($_FILES['media'])) {
|
||||||
// upload the image if we have one
|
// upload the image if we have one
|
||||||
$picture = wall_upload_post($a, false);
|
$picture = wall_upload_post($a, false);
|
||||||
if (is_array($picture)) {
|
if (is_array($picture)) {
|
||||||
$_REQUEST['body'] .= "\n\n" . '[url=' . $picture["albumpage"] . '][img]' . $picture["preview"] . "[/img][/url]";
|
$ids[] = $picture['id'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestdata('media_ids')) {
|
$attachments = [];
|
||||||
$ids = explode(',', requestdata('media_ids'));
|
$ressources = [];
|
||||||
|
|
||||||
|
if (!empty($ids)) {
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
$r = q(
|
$media = DBA::toArray(DBA::p("SELECT `resource-id`, `scale`, `nickname`, `type`, `desc`, `filename`, `datasize`, `width`, `height` FROM `photo`
|
||||||
"SELECT `resource-id`, `scale`, `nickname`, `type`, `desc` FROM `photo` INNER JOIN `user` ON `user`.`uid` = `photo`.`uid` WHERE `resource-id` IN (SELECT `resource-id` FROM `photo` WHERE `id` = %d) AND `scale` > 0 AND `photo`.`uid` = %d ORDER BY `photo`.`width` DESC LIMIT 1",
|
INNER JOIN `user` ON `user`.`uid` = `photo`.`uid` WHERE `resource-id` IN
|
||||||
intval($id),
|
(SELECT `resource-id` FROM `photo` WHERE `id` = ?) AND `photo`.`uid` = ?
|
||||||
api_user()
|
ORDER BY `photo`.`width` DESC LIMIT 2", $id, api_user()));
|
||||||
);
|
|
||||||
if (DBA::isResult($r)) {
|
if (!empty($media)) {
|
||||||
|
$ressources[] = $media[0]['resource-id'];
|
||||||
$phototypes = Images::supportedTypes();
|
$phototypes = Images::supportedTypes();
|
||||||
$ext = $phototypes[$r[0]['type']];
|
$ext = $phototypes[$media[0]['type']];
|
||||||
$description = $r[0]['desc'] ?? '';
|
|
||||||
$_REQUEST['body'] .= "\n\n" . '[url=' . DI::baseUrl() . '/photos/' . $r[0]['nickname'] . '/image/' . $r[0]['resource-id'] . ']';
|
$attachment = ['type' => Post\Media::IMAGE, 'mimetype' => $media[0]['type'],
|
||||||
$_REQUEST['body'] .= '[img=' . DI::baseUrl() . '/photo/' . $r[0]['resource-id'] . '-' . $r[0]['scale'] . '.' . $ext . ']' . $description . '[/img][/url]';
|
'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . '.' . $ext,
|
||||||
|
'size' => $media[0]['datasize'],
|
||||||
|
'name' => $media[0]['filename'] ?: $media[0]['resource-id'],
|
||||||
|
'description' => $media[0]['desc'] ?? '',
|
||||||
|
'width' => $media[0]['width'],
|
||||||
|
'height' => $media[0]['height']];
|
||||||
|
|
||||||
|
if (count($media) > 1) {
|
||||||
|
$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . '.' . $ext;
|
||||||
|
$attachment['preview-width'] = $media[1]['width'];
|
||||||
|
$attachment['preview-height'] = $media[1]['height'];
|
||||||
|
}
|
||||||
|
$attachments[] = $attachment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have to avoid that the post is rejected because of an empty body
|
||||||
|
if (empty($_REQUEST['body'])) {
|
||||||
|
$_REQUEST['body'] = '[hr]';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($attachments)) {
|
||||||
|
$_REQUEST['attachments'] = $attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set this so that the item_post() function is quiet and doesn't redirect or emit json
|
// set this so that the item_post() function is quiet and doesn't redirect or emit json
|
||||||
|
@ -1194,6 +1230,13 @@ function api_statuses_update($type)
|
||||||
// call out normal post function
|
// call out normal post function
|
||||||
$item_id = item_post($a);
|
$item_id = item_post($a);
|
||||||
|
|
||||||
|
if (!empty($ressources) && !empty($item_id)) {
|
||||||
|
$item = Post::selectFirst(['uri-id', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'], ['id' => $item_id]);
|
||||||
|
foreach ($ressources as $ressource) {
|
||||||
|
Photo::setPermissionForRessource($ressource, api_user(), $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// output the post that we just posted.
|
// output the post that we just posted.
|
||||||
return api_status_show($type, $item_id);
|
return api_status_show($type, $item_id);
|
||||||
}
|
}
|
||||||
|
@ -2534,7 +2577,7 @@ function api_convert_item($item)
|
||||||
$statustext = mb_substr($statustext, 0, 1000) . "... \n" . ($item['plink'] ?? '');
|
$statustext = mb_substr($statustext, 0, 1000) . "... \n" . ($item['plink'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
$statushtml = BBCode::convert(BBCode::removeAttachment($body), false);
|
$statushtml = BBCode::convert(BBCode::removeAttachment($body), false, BBCode::API, true);
|
||||||
|
|
||||||
// Workaround for clients with limited HTML parser functionality
|
// Workaround for clients with limited HTML parser functionality
|
||||||
$search = ["<br>", "<blockquote>", "</blockquote>",
|
$search = ["<br>", "<blockquote>", "</blockquote>",
|
||||||
|
@ -2585,25 +2628,7 @@ function api_convert_item($item)
|
||||||
*/
|
*/
|
||||||
function api_add_attachments_to_body(array $item)
|
function api_add_attachments_to_body(array $item)
|
||||||
{
|
{
|
||||||
$body = $item['body'];
|
$body = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
|
||||||
|
|
||||||
foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::IMAGE, Post\Media::AUDIO, Post\Media::VIDEO]) as $media) {
|
|
||||||
if (Item::containsLink($item['body'], $media['url'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($media['type'] == Post\Media::IMAGE) {
|
|
||||||
if (!empty($media['description'])) {
|
|
||||||
$body .= "\n[img=" . $media['url'] . ']' . $media['description'] .'[/img]';
|
|
||||||
} else {
|
|
||||||
$body .= "\n[img]" . $media['url'] .'[/img]';
|
|
||||||
}
|
|
||||||
} elseif ($media['type'] == Post\Media::AUDIO) {
|
|
||||||
$body .= "\n[audio]" . $media['url'] . "[/audio]\n";
|
|
||||||
} elseif ($media['type'] == Post\Media::VIDEO) {
|
|
||||||
$body .= "\n[video]" . $media['url'] . "[/video]\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos($body, '[/img]') !== false) {
|
if (strpos($body, '[/img]') !== false) {
|
||||||
return $body;
|
return $body;
|
||||||
|
|
|
@ -888,7 +888,8 @@ function conversation_fetch_items(array $parent, array $items, array $condition,
|
||||||
return $items;
|
return $items;
|
||||||
}
|
}
|
||||||
|
|
||||||
function item_photo_menu($item) {
|
function item_photo_menu($item)
|
||||||
|
{
|
||||||
$sub_link = '';
|
$sub_link = '';
|
||||||
$poke_link = '';
|
$poke_link = '';
|
||||||
$contact_url = '';
|
$contact_url = '';
|
||||||
|
@ -929,8 +930,8 @@ function item_photo_menu($item) {
|
||||||
if (!empty($pcid)) {
|
if (!empty($pcid)) {
|
||||||
$contact_url = 'contact/' . $pcid;
|
$contact_url = 'contact/' . $pcid;
|
||||||
$posts_link = $contact_url . '/posts';
|
$posts_link = $contact_url . '/posts';
|
||||||
$block_link = $contact_url . '/block';
|
$block_link = $item['self'] ? '' : $contact_url . '/block';
|
||||||
$ignore_link = $contact_url . '/ignore';
|
$ignore_link = $item['self'] ? '' : $contact_url . '/ignore';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($cid && !$item['self']) {
|
if ($cid && !$item['self']) {
|
||||||
|
@ -983,7 +984,7 @@ function item_photo_menu($item) {
|
||||||
if (strpos($v, 'javascript:') === 0) {
|
if (strpos($v, 'javascript:') === 0) {
|
||||||
$v = substr($v, 11);
|
$v = substr($v, 11);
|
||||||
$o .= '<li role="menuitem"><a onclick="' . $v . '">' . $k . '</a></li>' . PHP_EOL;
|
$o .= '<li role="menuitem"><a onclick="' . $v . '">' . $k . '</a></li>' . PHP_EOL;
|
||||||
} elseif ($v!='') {
|
} elseif ($v) {
|
||||||
$o .= '<li role="menuitem"><a href="' . $v . '">' . $k . '</a></li>' . PHP_EOL;
|
$o .= '<li role="menuitem"><a href="' . $v . '">' . $k . '</a></li>' . PHP_EOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1211,6 +1212,7 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
|
||||||
'$edimg' => DI::l10n()->t('Image'),
|
'$edimg' => DI::l10n()->t('Image'),
|
||||||
'$edurl' => DI::l10n()->t('Link'),
|
'$edurl' => DI::l10n()->t('Link'),
|
||||||
'$edattach' => DI::l10n()->t('Link or Media'),
|
'$edattach' => DI::l10n()->t('Link or Media'),
|
||||||
|
'$edvideo' => DI::l10n()->t('Video'),
|
||||||
'$setloc' => DI::l10n()->t('Set your location'),
|
'$setloc' => DI::l10n()->t('Set your location'),
|
||||||
'$shortsetloc' => DI::l10n()->t('set location'),
|
'$shortsetloc' => DI::l10n()->t('set location'),
|
||||||
'$noloc' => DI::l10n()->t('Clear browser location'),
|
'$noloc' => DI::l10n()->t('Clear browser location'),
|
||||||
|
|
|
@ -181,7 +181,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
|
||||||
* random key which is encrypted with their site public key.
|
* random key which is encrypted with their site public key.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$src_aes_key = openssl_random_pseudo_bytes(64);
|
$src_aes_key = random_bytes(64);
|
||||||
|
|
||||||
$result = '';
|
$result = '';
|
||||||
openssl_private_encrypt($dfrn_id, $result, $user['prvkey']);
|
openssl_private_encrypt($dfrn_id, $result, $user['prvkey']);
|
||||||
|
|
|
@ -614,7 +614,8 @@ function item_post(App $a) {
|
||||||
$datarray['origin'] = $origin;
|
$datarray['origin'] = $origin;
|
||||||
$datarray['object'] = $object;
|
$datarray['object'] = $object;
|
||||||
|
|
||||||
$datarray["uri-id"] = ItemURI::getIdByURI($datarray["uri"]);
|
$datarray['uri-id'] = ItemURI::getIdByURI($datarray['uri']);
|
||||||
|
$datarray['attachments'] = $_REQUEST['attachments'] ?? [];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These fields are for the convenience of addons...
|
* These fields are for the convenience of addons...
|
||||||
|
|
|
@ -139,7 +139,7 @@ function ping_init(App $a)
|
||||||
local_user(), Verb::getID(Activity::FOLLOW)];
|
local_user(), Verb::getID(Activity::FOLLOW)];
|
||||||
$items = Post::selectForUser(local_user(), ['wall', 'uid', 'uri-id'], $condition);
|
$items = Post::selectForUser(local_user(), ['wall', 'uid', 'uri-id'], $condition);
|
||||||
if (DBA::isResult($items)) {
|
if (DBA::isResult($items)) {
|
||||||
$items_unseen = Post::toArray($items);
|
$items_unseen = Post::toArray($items, false);
|
||||||
$arr = ['items' => $items_unseen];
|
$arr = ['items' => $items_unseen];
|
||||||
Hook::callAll('network_ping', $arr);
|
Hook::callAll('network_ping', $arr);
|
||||||
|
|
||||||
|
|
|
@ -500,77 +500,26 @@ function settings_content(App $a)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($a->argc > 1) && ($a->argv[1] === 'oauth')) {
|
if (($a->argc > 1) && ($a->argv[1] === 'oauth')) {
|
||||||
if (($a->argc > 2) && ($a->argv[2] === 'add')) {
|
|
||||||
$tpl = Renderer::getMarkupTemplate('settings/oauth_edit.tpl');
|
|
||||||
$o .= Renderer::replaceMacros($tpl, [
|
|
||||||
'$form_security_token' => BaseModule::getFormSecurityToken("settings_oauth"),
|
|
||||||
'$title' => DI::l10n()->t('Add application'),
|
|
||||||
'$submit' => DI::l10n()->t('Save Settings'),
|
|
||||||
'$cancel' => DI::l10n()->t('Cancel'),
|
|
||||||
'$name' => ['name', DI::l10n()->t('Name'), '', ''],
|
|
||||||
'$key' => ['key', DI::l10n()->t('Consumer Key'), '', ''],
|
|
||||||
'$secret' => ['secret', DI::l10n()->t('Consumer Secret'), '', ''],
|
|
||||||
'$redirect' => ['redirect', DI::l10n()->t('Redirect'), '', ''],
|
|
||||||
'$icon' => ['icon', DI::l10n()->t('Icon url'), '', ''],
|
|
||||||
]);
|
|
||||||
return $o;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($a->argc > 3) && ($a->argv[2] === 'edit')) {
|
|
||||||
$r = q("SELECT * FROM clients WHERE client_id='%s' AND uid=%d",
|
|
||||||
DBA::escape($a->argv[3]),
|
|
||||||
local_user());
|
|
||||||
|
|
||||||
if (!DBA::isResult($r)) {
|
|
||||||
notice(DI::l10n()->t("You can't edit this application."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$app = $r[0];
|
|
||||||
|
|
||||||
$tpl = Renderer::getMarkupTemplate('settings/oauth_edit.tpl');
|
|
||||||
$o .= Renderer::replaceMacros($tpl, [
|
|
||||||
'$form_security_token' => BaseModule::getFormSecurityToken("settings_oauth"),
|
|
||||||
'$title' => DI::l10n()->t('Add application'),
|
|
||||||
'$submit' => DI::l10n()->t('Update'),
|
|
||||||
'$cancel' => DI::l10n()->t('Cancel'),
|
|
||||||
'$name' => ['name', DI::l10n()->t('Name'), $app['name'] , ''],
|
|
||||||
'$key' => ['key', DI::l10n()->t('Consumer Key'), $app['client_id'], ''],
|
|
||||||
'$secret' => ['secret', DI::l10n()->t('Consumer Secret'), $app['pw'], ''],
|
|
||||||
'$redirect' => ['redirect', DI::l10n()->t('Redirect'), $app['redirect_uri'], ''],
|
|
||||||
'$icon' => ['icon', DI::l10n()->t('Icon url'), $app['icon'], ''],
|
|
||||||
]);
|
|
||||||
return $o;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($a->argc > 3) && ($a->argv[2] === 'delete')) {
|
if (($a->argc > 3) && ($a->argv[2] === 'delete')) {
|
||||||
BaseModule::checkFormSecurityTokenRedirectOnError('/settings/oauth', 'settings_oauth', 't');
|
BaseModule::checkFormSecurityTokenRedirectOnError('/settings/oauth', 'settings_oauth', 't');
|
||||||
|
|
||||||
DBA::delete('clients', ['client_id' => $a->argv[3], 'uid' => local_user()]);
|
DBA::delete('application-token', ['application-id' => $a->argv[3], 'uid' => local_user()]);
|
||||||
DI::baseUrl()->redirect('settings/oauth/', true);
|
DI::baseUrl()->redirect('settings/oauth/', true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @TODO validate result with DBA::isResult()
|
$applications = DBA::selectToArray('application-view', ['id', 'uid', 'name', 'website', 'scopes', 'created_at'], ['uid' => local_user()]);
|
||||||
$r = q("SELECT clients.*, tokens.id as oauth_token, (clients.uid=%d) AS my
|
|
||||||
FROM clients
|
|
||||||
LEFT JOIN tokens ON clients.client_id=tokens.client_id
|
|
||||||
WHERE clients.uid IN (%d, 0)",
|
|
||||||
local_user(),
|
|
||||||
local_user());
|
|
||||||
|
|
||||||
|
|
||||||
$tpl = Renderer::getMarkupTemplate('settings/oauth.tpl');
|
$tpl = Renderer::getMarkupTemplate('settings/oauth.tpl');
|
||||||
$o .= Renderer::replaceMacros($tpl, [
|
$o .= Renderer::replaceMacros($tpl, [
|
||||||
'$form_security_token' => BaseModule::getFormSecurityToken("settings_oauth"),
|
'$form_security_token' => BaseModule::getFormSecurityToken("settings_oauth"),
|
||||||
'$baseurl' => DI::baseUrl()->get(true),
|
'$baseurl' => DI::baseUrl()->get(true),
|
||||||
'$title' => DI::l10n()->t('Connected Apps'),
|
'$title' => DI::l10n()->t('Connected Apps'),
|
||||||
'$add' => DI::l10n()->t('Add application'),
|
'$name' => DI::l10n()->t('Name'),
|
||||||
'$edit' => DI::l10n()->t('Edit'),
|
'$website' => DI::l10n()->t('Home Page'),
|
||||||
'$delete' => DI::l10n()->t('Delete'),
|
'$created_at' => DI::l10n()->t('Created'),
|
||||||
'$consumerkey' => DI::l10n()->t('Client key starts with'),
|
'$delete' => DI::l10n()->t('Remove authorization'),
|
||||||
'$noname' => DI::l10n()->t('No name'),
|
'$apps' => $applications,
|
||||||
'$remove' => DI::l10n()->t('Remove authorization'),
|
|
||||||
'$apps' => $r,
|
|
||||||
]);
|
]);
|
||||||
return $o;
|
return $o;
|
||||||
}
|
}
|
||||||
|
|
495
mods/.drone.yml
495
mods/.drone.yml
|
@ -1,495 +0,0 @@
|
||||||
# Drone.io test YML-File, currently disabled
|
|
||||||
# See https://github.com/friendica/friendica/pull/7643 for further infos
|
|
||||||
kind: pipeline
|
|
||||||
name: mysql8.0-php7.1
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: mysql8.0-php7.1
|
|
||||||
image: friendicaci/php7.1:php7.1.32
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true ./autotest.sh mysql
|
|
||||||
environment:
|
|
||||||
MYSQL_USERNAME: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
MYSQL_HOST: mysql
|
|
||||||
|
|
||||||
node:
|
|
||||||
test: db
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: mysql
|
|
||||||
image: mysql:8.0
|
|
||||||
command: [ "--default-authentication-plugin=mysql_native_password" ]
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: friendica
|
|
||||||
MYSQL_USER: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /var/lib/mysql
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
temp: {}
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: mysql8.0-php7.2
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: mysql8.0-php7.2
|
|
||||||
image: friendicaci/php7.2:php7.2.22
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true ./autotest.sh mysql
|
|
||||||
environment:
|
|
||||||
MYSQL_USERNAME: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
MYSQL_HOST: mysql
|
|
||||||
|
|
||||||
node:
|
|
||||||
test: db
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: mysql
|
|
||||||
image: mysql:8.0
|
|
||||||
command: [ "--default-authentication-plugin=mysql_native_password" ]
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: friendica
|
|
||||||
MYSQL_USER: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /var/lib/mysql
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
temp: {}
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: mysql8.0-php7.3
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: mysql8.0-php7.3
|
|
||||||
image: friendicaci/php7.3:php7.3.9
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true ./autotest.sh mysql
|
|
||||||
environment:
|
|
||||||
MYSQL_USERNAME: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
MYSQL_HOST: mysql
|
|
||||||
|
|
||||||
node:
|
|
||||||
test: db
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: mysql
|
|
||||||
image: mysql:8.0
|
|
||||||
command: [ "--default-authentication-plugin=mysql_native_password" ]
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: friendica
|
|
||||||
MYSQL_USER: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /var/lib/mysql
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
temp: {}
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: mariadb10.1-php7.1
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: mariadb10.1-php7.1
|
|
||||||
image: friendicaci/php7.1:php7.1.32
|
|
||||||
commands:
|
|
||||||
- phpenmod xdebug
|
|
||||||
- sleep 20
|
|
||||||
- ./autotest.sh mariadb
|
|
||||||
- wget https://codecov.io/bash -O codecov.sh
|
|
||||||
- sh -c "if [ '$DRONE_BUILD_EVENT' = 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -P $DRONE_PULL_REQUEST -t 5ce7d64e-07b4-4adf-8700-e2eae27e14ec -f tests/autotest-clover.xml; fi"
|
|
||||||
- sh -c "if [ '$DRONE_BUILD_EVENT' != 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -t 5ce7d64e-07b4-4adf-8700-e2eae27e14ec -f tests/autotest-clover.xml; fi"
|
|
||||||
environment:
|
|
||||||
MYSQL_USER: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
MYSQL_HOST: mariadb
|
|
||||||
|
|
||||||
node:
|
|
||||||
test: db
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: mariadb
|
|
||||||
image: mariadb:10.1
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: friendica
|
|
||||||
MYSQL_USER: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /var/lib/mysql
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
temp: {}
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: mariadb10.1-php7.2
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: mariadb10.1-php7.2
|
|
||||||
image: friendicaci/php7.2:php7.2.22
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true ./autotest.sh mariadb
|
|
||||||
environment:
|
|
||||||
MYSQL_USER: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
MYSQL_HOST: mariadb
|
|
||||||
|
|
||||||
node:
|
|
||||||
test: db
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: mariadb
|
|
||||||
image: mariadb:10.1
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: friendica
|
|
||||||
MYSQL_USER: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /var/lib/mysql
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
temp: {}
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: mariadb10.1-php7.3
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: mariadb10.1-php7.3
|
|
||||||
image: friendicaci/php7.3:php7.3.9
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true ./autotest.sh mariadb
|
|
||||||
environment:
|
|
||||||
MYSQL_USER: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
MYSQL_HOST: mariadb
|
|
||||||
|
|
||||||
node:
|
|
||||||
test: db
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: mariadb
|
|
||||||
image: mariadb:10.1
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: friendica
|
|
||||||
MYSQL_USER: friendica
|
|
||||||
MYSQL_PASSWORD: friendica
|
|
||||||
MYSQL_DATABASE: friendica
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /var/lib/mysql
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
temp: {}
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: redis-php7.1
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: redis-php7.1
|
|
||||||
image: friendicaci/php7.1:php7.1.32
|
|
||||||
commands:
|
|
||||||
- phpenmod xdebug
|
|
||||||
- sleep 20
|
|
||||||
- NOINSTALL=true TEST_SELECTION=REDIS ./autotest.sh mysql
|
|
||||||
- wget https://codecov.io/bash -O codecov.sh
|
|
||||||
- sh -c "if [ '$DRONE_BUILD_EVENT' = 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -P $DRONE_PULL_REQUEST -t 5ce7d64e-07b4-4adf-8700-e2eae27e14ec -f tests/autotest-clover.xml; fi"
|
|
||||||
- sh -c "if [ '$DRONE_BUILD_EVENT' != 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -t 5ce7d64e-07b4-4adf-8700-e2eae27e14ec -f tests/autotest-clover.xml; fi"
|
|
||||||
environment:
|
|
||||||
REDIS_HOST: redis
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: redis
|
|
||||||
image: redis
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: redis-php7.2
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: redis-php7.2
|
|
||||||
image: friendicaci/php7.2:php7.2.22
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true NOINSTALL=true TEST_SELECTION=REDIS ./autotest.sh mysql
|
|
||||||
environment:
|
|
||||||
REDIS_HOST: redis
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: redis
|
|
||||||
image: redis
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: redis-php7.3
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: redis-php7.3
|
|
||||||
image: friendicaci/php7.3:php7.3.9
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true NOINSTALL=true TEST_SELECTION=REDIS ./autotest.sh mysql
|
|
||||||
environment:
|
|
||||||
REDIS_HOST: redis
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: redis
|
|
||||||
image: redis
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: memcache-php7.1
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: memcache-php7.1
|
|
||||||
image: friendicaci/php7.1:php7.1.32
|
|
||||||
commands:
|
|
||||||
- phpenmod xdebug
|
|
||||||
- sleep 20
|
|
||||||
- NOINSTALL=true TEST_SELECTION=MEMCACHE ./autotest.sh mysql
|
|
||||||
- wget https://codecov.io/bash -O codecov.sh
|
|
||||||
- sh -c "if [ '$DRONE_BUILD_EVENT' = 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -P $DRONE_PULL_REQUEST -t 5ce7d64e-07b4-4adf-8700-e2eae27e14ec -f tests/autotest-clover.xml; fi"
|
|
||||||
- sh -c "if [ '$DRONE_BUILD_EVENT' != 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -t 5ce7d64e-07b4-4adf-8700-e2eae27e14ec -f tests/autotest-clover.xml; fi"
|
|
||||||
environment:
|
|
||||||
MEMCACHE_HOST: memcached
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: memcached
|
|
||||||
image: memcached
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: memcache-php7.2
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: memcache-php7.2
|
|
||||||
image: friendicaci/php7.2:php7.2.22
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true NOINSTALL=true TEST_SELECTION=MEMCACHE ./autotest.sh mysql
|
|
||||||
environment:
|
|
||||||
MEMCACHE_HOST: memcached
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: memcached
|
|
||||||
image: memcached
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: memcache-php7.3
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: memcache-php7.3
|
|
||||||
image: friendicaci/php7.3:php7.3.9
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true NOINSTALL=true TEST_SELECTION=MEMCACHE ./autotest.sh mysql
|
|
||||||
environment:
|
|
||||||
MEMCACHE_HOST: memcached
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: memcached
|
|
||||||
image: memcached
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: memcached-php7.1
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: memcached-php7.1
|
|
||||||
image: friendicaci/php7.1:php7.1.32
|
|
||||||
commands:
|
|
||||||
- phpenmod xdebug
|
|
||||||
- sleep 20
|
|
||||||
- NOINSTALL=true TEST_SELECTION=MEMCACHED ./autotest.sh mysql
|
|
||||||
- wget https://codecov.io/bash -O codecov.sh
|
|
||||||
- sh -c "if [ '$DRONE_BUILD_EVENT' = 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -P $DRONE_PULL_REQUEST -t 5ce7d64e-07b4-4adf-8700-e2eae27e14ec -f tests/autotest-clover.xml; fi"
|
|
||||||
- sh -c "if [ '$DRONE_BUILD_EVENT' != 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -t 5ce7d64e-07b4-4adf-8700-e2eae27e14ec -f tests/autotest-clover.xml; fi"
|
|
||||||
environment:
|
|
||||||
MEMCACHED_HOST: memcached
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: memcached
|
|
||||||
image: memcached
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: memcached-php7.2
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: memcached-php7.2
|
|
||||||
image: friendicaci/php7.2:php7.2.22
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true NOINSTALL=true TEST_SELECTION=MEMCACHED ./autotest.sh mysql
|
|
||||||
environment:
|
|
||||||
MEMCACHED_HOST: memcached
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: memcached
|
|
||||||
image: memcached
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: memcached-php7.3
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: memcached-php7.3
|
|
||||||
image: friendicaci/php7.3:php7.3.9
|
|
||||||
commands:
|
|
||||||
- NOCOVERAGE=true NOINSTALL=true TEST_SELECTION=MEMCACHED ./autotest.sh mysql
|
|
||||||
environment:
|
|
||||||
MEMCACHED_HOST: memcached
|
|
||||||
|
|
||||||
services:
|
|
||||||
- name: memcached
|
|
||||||
image: memcached
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
# - stable
|
|
||||||
- develop
|
|
||||||
# - "*-rc"
|
|
||||||
# event:
|
|
||||||
# - pull_request
|
|
||||||
# - push
|
|
29
mods/release-list.txt
Normal file
29
mods/release-list.txt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
bin/auth_ejabberd.php
|
||||||
|
bin/console
|
||||||
|
bin/console.bat
|
||||||
|
bin/console.php
|
||||||
|
bin/daemon.php
|
||||||
|
bin/testargs.php
|
||||||
|
bin/worker.php
|
||||||
|
config/
|
||||||
|
doc/
|
||||||
|
images/
|
||||||
|
include/
|
||||||
|
mod/
|
||||||
|
mods/
|
||||||
|
spec/
|
||||||
|
src/
|
||||||
|
static/
|
||||||
|
vendor/
|
||||||
|
view/
|
||||||
|
.htaccess-dist
|
||||||
|
boot.php
|
||||||
|
CHANGELOG
|
||||||
|
CREDITS.txt
|
||||||
|
database.sql
|
||||||
|
index.php
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
security.txt
|
||||||
|
update.php
|
||||||
|
VERSION
|
|
@ -276,11 +276,23 @@ class Module
|
||||||
|
|
||||||
$profiler->set(microtime(true) - $timestamp, 'init');
|
$profiler->set(microtime(true) - $timestamp, 'init');
|
||||||
|
|
||||||
if ($server['REQUEST_METHOD'] === 'POST') {
|
if ($server['REQUEST_METHOD'] === Router::DELETE) {
|
||||||
|
call_user_func([$this->module_class, 'delete'], $this->module_parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($server['REQUEST_METHOD'] === Router::PATCH) {
|
||||||
|
call_user_func([$this->module_class, 'patch'], $this->module_parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($server['REQUEST_METHOD'] === Router::POST) {
|
||||||
Core\Hook::callAll($this->module . '_mod_post', $post);
|
Core\Hook::callAll($this->module . '_mod_post', $post);
|
||||||
call_user_func([$this->module_class, 'post'], $this->module_parameters);
|
call_user_func([$this->module_class, 'post'], $this->module_parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($server['REQUEST_METHOD'] === Router::PUT) {
|
||||||
|
call_user_func([$this->module_class, 'put'], $this->module_parameters);
|
||||||
|
}
|
||||||
|
|
||||||
Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder);
|
Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder);
|
||||||
call_user_func([$this->module_class, 'afterpost'], $this->module_parameters);
|
call_user_func([$this->module_class, 'afterpost'], $this->module_parameters);
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,26 @@ abstract class BaseModule
|
||||||
return $o;
|
return $o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module DELETE method to process submitted data
|
||||||
|
*
|
||||||
|
* Extend this method if the module is supposed to process DELETE requests.
|
||||||
|
* Doesn't display any content
|
||||||
|
*/
|
||||||
|
public static function delete(array $parameters = [])
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module PATCH method to process submitted data
|
||||||
|
*
|
||||||
|
* Extend this method if the module is supposed to process PATCH requests.
|
||||||
|
* Doesn't display any content
|
||||||
|
*/
|
||||||
|
public static function patch(array $parameters = [])
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module POST method to process submitted data
|
* Module POST method to process submitted data
|
||||||
*
|
*
|
||||||
|
@ -92,6 +112,16 @@ abstract class BaseModule
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module PUT method to process submitted data
|
||||||
|
*
|
||||||
|
* Extend this method if the module is supposed to process PUT requests.
|
||||||
|
* Doesn't display any content
|
||||||
|
*/
|
||||||
|
public static function put(array $parameters = [])
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Functions used to protect against Cross-Site Request Forgery
|
* 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.
|
* 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.
|
||||||
|
|
|
@ -128,6 +128,10 @@ HELP;
|
||||||
throw new RuntimeException("$cat.$key is an array and can't be set using this command.");
|
throw new RuntimeException("$cat.$key is an array and can't be set using this command.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->config->get($cat, $key) === $value) {
|
||||||
|
throw new RuntimeException("$cat.$key already set to $value.");
|
||||||
|
}
|
||||||
|
|
||||||
$result = $this->config->set($cat, $key, $value);
|
$result = $this->config->set($cat, $key, $value);
|
||||||
if ($result) {
|
if ($result) {
|
||||||
$this->out("{$cat}.{$key} <= " .
|
$this->out("{$cat}.{$key} <= " .
|
||||||
|
|
287
src/Console/Contact.php
Normal file
287
src/Console/Contact.php
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Console;
|
||||||
|
|
||||||
|
use Console_Table;
|
||||||
|
use Friendica\App;
|
||||||
|
use Friendica\Model\Contact as ContactModel;
|
||||||
|
use Friendica\Model\User as UserModel;
|
||||||
|
use Friendica\Util\Temporal;
|
||||||
|
use RuntimeException;
|
||||||
|
use Seld\CliPrompt\CliPrompt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tool to manage contacts of users of the current node
|
||||||
|
*/
|
||||||
|
class Contact extends \Asika\SimpleConsole\Console
|
||||||
|
{
|
||||||
|
protected $helpOptions = ['h', 'help', '?'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var App\Mode
|
||||||
|
*/
|
||||||
|
private $appMode;
|
||||||
|
/**
|
||||||
|
* @var IPConfig
|
||||||
|
*/
|
||||||
|
private $pConfig;
|
||||||
|
|
||||||
|
protected function getHelp()
|
||||||
|
{
|
||||||
|
$help = <<<HELP
|
||||||
|
console contact - Modify contact settings per console commands.
|
||||||
|
Usage
|
||||||
|
bin/console contact add <user nick> <URL> [<network>] [-h|--help|-?] [-v]
|
||||||
|
bin/console contact remove <CID> [-h|--help|-?] [-v]
|
||||||
|
bin/console contact search id <CID> [-h|--help|-?] [-v]
|
||||||
|
bin/console contact search url <user nick> <URL> [-h|--help|-?] [-v]
|
||||||
|
bin/console contact terminate <CID> [-h|--help|-?] [-v]
|
||||||
|
|
||||||
|
Description
|
||||||
|
Modify contact settings per console commands.
|
||||||
|
|
||||||
|
Options
|
||||||
|
-h|--help|-? Show help information
|
||||||
|
-v Show more debug information
|
||||||
|
-y Non-interactive mode, assume "yes" as answer to the user deletion prompt
|
||||||
|
HELP;
|
||||||
|
return $help;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(App\Mode $appMode, array $argv = null)
|
||||||
|
{
|
||||||
|
parent::__construct($argv);
|
||||||
|
|
||||||
|
$this->appMode = $appMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
$this->out($this->getHelp());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->appMode->isInstall()) {
|
||||||
|
throw new RuntimeException('Database isn\'t ready or populated yet');
|
||||||
|
}
|
||||||
|
|
||||||
|
$command = $this->getArgument(0);
|
||||||
|
|
||||||
|
switch ($command) {
|
||||||
|
case 'add':
|
||||||
|
return $this->addContact();
|
||||||
|
case 'remove':
|
||||||
|
return $this->removeContact();
|
||||||
|
case 'search':
|
||||||
|
return $this->searchContact();
|
||||||
|
case 'terminate':
|
||||||
|
return $this->terminateContact();
|
||||||
|
default:
|
||||||
|
throw new \Asika\SimpleConsole\CommandArgsException('Wrong command.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the user from a nick supplied as an argument or from a prompt
|
||||||
|
*
|
||||||
|
* @param int $arg_index Index of the nick argument in the arguments list
|
||||||
|
*
|
||||||
|
* @return array|boolean User record with uid field, or false if user is not found
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
private function getUserByNick($arg_index)
|
||||||
|
{
|
||||||
|
$nick = $this->getArgument($arg_index);
|
||||||
|
|
||||||
|
if (empty($nick)) {
|
||||||
|
$this->out('Enter user nickname: ');
|
||||||
|
$nick = CliPrompt::prompt();
|
||||||
|
if (empty($nick)) {
|
||||||
|
throw new RuntimeException('A user nickname must be specified.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = UserModel::getByNickname($nick, ['uid', 'nickname']);
|
||||||
|
if (empty($user)) {
|
||||||
|
throw new RuntimeException('User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a contact to a user from a URL
|
||||||
|
*
|
||||||
|
* @return bool True, if the command was successful
|
||||||
|
*/
|
||||||
|
private function addContact()
|
||||||
|
{
|
||||||
|
$user = $this->getUserByNick(1);
|
||||||
|
|
||||||
|
$url = $this->getArgument(2);
|
||||||
|
if (empty($url)) {
|
||||||
|
$this->out('Enter contact URL: ');
|
||||||
|
$url = CliPrompt::prompt();
|
||||||
|
if (empty($url)) {
|
||||||
|
throw new RuntimeException('A contact URL must be specified.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$contact = ContactModel::getByURLForUser($url, $user['uid']);
|
||||||
|
if (!empty($contact)) {
|
||||||
|
throw new RuntimeException('Contact already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
$network = $this->getArgument(3);
|
||||||
|
if ($network === null) {
|
||||||
|
$this->out('Enter network, or leave blank: ');
|
||||||
|
$network = CliPrompt::prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = ContactModel::createFromProbe($user, $url, false, $network);
|
||||||
|
|
||||||
|
if ($result['success']) {
|
||||||
|
$this->out('User ' . $user['nickname'] . ' now connected to ' . $url . ', contact ID ' . $result['cid']);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException($result['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an unfriend message. Does not remove the contact
|
||||||
|
*
|
||||||
|
* @return bool True, if the command was successful
|
||||||
|
*/
|
||||||
|
private function terminateContact()
|
||||||
|
{
|
||||||
|
$cid = $this->getArgument(1);
|
||||||
|
if (empty($cid)) {
|
||||||
|
$this->out('Enter contact ID: ');
|
||||||
|
$cid = CliPrompt::prompt();
|
||||||
|
if (empty($cid)) {
|
||||||
|
throw new RuntimeException('A contact ID must be specified.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$contact = ContactModel::getById($cid);
|
||||||
|
if (empty($contact)) {
|
||||||
|
throw new RuntimeException('Contact not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = UserModel::getById($contact['uid']);
|
||||||
|
|
||||||
|
$result = ContactModel::terminateFriendship($user, $contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks a contact for removal
|
||||||
|
*
|
||||||
|
* @return bool True, if the command was successful
|
||||||
|
*/
|
||||||
|
private function removeContact()
|
||||||
|
{
|
||||||
|
$cid = $this->getArgument(1);
|
||||||
|
if (empty($cid)) {
|
||||||
|
$this->out('Enter contact ID: ');
|
||||||
|
$cid = CliPrompt::prompt();
|
||||||
|
if (empty($cid)) {
|
||||||
|
throw new RuntimeException('A contact ID must be specified.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = ContactModel::remove($cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a contact based on search parameter
|
||||||
|
*
|
||||||
|
* @return bool True, if the command was successful
|
||||||
|
*/
|
||||||
|
private function searchContact()
|
||||||
|
{
|
||||||
|
$fields = [
|
||||||
|
'id',
|
||||||
|
'uid',
|
||||||
|
'network',
|
||||||
|
'name',
|
||||||
|
'nick',
|
||||||
|
'url',
|
||||||
|
'addr',
|
||||||
|
'created',
|
||||||
|
'updated',
|
||||||
|
'blocked',
|
||||||
|
'deleted',
|
||||||
|
];
|
||||||
|
|
||||||
|
$subCmd = $this->getArgument(1);
|
||||||
|
|
||||||
|
$table = new Console_Table();
|
||||||
|
$table->setHeaders(['ID', 'UID', 'Network', 'Name', 'Nick', 'URL', 'E-Mail', 'Created', 'Updated', 'Blocked', 'Deleted']);
|
||||||
|
|
||||||
|
$addRow = function ($row) use (&$table) {
|
||||||
|
$table->addRow([
|
||||||
|
$row['id'],
|
||||||
|
$row['uid'],
|
||||||
|
$row['network'],
|
||||||
|
$row['name'],
|
||||||
|
$row['nick'],
|
||||||
|
$row['url'],
|
||||||
|
$row['addr'],
|
||||||
|
Temporal::getRelativeDate($row['created']),
|
||||||
|
Temporal::getRelativeDate($row['updated']),
|
||||||
|
$row['blocked'],
|
||||||
|
$row['deleted'],
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
switch ($subCmd) {
|
||||||
|
case 'id':
|
||||||
|
$cid = $this->getArgument(2);
|
||||||
|
$contact = ContactModel::getById($cid, $fields);
|
||||||
|
if (!empty($contact)) {
|
||||||
|
$addRow($contact);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'url':
|
||||||
|
$user = $this->getUserByNick(2);
|
||||||
|
$url = $this->getArgument(3);
|
||||||
|
$contact = ContactModel::getByURLForUser($url, $user['uid'], false, $fields);
|
||||||
|
if (!empty($contact)) {
|
||||||
|
$addRow($contact);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->out($this->getHelp());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->out($table->getTable());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,6 @@ use Friendica\App;
|
||||||
use Friendica\Content\Pager;
|
use Friendica\Content\Pager;
|
||||||
use Friendica\Core\L10n;
|
use Friendica\Core\L10n;
|
||||||
use Friendica\Core\PConfig\IPConfig;
|
use Friendica\Core\PConfig\IPConfig;
|
||||||
use Friendica\Database\Database;
|
|
||||||
use Friendica\Model\Register;
|
use Friendica\Model\Register;
|
||||||
use Friendica\Model\User as UserModel;
|
use Friendica\Model\User as UserModel;
|
||||||
use Friendica\Util\Temporal;
|
use Friendica\Util\Temporal;
|
||||||
|
@ -49,9 +48,9 @@ class User extends \Asika\SimpleConsole\Console
|
||||||
*/
|
*/
|
||||||
private $l10n;
|
private $l10n;
|
||||||
/**
|
/**
|
||||||
* @var Database
|
* @var IPConfig
|
||||||
*/
|
*/
|
||||||
private $dba;
|
private $pConfig;
|
||||||
|
|
||||||
protected function getHelp()
|
protected function getHelp()
|
||||||
{
|
{
|
||||||
|
@ -89,13 +88,13 @@ HELP;
|
||||||
return $help;
|
return $help;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(App\Mode $appMode, L10n $l10n, Database $dba, array $argv = null)
|
public function __construct(App\Mode $appMode, L10n $l10n, IPConfig $pConfig, array $argv = null)
|
||||||
{
|
{
|
||||||
parent::__construct($argv);
|
parent::__construct($argv);
|
||||||
|
|
||||||
$this->appMode = $appMode;
|
$this->appMode = $appMode;
|
||||||
$this->l10n = $l10n;
|
$this->l10n = $l10n;
|
||||||
$this->dba = $dba;
|
$this->pConfig = $pConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function doExecute()
|
protected function doExecute()
|
||||||
|
@ -171,15 +170,15 @@ HELP;
|
||||||
*
|
*
|
||||||
* @param int $arg_index Index of the nick argument in the arguments list
|
* @param int $arg_index Index of the nick argument in the arguments list
|
||||||
*
|
*
|
||||||
* @return mixed user data or dba failure result
|
* @return array|boolean User record with uid field, or false if user is not found
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
*/
|
*/
|
||||||
private function getUserByNick($arg_index)
|
private function getUserByNick($arg_index)
|
||||||
{
|
{
|
||||||
$nick = $this->getNick($arg_index);
|
$nick = $this->getNick($arg_index);
|
||||||
|
|
||||||
$user = $this->dba->selectFirst('user', ['uid'], ['nickname' => $nick]);
|
$user = UserModel::getByNickname($nick, ['uid']);
|
||||||
if (!$this->dba->isResult($user)) {
|
if (empty($user)) {
|
||||||
throw new RuntimeException($this->l10n->t('User not found'));
|
throw new RuntimeException($this->l10n->t('User not found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +206,7 @@ HELP;
|
||||||
try {
|
try {
|
||||||
$result = UserModel::updatePassword($user['uid'], $password);
|
$result = UserModel::updatePassword($user['uid'], $password);
|
||||||
|
|
||||||
if (!$this->dba->isResult($result)) {
|
if (empty($result)) {
|
||||||
throw new \Exception($this->l10n->t('Password update failed. Please try again.'));
|
throw new \Exception($this->l10n->t('Password update failed. Please try again.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,7 +425,9 @@ HELP;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($user)) {
|
||||||
$table->addRow($user);
|
$table->addRow($user);
|
||||||
|
}
|
||||||
$this->out($table->getTable());
|
$this->out($table->getTable());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -463,8 +464,7 @@ HELP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$pconfig = \Friendica\DI::pConfig();
|
$values = $this->pConfig->load($user['uid'], $category);
|
||||||
$values = $pconfig->load($user['uid'], $category);
|
|
||||||
|
|
||||||
switch ($subCmd) {
|
switch ($subCmd) {
|
||||||
case 'list':
|
case 'list':
|
||||||
|
@ -485,7 +485,7 @@ HELP;
|
||||||
throw new RuntimeException('Key does not exist');
|
throw new RuntimeException('Key does not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->out($pconfig->get($user['uid'], $category, $key));
|
$this->out($this->pConfig->get($user['uid'], $category, $key));
|
||||||
break;
|
break;
|
||||||
case 'set':
|
case 'set':
|
||||||
$value = $this->getArgument(5);
|
$value = $this->getArgument(5);
|
||||||
|
@ -504,7 +504,7 @@ HELP;
|
||||||
throw new RuntimeException('Value not changed');
|
throw new RuntimeException('Value not changed');
|
||||||
}
|
}
|
||||||
|
|
||||||
$pconfig->set($user['uid'], $category, $key, $value);
|
$this->pConfig->set($user['uid'], $category, $key, $value);
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
if (!array_key_exists($category, $values)) {
|
if (!array_key_exists($category, $values)) {
|
||||||
|
@ -514,7 +514,7 @@ HELP;
|
||||||
throw new RuntimeException('Key does not exist');
|
throw new RuntimeException('Key does not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
$pconfig->delete($user['uid'], $category, $key);
|
$this->pConfig->delete($user['uid'], $category, $key);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$this->out($this->getHelp());
|
$this->out($this->getHelp());
|
||||||
|
|
|
@ -144,9 +144,9 @@ class Nav
|
||||||
* array 'userinfo' => Array of user information (name, icon)
|
* array 'userinfo' => Array of user information (name, icon)
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
*/
|
*/
|
||||||
private static function getInfo(App $a)
|
private static function getInfo(App $a): array
|
||||||
{
|
{
|
||||||
$ssl_state = ((local_user()) ? true : false);
|
$ssl_state = (bool) local_user();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Our network is distributed, and as you visit friends some of the
|
* Our network is distributed, and as you visit friends some of the
|
||||||
|
@ -158,13 +158,27 @@ class Nav
|
||||||
|
|
||||||
$sitelocation = $myident . substr(DI::baseUrl()->get($ssl_state), strpos(DI::baseUrl()->get($ssl_state), '//') + 2);
|
$sitelocation = $myident . substr(DI::baseUrl()->get($ssl_state), strpos(DI::baseUrl()->get($ssl_state), '//') + 2);
|
||||||
|
|
||||||
// nav links: array of array('href', 'text', 'extra css classes', 'title')
|
$nav = [
|
||||||
$nav = [];
|
'admin' => null,
|
||||||
|
'apps' => null,
|
||||||
|
'community' => null,
|
||||||
|
'home' => null,
|
||||||
|
'events' => null,
|
||||||
|
'login' => null,
|
||||||
|
'logout' => null,
|
||||||
|
'langselector' => null,
|
||||||
|
'messages' => null,
|
||||||
|
'network' => null,
|
||||||
|
'notifications' => null,
|
||||||
|
'remote' => null,
|
||||||
|
'search' => null,
|
||||||
|
'usermenu' => [],
|
||||||
|
];
|
||||||
|
|
||||||
// Display login or logout
|
// Display login or logout
|
||||||
$nav['usermenu'] = [];
|
|
||||||
$userinfo = null;
|
$userinfo = null;
|
||||||
|
|
||||||
|
// nav links: array of array('href', 'text', 'extra css classes', 'title')
|
||||||
if (Session::isAuthenticated()) {
|
if (Session::isAuthenticated()) {
|
||||||
$nav['logout'] = ['logout', DI::l10n()->t('Logout'), '', DI::l10n()->t('End this session')];
|
$nav['logout'] = ['logout', DI::l10n()->t('Logout'), '', DI::l10n()->t('End this session')];
|
||||||
} else {
|
} else {
|
||||||
|
@ -297,13 +311,15 @@ class Nav
|
||||||
$banner = '<a href="https://friendi.ca"><img id="logo-img" src="images/friendica-32.png" alt="logo" /></a><span id="logo-text"><a href="https://friendi.ca">Friendica</a></span>';
|
$banner = '<a href="https://friendi.ca"><img id="logo-img" src="images/friendica-32.png" alt="logo" /></a><span id="logo-text"><a href="https://friendi.ca">Friendica</a></span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
Hook::callAll('nav_info', $nav);
|
$nav_info = [
|
||||||
|
|
||||||
return [
|
|
||||||
'sitelocation' => $sitelocation,
|
|
||||||
'nav' => $nav,
|
|
||||||
'banner' => $banner,
|
'banner' => $banner,
|
||||||
|
'nav' => $nav,
|
||||||
|
'sitelocation' => $sitelocation,
|
||||||
'userinfo' => $userinfo,
|
'userinfo' => $userinfo,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Hook::callAll('nav_info', $nav_info);
|
||||||
|
|
||||||
|
return $nav_info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,10 +253,15 @@ class PageInfo
|
||||||
// Fix for Mastodon where the mentions are in a different format
|
// Fix for Mastodon where the mentions are in a different format
|
||||||
$body = preg_replace("~\[url=($URLSearchString)]([#!@])(.*?)\[/url]~is", '$2[url=$1]$3[/url]', $body);
|
$body = preg_replace("~\[url=($URLSearchString)]([#!@])(.*?)\[/url]~is", '$2[url=$1]$3[/url]', $body);
|
||||||
|
|
||||||
preg_match("~(?<![!#@])\[url]($URLSearchString)\[/url]$~is", $body, $matches);
|
// Remove all hashtags and mentions
|
||||||
|
$body = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '', $body);
|
||||||
|
|
||||||
|
// Search for pure links
|
||||||
|
preg_match("/\[url\](https?:.*?)\[\/url\]/ism", $body, $matches);
|
||||||
|
|
||||||
if (!$matches) {
|
if (!$matches) {
|
||||||
preg_match("~(?<![!#@])\[url=($URLSearchString)].*\[/url]$~is", $body, $matches);
|
// Search for links with descriptions
|
||||||
|
preg_match("/\[url\=(https?:.*?)\].*?\[\/url\]/ism", $body, $matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$matches && $searchNakedUrls) {
|
if (!$matches && $searchNakedUrls) {
|
||||||
|
|
|
@ -50,9 +50,10 @@ use Friendica\Util\XML;
|
||||||
class BBCode
|
class BBCode
|
||||||
{
|
{
|
||||||
// Update this value to the current date whenever changes are made to BBCode::convert
|
// Update this value to the current date whenever changes are made to BBCode::convert
|
||||||
const VERSION = '2021-04-24';
|
const VERSION = '2021-05-01';
|
||||||
|
|
||||||
const INTERNAL = 0;
|
const INTERNAL = 0;
|
||||||
|
const EXTERNAL = 1;
|
||||||
const API = 2;
|
const API = 2;
|
||||||
const DIASPORA = 3;
|
const DIASPORA = 3;
|
||||||
const CONNECTORS = 4;
|
const CONNECTORS = 4;
|
||||||
|
@ -61,7 +62,8 @@ class BBCode
|
||||||
const BACKLINK = 8;
|
const BACKLINK = 8;
|
||||||
const ACTIVITYPUB = 9;
|
const ACTIVITYPUB = 9;
|
||||||
|
|
||||||
const ANCHOR = '<br class="anchor">';
|
const TOP_ANCHOR = '<br class="top-anchor">';
|
||||||
|
const BOTTOM_ANCHOR = '<br class="button-anchor">';
|
||||||
/**
|
/**
|
||||||
* Fetches attachment data that were generated the old way
|
* Fetches attachment data that were generated the old way
|
||||||
*
|
*
|
||||||
|
@ -408,7 +410,7 @@ class BBCode
|
||||||
*/
|
*/
|
||||||
public static function removeAttachment($body, $no_link_desc = false)
|
public static function removeAttachment($body, $no_link_desc = false)
|
||||||
{
|
{
|
||||||
return preg_replace_callback("/\s*\[attachment (.*)\](.*?)\[\/attachment\]\s*/ism",
|
return preg_replace_callback("/\s*\[attachment (.*?)\](.*?)\[\/attachment\]\s*/ism",
|
||||||
function ($match) use ($no_link_desc) {
|
function ($match) use ($no_link_desc) {
|
||||||
$attach_data = self::getAttachmentData($match[0]);
|
$attach_data = self::getAttachmentData($match[0]);
|
||||||
if (empty($attach_data['url'])) {
|
if (empty($attach_data['url'])) {
|
||||||
|
@ -1084,12 +1086,12 @@ class BBCode
|
||||||
'$avatar' => $attributes['avatar'],
|
'$avatar' => $attributes['avatar'],
|
||||||
'$author' => $attributes['author'],
|
'$author' => $attributes['author'],
|
||||||
'$link' => $attributes['link'],
|
'$link' => $attributes['link'],
|
||||||
'$link_title' => DI::l10n()->t('link to source'),
|
'$link_title' => DI::l10n()->t('Link to source'),
|
||||||
'$posted' => $attributes['posted'],
|
'$posted' => $attributes['posted'],
|
||||||
'$guid' => $attributes['guid'],
|
'$guid' => $attributes['guid'],
|
||||||
'$network_name' => ContactSelector::networkToName($network, $attributes['profile']),
|
'$network_name' => ContactSelector::networkToName($network, $attributes['profile']),
|
||||||
'$network_icon' => ContactSelector::networkToIcon($network, $attributes['profile']),
|
'$network_icon' => ContactSelector::networkToIcon($network, $attributes['profile']),
|
||||||
'$content' => self::setMentions(trim($content), 0, $network) . self::ANCHOR,
|
'$content' => self::TOP_ANCHOR . self::setMentions(trim($content), 0, $network) . self::BOTTOM_ANCHOR,
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1103,20 +1105,14 @@ class BBCode
|
||||||
$text = DI::cache()->get($cache_key);
|
$text = DI::cache()->get($cache_key);
|
||||||
|
|
||||||
if (is_null($text)) {
|
if (is_null($text)) {
|
||||||
$a = DI::app();
|
$curlResult = DI::httpRequest()->head($match[1], ['timeout' => DI::config()->get('system', 'xrd_timeout')]);
|
||||||
|
if ($curlResult->isSuccess()) {
|
||||||
|
$mimetype = $curlResult->getHeader('Content-Type');
|
||||||
|
} else {
|
||||||
|
$mimetype = '';
|
||||||
|
}
|
||||||
|
|
||||||
$stamp1 = microtime(true);
|
if (substr($mimetype, 0, 6) == 'image/') {
|
||||||
|
|
||||||
$ch = @curl_init($match[1]);
|
|
||||||
@curl_setopt($ch, CURLOPT_NOBODY, true);
|
|
||||||
@curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
@curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent());
|
|
||||||
@curl_exec($ch);
|
|
||||||
$curl_info = @curl_getinfo($ch);
|
|
||||||
|
|
||||||
DI::profiler()->saveTimestamp($stamp1, "network");
|
|
||||||
|
|
||||||
if (substr($curl_info['content_type'], 0, 6) == 'image/') {
|
|
||||||
$text = "[url=" . $match[1] . ']' . $match[1] . "[/url]";
|
$text = "[url=" . $match[1] . ']' . $match[1] . "[/url]";
|
||||||
} else {
|
} else {
|
||||||
$text = "[url=" . $match[2] . ']' . $match[2] . "[/url]";
|
$text = "[url=" . $match[2] . ']' . $match[2] . "[/url]";
|
||||||
|
@ -1180,20 +1176,15 @@ class BBCode
|
||||||
return $text;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only fetch the header, not the content
|
$curlResult = DI::httpRequest()->head($match[1], ['timeout' => DI::config()->get('system', 'xrd_timeout')]);
|
||||||
$stamp1 = microtime(true);
|
if ($curlResult->isSuccess()) {
|
||||||
|
$mimetype = $curlResult->getHeader('Content-Type');
|
||||||
$ch = @curl_init($match[1]);
|
} else {
|
||||||
@curl_setopt($ch, CURLOPT_NOBODY, true);
|
$mimetype = '';
|
||||||
@curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
}
|
||||||
@curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent());
|
|
||||||
@curl_exec($ch);
|
|
||||||
$curl_info = @curl_getinfo($ch);
|
|
||||||
|
|
||||||
DI::profiler()->saveTimestamp($stamp1, "network");
|
|
||||||
|
|
||||||
// if its a link to a picture then embed this picture
|
// if its a link to a picture then embed this picture
|
||||||
if (substr($curl_info['content_type'], 0, 6) == 'image/') {
|
if (substr($mimetype, 0, 6) == 'image/') {
|
||||||
$text = '[img]' . $match[1] . '[/img]';
|
$text = '[img]' . $match[1] . '[/img]';
|
||||||
} else {
|
} else {
|
||||||
if (!empty($match[3])) {
|
if (!empty($match[3])) {
|
||||||
|
@ -1396,7 +1387,7 @@ class BBCode
|
||||||
// Handle attached links or videos
|
// Handle attached links or videos
|
||||||
if ($simple_html == self::ACTIVITYPUB) {
|
if ($simple_html == self::ACTIVITYPUB) {
|
||||||
$text = self::removeAttachment($text);
|
$text = self::removeAttachment($text);
|
||||||
} elseif (!in_array($simple_html, [self::INTERNAL, self::CONNECTORS])) {
|
} elseif (!in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::CONNECTORS])) {
|
||||||
$text = self::removeAttachment($text, true);
|
$text = self::removeAttachment($text, true);
|
||||||
} else {
|
} else {
|
||||||
$text = self::convertAttachment($text, $simple_html, $try_oembed);
|
$text = self::convertAttachment($text, $simple_html, $try_oembed);
|
||||||
|
@ -1406,9 +1397,9 @@ class BBCode
|
||||||
$text = str_replace('[nosmile]', '', $text);
|
$text = str_replace('[nosmile]', '', $text);
|
||||||
|
|
||||||
// Replace non graphical smilies for external posts
|
// Replace non graphical smilies for external posts
|
||||||
if (!$nosmile && !$for_plaintext) {
|
if (!$nosmile) {
|
||||||
$text = self::performWithEscapedTags($text, ['img'], function ($text) {
|
$text = self::performWithEscapedTags($text, ['img'], function ($text) use ($simple_html, $for_plaintext) {
|
||||||
return Smilies::replace($text);
|
return Smilies::replace($text, ($simple_html != self::INTERNAL) || $for_plaintext);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1730,7 +1721,7 @@ class BBCode
|
||||||
$text);
|
$text);
|
||||||
} elseif (!$simple_html) {
|
} elseif (!$simple_html) {
|
||||||
$text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
|
$text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
|
||||||
'$1<a href="$2" class="userinfo mention" title="$3">$3</a>',
|
'$1<a href="$2" class="userinfo mention" title="$3"><bdi>$3</bdi></a>',
|
||||||
$text);
|
$text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1902,7 +1893,7 @@ class BBCode
|
||||||
|
|
||||||
$text = HTML::purify($text, $allowedIframeDomains);
|
$text = HTML::purify($text, $allowedIframeDomains);
|
||||||
|
|
||||||
return $text;
|
return trim($text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -47,6 +47,7 @@ Commands:
|
||||||
addon Addon management
|
addon Addon management
|
||||||
cache Manage node cache
|
cache Manage node cache
|
||||||
config Edit site config
|
config Edit site config
|
||||||
|
contact Contact management
|
||||||
createdoxygen Generate Doxygen headers
|
createdoxygen Generate Doxygen headers
|
||||||
dbstructure Do database updates
|
dbstructure Do database updates
|
||||||
docbloxerrorchecker Check the file tree for DocBlox errors
|
docbloxerrorchecker Check the file tree for DocBlox errors
|
||||||
|
@ -78,6 +79,7 @@ HELP;
|
||||||
'addon' => Friendica\Console\Addon::class,
|
'addon' => Friendica\Console\Addon::class,
|
||||||
'cache' => Friendica\Console\Cache::class,
|
'cache' => Friendica\Console\Cache::class,
|
||||||
'config' => Friendica\Console\Config::class,
|
'config' => Friendica\Console\Config::class,
|
||||||
|
'contact' => Friendica\Console\Contact::class,
|
||||||
'createdoxygen' => Friendica\Console\CreateDoxygen::class,
|
'createdoxygen' => Friendica\Console\CreateDoxygen::class,
|
||||||
'docbloxerrorchecker' => Friendica\Console\DocBloxErrorChecker::class,
|
'docbloxerrorchecker' => Friendica\Console\DocBloxErrorChecker::class,
|
||||||
'dbstructure' => Friendica\Console\DatabaseStructure::class,
|
'dbstructure' => Friendica\Console\DatabaseStructure::class,
|
||||||
|
|
|
@ -35,7 +35,7 @@ interface IPConfig
|
||||||
* @param int $uid The user_id
|
* @param int $uid The user_id
|
||||||
* @param string $cat The category of the configuration value
|
* @param string $cat The category of the configuration value
|
||||||
*
|
*
|
||||||
* @return void
|
* @return array The loaded config array
|
||||||
* @see Cache
|
* @see Cache
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -68,6 +68,8 @@ class PreloadPConfig extends BasePConfig
|
||||||
|
|
||||||
// load the whole category out of the DB into the cache
|
// load the whole category out of the DB into the cache
|
||||||
$this->configCache->load($uid, $config);
|
$this->configCache->load($uid, $config);
|
||||||
|
|
||||||
|
return $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -134,7 +134,7 @@ class Search
|
||||||
$profiles = $results['profiles'] ?? [];
|
$profiles = $results['profiles'] ?? [];
|
||||||
|
|
||||||
foreach ($profiles as $profile) {
|
foreach ($profiles as $profile) {
|
||||||
$profile_url = $profile['url'] ?? '';
|
$profile_url = $profile['profile_url'] ?? '';
|
||||||
$contactDetails = Contact::getByURLForUser($profile_url, local_user());
|
$contactDetails = Contact::getByURLForUser($profile_url, local_user());
|
||||||
|
|
||||||
$result = new ContactResult(
|
$result = new ContactResult(
|
||||||
|
|
32
src/DI.php
32
src/DI.php
|
@ -239,6 +239,14 @@ abstract class DI
|
||||||
return self::$dice->create(Factory\Api\Mastodon\Account::class);
|
return self::$dice->create(Factory\Api\Mastodon\Account::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Factory\Api\Mastodon\Application
|
||||||
|
*/
|
||||||
|
public static function mstdnApplication()
|
||||||
|
{
|
||||||
|
return self::$dice->create(Factory\Api\Mastodon\Application::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Factory\Api\Mastodon\Attachment
|
* @return Factory\Api\Mastodon\Attachment
|
||||||
*/
|
*/
|
||||||
|
@ -247,6 +255,14 @@ abstract class DI
|
||||||
return self::$dice->create(Factory\Api\Mastodon\Attachment::class);
|
return self::$dice->create(Factory\Api\Mastodon\Attachment::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Factory\Api\Mastodon\Card
|
||||||
|
*/
|
||||||
|
public static function mstdnCard()
|
||||||
|
{
|
||||||
|
return self::$dice->create(Factory\Api\Mastodon\Card::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Factory\Api\Mastodon\Emoji
|
* @return Factory\Api\Mastodon\Emoji
|
||||||
*/
|
*/
|
||||||
|
@ -295,6 +311,14 @@ abstract class DI
|
||||||
return self::$dice->create(Factory\Api\Mastodon\Status::class);
|
return self::$dice->create(Factory\Api\Mastodon\Status::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Factory\Api\Mastodon\ListEntity
|
||||||
|
*/
|
||||||
|
public static function mstdnList()
|
||||||
|
{
|
||||||
|
return self::$dice->create(Factory\Api\Mastodon\ListEntity::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Factory\Api\Mastodon\Mention
|
* @return Factory\Api\Mastodon\Mention
|
||||||
*/
|
*/
|
||||||
|
@ -303,6 +327,14 @@ abstract class DI
|
||||||
return self::$dice->create(Factory\Api\Mastodon\Mention::class);
|
return self::$dice->create(Factory\Api\Mastodon\Mention::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Factory\Api\Mastodon\Notification
|
||||||
|
*/
|
||||||
|
public static function mstdnNotification()
|
||||||
|
{
|
||||||
|
return self::$dice->create(Factory\Api\Mastodon\Notification::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Factory\Api\Mastodon\Tag
|
* @return Factory\Api\Mastodon\Tag
|
||||||
*/
|
*/
|
||||||
|
|
49
src/Factory/Api/Mastodon/Application.php
Normal file
49
src/Factory/Api/Mastodon/Application.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Factory\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\BaseFactory;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
|
||||||
|
class Application extends BaseFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param int $id Application ID
|
||||||
|
*/
|
||||||
|
public function createFromApplicationId(int $id)
|
||||||
|
{
|
||||||
|
$application = DBA::selectFirst('application', ['client_id', 'client_secret', 'id', 'name', 'redirect_uri', 'website'], ['id' => $id]);
|
||||||
|
if (!DBA::isResult($application)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$object = new \Friendica\Object\Api\Mastodon\Application(
|
||||||
|
$application['name'],
|
||||||
|
$application['client_id'],
|
||||||
|
$application['client_secret'],
|
||||||
|
$application['id'],
|
||||||
|
$application['redirect_uri'],
|
||||||
|
$application['website']);
|
||||||
|
|
||||||
|
return $object->toArray();
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,9 +23,12 @@ namespace Friendica\Factory\Api\Mastodon;
|
||||||
|
|
||||||
use Friendica\App\BaseURL;
|
use Friendica\App\BaseURL;
|
||||||
use Friendica\BaseFactory;
|
use Friendica\BaseFactory;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Photo;
|
||||||
use Friendica\Network\HTTPException;
|
use Friendica\Network\HTTPException;
|
||||||
use Friendica\Model\Post;
|
use Friendica\Model\Post;
|
||||||
use Friendica\Repository\ProfileField;
|
use Friendica\Repository\ProfileField;
|
||||||
|
use Friendica\Util\Images;
|
||||||
use Friendica\Util\Proxy;
|
use Friendica\Util\Proxy;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
@ -56,7 +59,7 @@ class Attachment extends BaseFactory
|
||||||
public function createFromUriId(int $uriId)
|
public function createFromUriId(int $uriId)
|
||||||
{
|
{
|
||||||
$attachments = [];
|
$attachments = [];
|
||||||
foreach (Post\Media::getByURIId($uriId) as $attachment) {
|
foreach (Post\Media::getByURIId($uriId, [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE]) as $attachment) {
|
||||||
|
|
||||||
$filetype = !empty($attachment['mimetype']) ? strtolower(substr($attachment['mimetype'], 0, strpos($attachment['mimetype'], '/'))) : '';
|
$filetype = !empty($attachment['mimetype']) ? strtolower(substr($attachment['mimetype'], 0, strpos($attachment['mimetype'], '/'))) : '';
|
||||||
|
|
||||||
|
@ -93,4 +96,36 @@ class Attachment extends BaseFactory
|
||||||
|
|
||||||
return $attachments;
|
return $attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id id of the photo
|
||||||
|
* @return array
|
||||||
|
* @throws HTTPException\InternalServerErrorException
|
||||||
|
* @throws \ImagickException
|
||||||
|
*/
|
||||||
|
public function createFromPhoto(int $id)
|
||||||
|
{
|
||||||
|
$photo = Photo::selectFirst(['resource-id', 'uid', 'id', 'title', 'type'], ['id' => $id]);
|
||||||
|
if (empty($photo)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attachment = ['id' => $photo['id'], 'description' => $photo['title']];
|
||||||
|
|
||||||
|
$phototypes = Images::supportedTypes();
|
||||||
|
$ext = $phototypes[$photo['type']];
|
||||||
|
|
||||||
|
$url = DI::baseUrl() . '/photo/' . $photo['resource-id'] . '-0.' . $ext;
|
||||||
|
|
||||||
|
$preview = Photo::selectFirst(['scale'], ["`resource-id` = ? AND `uid` = ? AND `scale` > ?", $photo['resource-id'], $photo['uid'], 0], ['order' => ['scale']]);
|
||||||
|
if (empty($scale)) {
|
||||||
|
$preview_url = DI::baseUrl() . '/photo/' . $photo['resource-id'] . '-' . $preview['scale'] . '.' . $ext;
|
||||||
|
} else {
|
||||||
|
$preview_url = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$object = new \Friendica\Object\Api\Mastodon\Attachment($attachment, 'image', $url, $preview_url, '');
|
||||||
|
return $object->toArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
80
src/Factory/Api/Mastodon/Card.php
Normal file
80
src/Factory/Api/Mastodon/Card.php
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Factory\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\BaseFactory;
|
||||||
|
use Friendica\Content\Text\BBCode;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Network\HTTPException;
|
||||||
|
use Friendica\Util\Strings;
|
||||||
|
|
||||||
|
class Card extends BaseFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param int $uriId Uri-ID of the item
|
||||||
|
* @return \Friendica\Object\Api\Mastodon\Card
|
||||||
|
* @throws HTTPException\InternalServerErrorException
|
||||||
|
* @throws \ImagickException
|
||||||
|
*/
|
||||||
|
public function createFromUriId(int $uriId)
|
||||||
|
{
|
||||||
|
$item = Post::selectFirst(['body'], ['uri-id' => $uriId]);
|
||||||
|
if (!empty($item['body'])) {
|
||||||
|
$data = BBCode::getAttachmentData($item['body']);
|
||||||
|
} else {
|
||||||
|
$data = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Post\Media::getByURIId($uriId, [Post\Media::HTML]) as $attached) {
|
||||||
|
if ((empty($data['url']) || Strings::compareLink($data['url'], $attached['url'])) &&
|
||||||
|
(!empty($attached['description']) || !empty($attached['image']) || !empty($attached['preview']))) {
|
||||||
|
$parts = parse_url($attached['url']);
|
||||||
|
if (!empty($parts['scheme']) && !empty($parts['host'])) {
|
||||||
|
if (empty($attached['publisher-name'])) {
|
||||||
|
$attached['publisher-name'] = $parts['host'];
|
||||||
|
}
|
||||||
|
if (empty($attached['publisher-url']) || empty(parse_url($attached['publisher-url'], PHP_URL_SCHEME))) {
|
||||||
|
$attached['publisher-url'] = $parts['scheme'] . '://' . $parts['host'];
|
||||||
|
|
||||||
|
if (!empty($parts['port'])) {
|
||||||
|
$attached['publisher-url'] .= ':' . $parts['port'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['url'] = $attached['url'];
|
||||||
|
$data['title'] = $attached['name'];
|
||||||
|
$data['description'] = $attached['description'];
|
||||||
|
$data['type'] = 'link';
|
||||||
|
$data['author_name'] = $attached['author-name'];
|
||||||
|
$data['author_url'] = $attached['author-url'];
|
||||||
|
$data['provider_name'] = $attached['publisher-name'];
|
||||||
|
$data['provider_url'] = $attached['publisher-url'];
|
||||||
|
$data['image'] = $attached['preview'];
|
||||||
|
$data['width'] = $attached['preview-width'];
|
||||||
|
$data['height'] = $attached['preview-height'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new \Friendica\Object\Api\Mastodon\Card($data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,4 +35,31 @@ class Error extends BaseFactory
|
||||||
|
|
||||||
System::jsonError(404, $errorobj->toArray());
|
System::jsonError(404, $errorobj->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function UnprocessableEntity(string $error = '')
|
||||||
|
{
|
||||||
|
$error = $error ?: DI::l10n()->t('Unprocessable Entity');
|
||||||
|
$error_description = '';
|
||||||
|
$errorobj = New \Friendica\Object\Api\Mastodon\Error($error, $error_description);
|
||||||
|
|
||||||
|
System::jsonError(422, $errorobj->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Unauthorized(string $error = '')
|
||||||
|
{
|
||||||
|
$error = $error ?: DI::l10n()->t('Unauthorized');
|
||||||
|
$error_description = '';
|
||||||
|
$errorobj = New \Friendica\Object\Api\Mastodon\Error($error, $error_description);
|
||||||
|
|
||||||
|
System::jsonError(401, $errorobj->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function InternalError(string $error = '')
|
||||||
|
{
|
||||||
|
$error = $error ?: DI::l10n()->t('Internal Server Error');
|
||||||
|
$error_description = '';
|
||||||
|
$errorobj = New \Friendica\Object\Api\Mastodon\Error($error, $error_description);
|
||||||
|
|
||||||
|
System::jsonError(500, $errorobj->toArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
34
src/Factory/Api/Mastodon/ListEntity.php
Normal file
34
src/Factory/Api/Mastodon/ListEntity.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Factory\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\BaseFactory;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
|
||||||
|
class ListEntity extends BaseFactory
|
||||||
|
{
|
||||||
|
public function createFromGroupId(int $id)
|
||||||
|
{
|
||||||
|
$group = DBA::selectFirst('group', ['name'], ['id' => $id, 'deleted' => false]);
|
||||||
|
return new \Friendica\Object\Api\Mastodon\ListEntity($id, $group['name'] ?? '', 'list');
|
||||||
|
}
|
||||||
|
}
|
89
src/Factory/Api/Mastodon/Notification.php
Normal file
89
src/Factory/Api/Mastodon/Notification.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Factory\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\BaseFactory;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Model\Notification as ModelNotification;
|
||||||
|
|
||||||
|
class Notification extends BaseFactory
|
||||||
|
{
|
||||||
|
public function createFromNotifyId(int $id)
|
||||||
|
{
|
||||||
|
$notification = DBA::selectFirst('notify', [], ['id' => $id]);
|
||||||
|
if (!DBA::isResult($notification)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cid = Contact::getIdForURL($notification['url'], 0, false);
|
||||||
|
if (empty($cid)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
follow = Someone followed you
|
||||||
|
follow_request = Someone requested to follow you
|
||||||
|
mention = Someone mentioned you in their status
|
||||||
|
reblog = Someone boosted one of your statuses
|
||||||
|
favourite = Someone favourited one of your statuses
|
||||||
|
poll = A poll you have voted in or created has ended
|
||||||
|
status = Someone you enabled notifications for has posted a status
|
||||||
|
*/
|
||||||
|
|
||||||
|
switch ($notification['type']) {
|
||||||
|
case ModelNotification\Type::INTRO:
|
||||||
|
$type = 'follow_request';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ModelNotification\Type::WALL:
|
||||||
|
case ModelNotification\Type::COMMENT:
|
||||||
|
case ModelNotification\Type::MAIL:
|
||||||
|
case ModelNotification\Type::TAG_SELF:
|
||||||
|
case ModelNotification\Type::POKE:
|
||||||
|
$type = 'mention';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ModelNotification\Type::SHARE:
|
||||||
|
$type = 'status';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$account = DI::mstdnAccount()->createFromContactId($cid);
|
||||||
|
|
||||||
|
if (!empty($notification['uri-id'])) {
|
||||||
|
try {
|
||||||
|
$status = DI::mstdnStatus()->createFromUriId($notification['uri-id'], $notification['uid']);
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
$status = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$status = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new \Friendica\Object\Api\Mastodon\Notification($id, $type, $notification['date'], $account, $status);
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ namespace Friendica\Factory\Api\Mastodon;
|
||||||
|
|
||||||
use Friendica\App\BaseURL;
|
use Friendica\App\BaseURL;
|
||||||
use Friendica\BaseFactory;
|
use Friendica\BaseFactory;
|
||||||
|
use Friendica\Content\ContactSelector;
|
||||||
use Friendica\Content\Text\BBCode;
|
use Friendica\Content\Text\BBCode;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
|
@ -60,9 +61,9 @@ class Status extends BaseFactory
|
||||||
*/
|
*/
|
||||||
public function createFromUriId(int $uriId, $uid = 0)
|
public function createFromUriId(int $uriId, $uid = 0)
|
||||||
{
|
{
|
||||||
$fields = ['uri-id', 'uid', 'author-id', 'starred', 'app', 'title', 'body', 'raw-body', 'created',
|
$fields = ['uri-id', 'uid', 'author-id', 'author-link', 'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network',
|
||||||
'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity'];
|
'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity'];
|
||||||
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => $uid]);
|
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||||
if (!$item) {
|
if (!$item) {
|
||||||
throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . 'not found' . ($uid ? ' for user ' . $uid : '.'));
|
throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . 'not found' . ($uid ? ' for user ' . $uid : '.'));
|
||||||
}
|
}
|
||||||
|
@ -70,32 +71,46 @@ class Status extends BaseFactory
|
||||||
$account = DI::mstdnAccount()->createFromContactId($item['author-id']);
|
$account = DI::mstdnAccount()->createFromContactId($item['author-id']);
|
||||||
|
|
||||||
$counts = new \Friendica\Object\Api\Mastodon\Status\Counts(
|
$counts = new \Friendica\Object\Api\Mastodon\Status\Counts(
|
||||||
Post::count(['thr-parent-id' => $uriId, 'uid' => $uid, 'gravity' => GRAVITY_COMMENT]),
|
Post::count(['thr-parent-id' => $uriId, 'gravity' => GRAVITY_COMMENT], [], false),
|
||||||
Post::count(['thr-parent-id' => $uriId, 'uid' => $uid, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::ANNOUNCE)]),
|
Post::count(['thr-parent-id' => $uriId, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::ANNOUNCE)], [], false),
|
||||||
Post::count(['thr-parent-id' => $uriId, 'uid' => $uid, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::LIKE)])
|
Post::count(['thr-parent-id' => $uriId, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::LIKE)], [], false)
|
||||||
);
|
);
|
||||||
|
|
||||||
$userAttributes = new \Friendica\Object\Api\Mastodon\Status\UserAttributes(
|
$userAttributes = new \Friendica\Object\Api\Mastodon\Status\UserAttributes(
|
||||||
Post::exists(['thr-parent-id' => $uriId, 'uid' => $uid, 'origin' => true, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::LIKE)]),
|
Post::exists(['thr-parent-id' => $uriId, 'uid' => $uid, 'origin' => true, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::LIKE)]),
|
||||||
Post::exists(['thr-parent-id' => $uriId, 'uid' => $uid, 'origin' => true, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::ANNOUNCE)]),
|
Post::exists(['thr-parent-id' => $uriId, 'uid' => $uid, 'origin' => true, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::ANNOUNCE)]),
|
||||||
Post\ThreadUser::getIgnored($uriId, $item['uid']),
|
Post\ThreadUser::getIgnored($uriId, $uid),
|
||||||
(bool)$item['starred'],
|
(bool)$item['starred'],
|
||||||
Post\ThreadUser::getPinned($uriId, $item['uid'])
|
Post\ThreadUser::getPinned($uriId, $uid)
|
||||||
);
|
);
|
||||||
|
|
||||||
$sensitive = DBA::exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw']);
|
$sensitive = DBA::exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw']);
|
||||||
$application = new \Friendica\Object\Api\Mastodon\Application($item['app'] ?? '');
|
$application = new \Friendica\Object\Api\Mastodon\Application($item['app'] ?: ContactSelector::networkToName($item['network'], $item['author-link']));
|
||||||
|
|
||||||
$mentions = DI::mstdnMention()->createFromUriId($uriId);
|
$mentions = DI::mstdnMention()->createFromUriId($uriId);
|
||||||
$tags = DI::mstdnTag()->createFromUriId($uriId);
|
$tags = DI::mstdnTag()->createFromUriId($uriId);
|
||||||
|
$card = DI::mstdnCard()->createFromUriId($uriId);
|
||||||
$data = BBCode::getAttachmentData($item['body']);
|
|
||||||
$card = new \Friendica\Object\Api\Mastodon\Card($data);
|
|
||||||
|
|
||||||
$attachments = DI::mstdnAttachment()->createFromUriId($uriId);
|
$attachments = DI::mstdnAttachment()->createFromUriId($uriId);
|
||||||
|
|
||||||
|
$shared = BBCode::fetchShareAttributes($item['body']);
|
||||||
|
if (!empty($shared['guid'])) {
|
||||||
|
$shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]);
|
||||||
|
|
||||||
|
$shared_uri_id = $shared_item['uri-id'] ?? 0;
|
||||||
|
|
||||||
|
$mentions = array_merge($mentions, DI::mstdnMention()->createFromUriId($shared_uri_id));
|
||||||
|
$tags = array_merge($tags, DI::mstdnTag()->createFromUriId($shared_uri_id));
|
||||||
|
$attachments = array_merge($attachments, DI::mstdnAttachment()->createFromUriId($shared_uri_id));
|
||||||
|
|
||||||
|
if (empty($card->toArray())) {
|
||||||
|
$card = DI::mstdnCard()->createFromUriId($shared_uri_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($item['vid'] == Verb::getID(Activity::ANNOUNCE)) {
|
if ($item['vid'] == Verb::getID(Activity::ANNOUNCE)) {
|
||||||
$reshare = $this->createFromUriId($item['thr-parent-id'], $uid)->toArray();
|
$reshare = $this->createFromUriId($item['thr-parent-id'], $uid)->toArray();
|
||||||
$reshared_item = Post::selectFirst(['title', 'body'], ['uri-id' => $item['thr-parent-id'], 'uid' => $uid]);
|
$reshared_item = Post::selectFirst(['title', 'body'], ['uri-id' => $item['thr-parent-id'], 'uid' => [0, $uid]]);
|
||||||
$item['title'] = $reshared_item['title'] ?? $item['title'];
|
$item['title'] = $reshared_item['title'] ?? $item['title'];
|
||||||
$item['body'] = $reshared_item['body'] ?? $item['body'];
|
$item['body'] = $reshared_item['body'] ?? $item['body'];
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -235,7 +235,7 @@ class APContact
|
||||||
unset($parts['path']);
|
unset($parts['path']);
|
||||||
|
|
||||||
if (empty($apcontact['addr'])) {
|
if (empty($apcontact['addr'])) {
|
||||||
if (!empty($apcontact['nick'])) {
|
if (!empty($apcontact['nick']) && is_array($parts)) {
|
||||||
$apcontact['addr'] = $apcontact['nick'] . '@' . str_replace('//', '', Network::unparseURL($parts));
|
$apcontact['addr'] = $apcontact['nick'] . '@' . str_replace('//', '', Network::unparseURL($parts));
|
||||||
} else {
|
} else {
|
||||||
$apcontact['addr'] = '';
|
$apcontact['addr'] = '';
|
||||||
|
|
|
@ -2763,11 +2763,12 @@ class Contact
|
||||||
*
|
*
|
||||||
* @param string $search Name or nick
|
* @param string $search Name or nick
|
||||||
* @param string $mode Search mode (e.g. "community")
|
* @param string $mode Search mode (e.g. "community")
|
||||||
|
* @param int $uid User ID
|
||||||
*
|
*
|
||||||
* @return array with search results
|
* @return array with search results
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
*/
|
*/
|
||||||
public static function searchByName($search, $mode = '')
|
public static function searchByName(string $search, string $mode = '', int $uid = 0)
|
||||||
{
|
{
|
||||||
if (empty($search)) {
|
if (empty($search)) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -2800,7 +2801,7 @@ class Contact
|
||||||
NOT `failed` AND `uid` = ? AND
|
NOT `failed` AND `uid` = ? AND
|
||||||
(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql
|
(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql
|
||||||
ORDER BY `nurl` DESC LIMIT 1000",
|
ORDER BY `nurl` DESC LIMIT 1000",
|
||||||
Protocol::DFRN, Protocol::ACTIVITYPUB, $ostatus, $diaspora, 0, $search, $search, $search
|
Protocol::DFRN, Protocol::ACTIVITYPUB, $ostatus, $diaspora, $uid, $search, $search, $search
|
||||||
);
|
);
|
||||||
|
|
||||||
$contacts = DBA::toArray($results);
|
$contacts = DBA::toArray($results);
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
namespace Friendica\Model;
|
namespace Friendica\Model;
|
||||||
|
|
||||||
use Friendica\Content\PageInfo;
|
|
||||||
use Friendica\Content\Text\BBCode;
|
use Friendica\Content\Text\BBCode;
|
||||||
use Friendica\Content\Text\HTML;
|
use Friendica\Content\Text\HTML;
|
||||||
use Friendica\Core\Hook;
|
use Friendica\Core\Hook;
|
||||||
|
@ -173,7 +172,7 @@ class Item
|
||||||
|
|
||||||
Logger::info('Updating per single row method', ['fields' => $fields, 'condition' => $condition]);
|
Logger::info('Updating per single row method', ['fields' => $fields, 'condition' => $condition]);
|
||||||
|
|
||||||
$items = Post::select(['id', 'origin', 'uri-id', 'uid'], $condition);
|
$items = Post::select(['id', 'origin', 'uri-id', 'uid', 'author-network'], $condition);
|
||||||
|
|
||||||
$notify_items = [];
|
$notify_items = [];
|
||||||
|
|
||||||
|
@ -181,9 +180,14 @@ class Item
|
||||||
if (!empty($fields['body'])) {
|
if (!empty($fields['body'])) {
|
||||||
Post\Media::insertFromAttachmentData($item['uri-id'], $fields['body']);
|
Post\Media::insertFromAttachmentData($item['uri-id'], $fields['body']);
|
||||||
|
|
||||||
|
if ($item['author-network'] != Protocol::DFRN) {
|
||||||
|
Post\Media::insertFromRelevantUrl($item['uri-id'], $fields['body']);
|
||||||
|
}
|
||||||
|
|
||||||
$content_fields = ['raw-body' => trim($fields['raw-body'] ?? $fields['body'])];
|
$content_fields = ['raw-body' => trim($fields['raw-body'] ?? $fields['body'])];
|
||||||
|
|
||||||
// Remove all media attachments from the body and store them in the post-media table
|
// Remove all media attachments from the body and store them in the post-media table
|
||||||
|
// @todo On shared postings (Diaspora style and commented reshare) don't fetch content from the shared part
|
||||||
$content_fields['raw-body'] = Post\Media::insertFromBody($item['uri-id'], $content_fields['raw-body']);
|
$content_fields['raw-body'] = Post\Media::insertFromBody($item['uri-id'], $content_fields['raw-body']);
|
||||||
$content_fields['raw-body'] = self::setHashtags($content_fields['raw-body']);
|
$content_fields['raw-body'] = self::setHashtags($content_fields['raw-body']);
|
||||||
}
|
}
|
||||||
|
@ -959,8 +963,20 @@ class Item
|
||||||
self::setOwnerforResharedItem($item);
|
self::setOwnerforResharedItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($item['attachments'])) {
|
||||||
|
foreach ($item['attachments'] as $attachment) {
|
||||||
|
$attachment['uri-id'] = $item['uri-id'];
|
||||||
|
Post\Media::insert($attachment);
|
||||||
|
}
|
||||||
|
unset($item['attachments']);
|
||||||
|
}
|
||||||
|
|
||||||
Post\Media::insertFromAttachmentData($item['uri-id'], $item['body']);
|
Post\Media::insertFromAttachmentData($item['uri-id'], $item['body']);
|
||||||
|
|
||||||
|
if (!DBA::exists('contact', ['id' => $item['author-id'], 'network' => Protocol::DFRN])) {
|
||||||
|
Post\Media::insertFromRelevantUrl($item['uri-id'], $item['body']);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove all media attachments from the body and store them in the post-media table
|
// Remove all media attachments from the body and store them in the post-media table
|
||||||
$item['raw-body'] = Post\Media::insertFromBody($item['uri-id'], $item['raw-body']);
|
$item['raw-body'] = Post\Media::insertFromBody($item['uri-id'], $item['raw-body']);
|
||||||
$item['raw-body'] = self::setHashtags($item['raw-body']);
|
$item['raw-body'] = self::setHashtags($item['raw-body']);
|
||||||
|
@ -1706,7 +1722,7 @@ class Item
|
||||||
return ("[bookmark=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/bookmark]");
|
return ("[bookmark=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/bookmark]");
|
||||||
}, $body);
|
}, $body);
|
||||||
|
|
||||||
$body = preg_replace_callback("/\[attachment (.*)\](.*?)\[\/attachment\]/ism",
|
$body = preg_replace_callback("/\[attachment (.*?)\](.*?)\[\/attachment\]/ism",
|
||||||
function ($match) {
|
function ($match) {
|
||||||
return ("[attachment " . str_replace("#", "#", $match[1]) . "]" . $match[2] . "[/attachment]");
|
return ("[attachment " . str_replace("#", "#", $match[1]) . "]" . $match[2] . "[/attachment]");
|
||||||
}, $body);
|
}, $body);
|
||||||
|
@ -2639,10 +2655,10 @@ class Item
|
||||||
unset($hook_data);
|
unset($hook_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
$orig_body = $item['body'];
|
$body = $item['body'] ?? '';
|
||||||
$item['body'] = preg_replace("/\s*\[attachment .*\].*?\[\/attachment\]\s*/ism", '', $item['body']);
|
$item['body'] = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", "\n", $item['body']);
|
||||||
self::putInCache($item);
|
self::putInCache($item);
|
||||||
$item['body'] = $orig_body;
|
$item['body'] = $body;
|
||||||
$s = $item["rendered-html"];
|
$s = $item["rendered-html"];
|
||||||
|
|
||||||
$hook_data = [
|
$hook_data = [
|
||||||
|
@ -2666,19 +2682,23 @@ class Item
|
||||||
if (!empty($shared['guid'])) {
|
if (!empty($shared['guid'])) {
|
||||||
$shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]);
|
$shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]);
|
||||||
$shared_uri_id = $shared_item['uri-id'] ?? 0;
|
$shared_uri_id = $shared_item['uri-id'] ?? 0;
|
||||||
$shared_plink = $shared_item['plink'] ?? '';
|
$shared_links = [strtolower($shared_item['plink'] ?? '')];
|
||||||
$attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid']);
|
$attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid']);
|
||||||
$s = self::addVisualAttachments($attachments, $item, $s, true);
|
$s = self::addVisualAttachments($attachments, $item, $s, true);
|
||||||
$s = self::addLinkAttachment($attachments, $item, $s, true, '');
|
$s = self::addLinkAttachment($attachments, $body, $s, true, []);
|
||||||
$s = self::addNonVisualAttachments($attachments, $item, $s, true);
|
$s = self::addNonVisualAttachments($attachments, $item, $s, true);
|
||||||
|
$shared_links = array_merge($shared_links, array_column($attachments['visual'], 'url'));
|
||||||
|
$shared_links = array_merge($shared_links, array_column($attachments['link'], 'url'));
|
||||||
|
$shared_links = array_merge($shared_links, array_column($attachments['additional'], 'url'));
|
||||||
|
$body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
|
||||||
} else {
|
} else {
|
||||||
$shared_uri_id = 0;
|
$shared_uri_id = 0;
|
||||||
$shared_plink = '';
|
$shared_links = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid']);
|
$attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid'], $shared_links);
|
||||||
$s = self::addVisualAttachments($attachments, $item, $s, false);
|
$s = self::addVisualAttachments($attachments, $item, $s, false);
|
||||||
$s = self::addLinkAttachment($attachments, $item, $s, false, $shared_plink);
|
$s = self::addLinkAttachment($attachments, $body, $s, false, $shared_links);
|
||||||
$s = self::addNonVisualAttachments($attachments, $item, $s, false);
|
$s = self::addNonVisualAttachments($attachments, $item, $s, false);
|
||||||
|
|
||||||
// Map.
|
// Map.
|
||||||
|
@ -2712,6 +2732,12 @@ class Item
|
||||||
*/
|
*/
|
||||||
public static function containsLink(string $body, string $url)
|
public static function containsLink(string $body, string $url)
|
||||||
{
|
{
|
||||||
|
// Make sure that for example site parameters aren't used when testing if the link is contained in the body
|
||||||
|
$urlparts = parse_url($url);
|
||||||
|
unset($urlparts['query']);
|
||||||
|
unset($urlparts['fragment']);
|
||||||
|
$url = Network::unparseURL($urlparts);
|
||||||
|
|
||||||
if (strpos($body, $url)) {
|
if (strpos($body, $url)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2738,6 +2764,7 @@ class Item
|
||||||
$leading = '';
|
$leading = '';
|
||||||
$trailing = '';
|
$trailing = '';
|
||||||
|
|
||||||
|
// @todo In the future we should make a single for the template engine with all media in it. This allows more flexibilty.
|
||||||
foreach ($attachments['visual'] as $attachment) {
|
foreach ($attachments['visual'] as $attachment) {
|
||||||
if (self::containsLink($item['body'], $attachment['url'])) {
|
if (self::containsLink($item['body'], $attachment['url'])) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -2794,13 +2821,18 @@ class Item
|
||||||
'attachment' => $attachment,
|
'attachment' => $attachment,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
// On Diaspora posts the attached pictures are leading
|
||||||
|
if ($item['network'] == Protocol::DIASPORA) {
|
||||||
|
$leading .= $media;
|
||||||
|
} else {
|
||||||
$trailing .= $media;
|
$trailing .= $media;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($shared) {
|
if ($shared) {
|
||||||
$content = str_replace(BBCode::ANCHOR, '<div class="body-attach">' . $leading . '<div class="clear"></div></div>' . BBCode::ANCHOR, $content);
|
$content = str_replace(BBCode::TOP_ANCHOR, '<div class="body-attach">' . $leading . '<div class="clear"></div></div>' . BBCode::TOP_ANCHOR, $content);
|
||||||
$content = str_replace(BBCode::ANCHOR, BBCode::ANCHOR . '<div class="body-attach">' . $trailing . '<div class="clear"></div></div>', $content);
|
$content = str_replace(BBCode::BOTTOM_ANCHOR, '<div class="body-attach">' . $trailing . '<div class="clear"></div></div>' . BBCode::BOTTOM_ANCHOR, $content);
|
||||||
} else {
|
} else {
|
||||||
if ($leading != '') {
|
if ($leading != '') {
|
||||||
$content = '<div class="body-attach">' . $leading . '<div class="clear"></div></div>' . $content;
|
$content = '<div class="body-attach">' . $leading . '<div class="clear"></div></div>' . $content;
|
||||||
|
@ -2819,12 +2851,13 @@ class Item
|
||||||
* Add link attachment to the content
|
* Add link attachment to the content
|
||||||
*
|
*
|
||||||
* @param array $attachments
|
* @param array $attachments
|
||||||
* @param array $item
|
* @param string $body
|
||||||
* @param string $content
|
* @param string $content
|
||||||
* @param bool $shared
|
* @param bool $shared
|
||||||
|
* @param array $ignore_links A list of URLs to ignore
|
||||||
* @return string modified content
|
* @return string modified content
|
||||||
*/
|
*/
|
||||||
private static function addLinkAttachment(array $attachments, array $item, string $content, bool $shared, string $ignore_link)
|
private static function addLinkAttachment(array $attachments, string $body, string $content, bool $shared, array $ignore_links)
|
||||||
{
|
{
|
||||||
$stamp1 = microtime(true);
|
$stamp1 = microtime(true);
|
||||||
// @ToDo Check only for audio and video
|
// @ToDo Check only for audio and video
|
||||||
|
@ -2832,14 +2865,23 @@ class Item
|
||||||
|
|
||||||
if (!empty($attachments['link'])) {
|
if (!empty($attachments['link'])) {
|
||||||
foreach ($attachments['link'] as $link) {
|
foreach ($attachments['link'] as $link) {
|
||||||
if (!Strings::compareLink($link['url'], $ignore_link)) {
|
$found = false;
|
||||||
|
foreach ($ignore_links as $ignore_link) {
|
||||||
|
if (Strings::compareLink($link['url'], $ignore_link)) {
|
||||||
|
$found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @todo Judge between the links to use the one with most information
|
||||||
|
if (!$found && (empty($attachment) || !empty($link['author-name']) ||
|
||||||
|
(empty($attachment['name']) && !empty($link['name'])) ||
|
||||||
|
(empty($attachment['description']) && !empty($link['description'])) ||
|
||||||
|
(empty($attachment['preview']) && !empty($link['preview'])))) {
|
||||||
$attachment = $link;
|
$attachment = $link;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($attachment)) {
|
if (!empty($attachment)) {
|
||||||
$footer = '';
|
|
||||||
$data = [
|
$data = [
|
||||||
'after' => '',
|
'after' => '',
|
||||||
'author_name' => $attachment['author-name'] ?? '',
|
'author_name' => $attachment['author-name'] ?? '',
|
||||||
|
@ -2861,17 +2903,58 @@ class Item
|
||||||
$data['preview'] = $attachment['preview'] ?? '';
|
$data['preview'] = $attachment['preview'] ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif (preg_match("/.*(\[attachment.*?\].*?\[\/attachment\]).*/ism", $item['body'], $match)) {
|
|
||||||
$footer = $match[1];
|
if (!empty($data['description']) && !empty($content)) {
|
||||||
$data = [];
|
similar_text($data['description'], $content, $percent);
|
||||||
|
} else {
|
||||||
|
$percent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['description']) && (($data['title'] == $data['description']) || ($percent > 95) || (strpos($content, $data['description']) !== false))) {
|
||||||
|
$data['description'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($data['author_name'] ?? '') == ($data['provider_name'] ?? '')) {
|
||||||
|
$data['author_name'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($data['author_url'] ?? '') == ($data['provider_url'] ?? '')) {
|
||||||
|
$data['author_url'] = '';
|
||||||
|
}
|
||||||
|
} elseif (preg_match("/.*(\[attachment.*?\].*?\[\/attachment\]).*/ism", $body, $match)) {
|
||||||
|
$data = BBCode::getAttachmentData($match[1]);
|
||||||
}
|
}
|
||||||
DI::profiler()->saveTimestamp($stamp1, 'rendering');
|
DI::profiler()->saveTimestamp($stamp1, 'rendering');
|
||||||
|
|
||||||
if (!empty($footer) || !empty($data)) {
|
if (isset($data['url']) && !in_array($data['url'], $ignore_links)) {
|
||||||
|
if (!empty($data['description']) || !empty($data['image']) || !empty($data['preview'])) {
|
||||||
|
$parts = parse_url($data['url']);
|
||||||
|
if (!empty($parts['scheme']) && !empty($parts['host'])) {
|
||||||
|
if (empty($data['provider_name'])) {
|
||||||
|
$data['provider_name'] = $parts['host'];
|
||||||
|
}
|
||||||
|
if (empty($data['provider_url']) || empty(parse_url($data['provider_url'], PHP_URL_SCHEME))) {
|
||||||
|
$data['provider_url'] = $parts['scheme'] . '://' . $parts['host'];
|
||||||
|
|
||||||
|
if (!empty($parts['port'])) {
|
||||||
|
$data['provider_url'] .= ':' . $parts['port'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// @todo Use a template
|
// @todo Use a template
|
||||||
$rendered = BBCode::convertAttachment($footer, BBCode::INTERNAL, false, $data);
|
$rendered = BBCode::convertAttachment('', BBCode::INTERNAL, false, $data);
|
||||||
|
} elseif (!self::containsLink($content, $data['url'])) {
|
||||||
|
$rendered = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/link.tpl'), [
|
||||||
|
'$url' => $data['url'],
|
||||||
|
'$title' => $data['title'],
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
if ($shared) {
|
if ($shared) {
|
||||||
return str_replace(BBCode::ANCHOR, BBCode::ANCHOR . $rendered, $content);
|
return str_replace(BBCode::BOTTOM_ANCHOR, BBCode::BOTTOM_ANCHOR . $rendered, $content);
|
||||||
} else {
|
} else {
|
||||||
return $content . $rendered;
|
return $content . $rendered;
|
||||||
}
|
}
|
||||||
|
@ -2933,18 +3016,19 @@ class Item
|
||||||
'href' => "display/" . $item['guid'],
|
'href' => "display/" . $item['guid'],
|
||||||
'orig' => "display/" . $item['guid'],
|
'orig' => "display/" . $item['guid'],
|
||||||
'title' => DI::l10n()->t('View on separate page'),
|
'title' => DI::l10n()->t('View on separate page'),
|
||||||
'orig_title' => DI::l10n()->t('view on separate page'),
|
'orig_title' => DI::l10n()->t('View on separate page'),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!empty($item['plink'])) {
|
if (!empty($item['plink'])) {
|
||||||
$ret["href"] = DI::baseUrl()->remove($item['plink']);
|
$ret['href'] = DI::baseUrl()->remove($item['plink']);
|
||||||
$ret["title"] = DI::l10n()->t('link to source');
|
$ret['title'] = DI::l10n()->t('Link to source');
|
||||||
}
|
}
|
||||||
} elseif (!empty($item['plink']) && ($item['private'] != self::PRIVATE)) {
|
} elseif (!empty($item['plink']) && ($item['private'] != self::PRIVATE)) {
|
||||||
$ret = [
|
$ret = [
|
||||||
'href' => $item['plink'],
|
'href' => $item['plink'],
|
||||||
'orig' => $item['plink'],
|
'orig' => $item['plink'],
|
||||||
'title' => DI::l10n()->t('link to source'),
|
'title' => DI::l10n()->t('Link to source'),
|
||||||
|
'orig_title' => DI::l10n()->t('Link to source'),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$ret = [];
|
$ret = [];
|
||||||
|
@ -3182,4 +3266,41 @@ class Item
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Improve the data in shared posts
|
||||||
|
*
|
||||||
|
* @param array $item
|
||||||
|
* @return string body
|
||||||
|
*/
|
||||||
|
public static function improveSharedDataInBody(array $item)
|
||||||
|
{
|
||||||
|
$shared = BBCode::fetchShareAttributes($item['body']);
|
||||||
|
if (empty($shared['link'])) {
|
||||||
|
return $item['body'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = self::fetchByLink($shared['link']);
|
||||||
|
Logger::info('Fetched shared post', ['uri-id' => $item['uri-id'], 'id' => $id, 'author' => $shared['profile'], 'url' => $shared['link'], 'guid' => $shared['guid'], 'callstack' => System::callstack()]);
|
||||||
|
if (!$id) {
|
||||||
|
return $item['body'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$shared_item = Post::selectFirst(['author-name', 'author-link', 'author-avatar', 'plink', 'created', 'guid', 'title', 'body'], ['id' => $id]);
|
||||||
|
if (!DBA::isResult($shared_item)) {
|
||||||
|
return $item['body'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$shared_content = BBCode::getShareOpeningTag($shared_item['author-name'], $shared_item['author-link'], $shared_item['author-avatar'], $shared_item['plink'], $shared_item['created'], $shared_item['guid']);
|
||||||
|
|
||||||
|
if (!empty($shared_item['title'])) {
|
||||||
|
$shared_content .= '[h3]'.$shared_item['title'].'[/h3]'."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$shared_content .= $shared_item['body'];
|
||||||
|
|
||||||
|
$item['body'] = preg_replace("/\[share.*?\](.*)\[\/share\]/ism", $shared_content . '[/share]', $item['body']);
|
||||||
|
Logger::info('New shared data', ['uri-id' => $item['uri-id'], 'id' => $id, 'shared_item' => $shared_item]);
|
||||||
|
return $item['body'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -726,6 +726,26 @@ class Photo
|
||||||
* Then set the permissions to public.
|
* Then set the permissions to public.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
self::setPermissionForRessource($image_rid, $uid, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add permissions to photo ressource
|
||||||
|
* @todo mix with previous photo permissions
|
||||||
|
*
|
||||||
|
* @param string $image_rid
|
||||||
|
* @param integer $uid
|
||||||
|
* @param string $str_contact_allow
|
||||||
|
* @param string $str_group_allow
|
||||||
|
* @param string $str_contact_deny
|
||||||
|
* @param string $str_group_deny
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function setPermissionForRessource(string $image_rid, int $uid, string $str_contact_allow, string $str_group_allow, string $str_contact_deny, string $str_group_deny)
|
||||||
|
{
|
||||||
$fields = ['allow_cid' => $str_contact_allow, 'allow_gid' => $str_group_allow,
|
$fields = ['allow_cid' => $str_contact_allow, 'allow_gid' => $str_group_allow,
|
||||||
'deny_cid' => $str_contact_deny, 'deny_gid' => $str_group_deny,
|
'deny_cid' => $str_contact_deny, 'deny_gid' => $str_group_deny,
|
||||||
'accessible' => DI::pConfig()->get($uid, 'system', 'accessible-photos', false)];
|
'accessible' => DI::pConfig()->get($uid, 'system', 'accessible-photos', false)];
|
||||||
|
@ -735,9 +755,6 @@ class Photo
|
||||||
Photo::update($fields, $condition);
|
Photo::update($fields, $condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strips known picture extensions from picture links
|
* Strips known picture extensions from picture links
|
||||||
*
|
*
|
||||||
|
|
|
@ -127,12 +127,13 @@ class Post
|
||||||
* Check if post data exists
|
* Check if post data exists
|
||||||
*
|
*
|
||||||
* @param array $condition array of fields for condition
|
* @param array $condition array of fields for condition
|
||||||
|
* @param bool $user_mode true = post-user-view, false = post-view
|
||||||
*
|
*
|
||||||
* @return boolean Are there rows for that condition?
|
* @return boolean Are there rows for that condition?
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function exists($condition) {
|
public static function exists($condition, bool $user_mode = true) {
|
||||||
return DBA::exists('post-user-view', $condition);
|
return DBA::exists($user_mode ? 'post-user-view' : 'post-view', $condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,6 +141,7 @@ class Post
|
||||||
*
|
*
|
||||||
* @param array $condition array of fields for condition
|
* @param array $condition array of fields for condition
|
||||||
* @param array $params Array of several parameters
|
* @param array $params Array of several parameters
|
||||||
|
* @param bool $user_mode true = post-user-view, false = post-view
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*
|
*
|
||||||
|
@ -151,9 +153,9 @@ class Post
|
||||||
* $count = Post::count($condition);
|
* $count = Post::count($condition);
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function count(array $condition = [], array $params = [])
|
public static function count(array $condition = [], array $params = [], bool $user_mode = true)
|
||||||
{
|
{
|
||||||
return DBA::count('post-user-view', $condition, $params);
|
return DBA::count($user_mode ? 'post-user-view' : 'post-view', $condition, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -265,13 +267,14 @@ class Post
|
||||||
* @param array $selected Array of selected fields, empty for all
|
* @param array $selected Array of selected fields, empty for all
|
||||||
* @param array $condition Array of fields for condition
|
* @param array $condition Array of fields for condition
|
||||||
* @param array $params Array of several parameters
|
* @param array $params Array of several parameters
|
||||||
|
* @param bool $user_mode true = post-user-view, false = post-view
|
||||||
*
|
*
|
||||||
* @return boolean|object
|
* @return boolean|object
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function select(array $selected = [], array $condition = [], $params = [])
|
public static function select(array $selected = [], array $condition = [], $params = [], bool $user_mode = true)
|
||||||
{
|
{
|
||||||
return self::selectView('post-user-view', $selected, $condition, $params);
|
return self::selectView($user_mode ? 'post-user-view' : 'post-view', $selected, $condition, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -112,6 +112,10 @@ class Delayed
|
||||||
*/
|
*/
|
||||||
public static function publish(array $item, int $notify = 0, array $taglist = [], array $attachments = [], bool $unprepared = false, string $uri = '')
|
public static function publish(array $item, int $notify = 0, array $taglist = [], array $attachments = [], bool $unprepared = false, string $uri = '')
|
||||||
{
|
{
|
||||||
|
if (!empty($attachments)) {
|
||||||
|
$item['attachments'] = $attachments;
|
||||||
|
}
|
||||||
|
|
||||||
if ($unprepared) {
|
if ($unprepared) {
|
||||||
$_SESSION['authenticated'] = true;
|
$_SESSION['authenticated'] = true;
|
||||||
$_SESSION['uid'] = $item['uid'];
|
$_SESSION['uid'] = $item['uid'];
|
||||||
|
@ -157,11 +161,6 @@ class Delayed
|
||||||
foreach ($taglist as $tag) {
|
foreach ($taglist as $tag) {
|
||||||
Tag::store($feeditem['uri-id'], Tag::HASHTAG, $tag);
|
Tag::store($feeditem['uri-id'], Tag::HASHTAG, $tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($attachments as $attachment) {
|
|
||||||
$attachment['uri-id'] = $feeditem['uri-id'];
|
|
||||||
Media::insert($attachment);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $id;
|
return $id;
|
||||||
|
|
|
@ -21,15 +21,17 @@
|
||||||
|
|
||||||
namespace Friendica\Model\Post;
|
namespace Friendica\Model\Post;
|
||||||
|
|
||||||
use Friendica\Content\PageInfo;
|
|
||||||
use Friendica\Content\Text\BBCode;
|
use Friendica\Content\Text\BBCode;
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
use Friendica\Core\System;
|
use Friendica\Core\System;
|
||||||
use Friendica\Database\Database;
|
use Friendica\Database\Database;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Item;
|
||||||
|
use Friendica\Model\Post;
|
||||||
use Friendica\Util\Images;
|
use Friendica\Util\Images;
|
||||||
use Friendica\Util\ParseUrl;
|
use Friendica\Util\ParseUrl;
|
||||||
|
use Friendica\Util\Strings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Media
|
* Class Media
|
||||||
|
@ -64,6 +66,11 @@ class Media
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DBA::exists('post-media', ['uri-id' => $media['uri-id'], 'preview' => $media['url']])) {
|
||||||
|
Logger::info('Media already exists as preview', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'callstack' => System::callstack()]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// "document" has got the lowest priority. So when the same file is both attached as document
|
// "document" has got the lowest priority. So when the same file is both attached as document
|
||||||
// and embedded as picture then we only store the picture or replace the document
|
// and embedded as picture then we only store the picture or replace the document
|
||||||
$found = DBA::selectFirst('post-media', ['type'], ['uri-id' => $media['uri-id'], 'url' => $media['url']]);
|
$found = DBA::selectFirst('post-media', ['type'], ['uri-id' => $media['uri-id'], 'url' => $media['url']]);
|
||||||
|
@ -281,7 +288,13 @@ class Media
|
||||||
public static function insertFromBody(int $uriid, string $body)
|
public static function insertFromBody(int $uriid, string $body)
|
||||||
{
|
{
|
||||||
// Simplify image codes
|
// Simplify image codes
|
||||||
$body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
|
$unshared_body = $body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
|
||||||
|
|
||||||
|
// Only remove the shared data from "real" reshares
|
||||||
|
$shared = BBCode::fetchShareAttributes($body);
|
||||||
|
if (!empty($shared['guid'])) {
|
||||||
|
$unshared_body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
|
||||||
|
}
|
||||||
|
|
||||||
$attachments = [];
|
$attachments = [];
|
||||||
if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]\s*\[/url\]#ism", $body, $pictures, PREG_SET_ORDER)) {
|
if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]\s*\[/url\]#ism", $body, $pictures, PREG_SET_ORDER)) {
|
||||||
|
@ -336,19 +349,51 @@ class Media
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = PageInfo::getRelevantUrlFromBody($body);
|
|
||||||
if (!empty($url)) {
|
|
||||||
Logger::debug('Got page url', ['url' => $url]);
|
|
||||||
$attachments[$url] = ['uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $url];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($attachments as $attachment) {
|
foreach ($attachments as $attachment) {
|
||||||
|
// Only store attachments that are part of the unshared body
|
||||||
|
if (strpos($unshared_body, $attachment['url']) !== false) {
|
||||||
self::insert($attachment);
|
self::insert($attachment);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return trim($body);
|
return trim($body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add media links from a relevant url in the body
|
||||||
|
*
|
||||||
|
* @param integer $uriid
|
||||||
|
* @param string $body
|
||||||
|
*/
|
||||||
|
public static function insertFromRelevantUrl(int $uriid, string $body)
|
||||||
|
{
|
||||||
|
// Only remove the shared data from "real" reshares
|
||||||
|
$shared = BBCode::fetchShareAttributes($body);
|
||||||
|
if (!empty($shared['guid'])) {
|
||||||
|
// Don't look at the shared content
|
||||||
|
$body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all hashtags and mentions
|
||||||
|
$body = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '', $body);
|
||||||
|
|
||||||
|
// Search for pure links
|
||||||
|
if (preg_match_all("/\[url\](https?:.*?)\[\/url\]/ism", $body, $matches)) {
|
||||||
|
foreach ($matches[1] as $url) {
|
||||||
|
Logger::info('Got page url (link without description)', ['uri-id' => $uriid, 'url' => $url]);
|
||||||
|
self::insert(['uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $url]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for links with descriptions
|
||||||
|
if (preg_match_all("/\[url\=(https?:.*?)\].*?\[\/url\]/ism", $body, $matches)) {
|
||||||
|
foreach ($matches[1] as $url) {
|
||||||
|
Logger::info('Got page url (link with description)', ['uri-id' => $uriid, 'url' => $url]);
|
||||||
|
self::insert(['uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $url]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add media links from the attachment field
|
* Add media links from the attachment field
|
||||||
*
|
*
|
||||||
|
@ -357,6 +402,9 @@ class Media
|
||||||
*/
|
*/
|
||||||
public static function insertFromAttachmentData(int $uriid, string $body)
|
public static function insertFromAttachmentData(int $uriid, string $body)
|
||||||
{
|
{
|
||||||
|
// Don't look at the shared content
|
||||||
|
$body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
|
||||||
|
|
||||||
$data = BBCode::getAttachmentData($body);
|
$data = BBCode::getAttachmentData($body);
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
return;
|
return;
|
||||||
|
@ -449,9 +497,10 @@ class Media
|
||||||
*
|
*
|
||||||
* @param int $uri_id
|
* @param int $uri_id
|
||||||
* @param string $guid
|
* @param string $guid
|
||||||
|
* @param array $links ist of links that shouldn't be added
|
||||||
* @return array attachments
|
* @return array attachments
|
||||||
*/
|
*/
|
||||||
public static function splitAttachments(int $uri_id, string $guid = '')
|
public static function splitAttachments(int $uri_id, string $guid = '', array $links = [])
|
||||||
{
|
{
|
||||||
$attachments = ['visual' => [], 'link' => [], 'additional' => []];
|
$attachments = ['visual' => [], 'link' => [], 'additional' => []];
|
||||||
|
|
||||||
|
@ -462,8 +511,26 @@ class Media
|
||||||
|
|
||||||
$height = 0;
|
$height = 0;
|
||||||
$selected = '';
|
$selected = '';
|
||||||
|
$previews = [];
|
||||||
|
|
||||||
foreach ($media as $medium) {
|
foreach ($media as $medium) {
|
||||||
|
foreach ($links as $link) {
|
||||||
|
if (Strings::compareLink($link, $medium['url'])) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid adding separate media entries for previews
|
||||||
|
foreach ($previews as $preview) {
|
||||||
|
if (Strings::compareLink($preview, $medium['url'])) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($medium['preview'])) {
|
||||||
|
$previews[] = $medium['preview'];
|
||||||
|
}
|
||||||
|
|
||||||
$type = explode('/', current(explode(';', $medium['mimetype'])));
|
$type = explode('/', current(explode(';', $medium['mimetype'])));
|
||||||
if (count($type) < 2) {
|
if (count($type) < 2) {
|
||||||
Logger::info('Unknown MimeType', ['type' => $type, 'media' => $medium]);
|
Logger::info('Unknown MimeType', ['type' => $type, 'media' => $medium]);
|
||||||
|
@ -511,4 +578,57 @@ class Media
|
||||||
}
|
}
|
||||||
return $attachments;
|
return $attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add media attachments to the body
|
||||||
|
*
|
||||||
|
* @param int $uriid
|
||||||
|
* @param string $body
|
||||||
|
* @return string body
|
||||||
|
*/
|
||||||
|
public static function addAttachmentsToBody(int $uriid, string $body = '')
|
||||||
|
{
|
||||||
|
if (empty($body)) {
|
||||||
|
$item = Post::selectFirst(['body'], ['uri-id' => $uriid]);
|
||||||
|
if (!DBA::isResult($item)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$body = $item['body'];
|
||||||
|
}
|
||||||
|
$original_body = $body;
|
||||||
|
|
||||||
|
$body = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", '', $body);
|
||||||
|
|
||||||
|
foreach (self::getByURIId($uriid, [self::IMAGE, self::AUDIO, self::VIDEO]) as $media) {
|
||||||
|
if (Item::containsLink($body, $media['url'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($media['type'] == self::IMAGE) {
|
||||||
|
if (!empty($media['preview'])) {
|
||||||
|
if (!empty($media['description'])) {
|
||||||
|
$body .= "\n[url=" . $media['url'] . "][img=" . $media['preview'] . ']' . $media['description'] .'[/img][/url]';
|
||||||
|
} else {
|
||||||
|
$body .= "\n[url=" . $media['url'] . "][img]" . $media['preview'] .'[/img][/url]';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!empty($media['description'])) {
|
||||||
|
$body .= "\n[img=" . $media['url'] . ']' . $media['description'] .'[/img]';
|
||||||
|
} else {
|
||||||
|
$body .= "\n[img]" . $media['url'] .'[/img]';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($media['type'] == self::AUDIO) {
|
||||||
|
$body .= "\n[audio]" . $media['url'] . "[/audio]\n";
|
||||||
|
} elseif ($media['type'] == self::VIDEO) {
|
||||||
|
$body .= "\n[video]" . $media['url'] . "[/video]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match("/.*(\[attachment.*?\].*?\[\/attachment\]).*/ism", $original_body, $match)) {
|
||||||
|
$body .= "\n" . $match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -412,7 +412,12 @@ class Profile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$p = [];
|
// Expected profile/vcard.tpl profile.* template variables
|
||||||
|
$p = [
|
||||||
|
'address' => null,
|
||||||
|
'edit' => null,
|
||||||
|
'upubkey' => null,
|
||||||
|
];
|
||||||
foreach ($profile as $k => $v) {
|
foreach ($profile as $k => $v) {
|
||||||
$k = str_replace('-', '_', $k);
|
$k = str_replace('-', '_', $k);
|
||||||
$p[$k] = $v;
|
$p[$k] = $v;
|
||||||
|
|
|
@ -56,7 +56,7 @@ class Queue extends BaseAdmin
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO Move to Model\WorkerQueue::getEntries()
|
// @TODO Move to Model\WorkerQueue::getEntries()
|
||||||
$entries = DBA::select('workerqueue', ['id', 'parameter', 'created', 'priority'], $condition, ['limit' => 999, 'order' => ['created']]);
|
$entries = DBA::select('workerqueue', ['id', 'parameter', 'created', 'priority', 'command'], $condition, ['limit' => 999, 'order' => ['created']]);
|
||||||
|
|
||||||
$r = [];
|
$r = [];
|
||||||
while ($entry = DBA::fetch($entries)) {
|
while ($entry = DBA::fetch($entries)) {
|
||||||
|
@ -73,6 +73,7 @@ class Queue extends BaseAdmin
|
||||||
'$page' => $sub_title,
|
'$page' => $sub_title,
|
||||||
'$count' => count($r),
|
'$count' => count($r),
|
||||||
'$id_header' => DI::l10n()->t('ID'),
|
'$id_header' => DI::l10n()->t('ID'),
|
||||||
|
'$command_header' => DI::l10n()->t('Command'),
|
||||||
'$param_header' => DI::l10n()->t('Job Parameters'),
|
'$param_header' => DI::l10n()->t('Job Parameters'),
|
||||||
'$created_header' => DI::l10n()->t('Created'),
|
'$created_header' => DI::l10n()->t('Created'),
|
||||||
'$prio_header' => DI::l10n()->t('Priority'),
|
'$prio_header' => DI::l10n()->t('Priority'),
|
||||||
|
|
|
@ -38,7 +38,7 @@ class Accounts extends BaseApi
|
||||||
public static function rawContent(array $parameters = [])
|
public static function rawContent(array $parameters = [])
|
||||||
{
|
{
|
||||||
if (empty($parameters['id'])) {
|
if (empty($parameters['id'])) {
|
||||||
DI::mstdnError()->RecordNotFound();
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $parameters['id'];
|
$id = $parameters['id'];
|
||||||
|
@ -46,7 +46,7 @@ class Accounts extends BaseApi
|
||||||
DI::mstdnError()->RecordNotFound();
|
DI::mstdnError()->RecordNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
$account = DI::mstdnAccount()->createFromContactId($id);
|
$account = DI::mstdnAccount()->createFromContactId($id, self::getCurrentUserID());
|
||||||
System::jsonExit($account);
|
System::jsonExit($account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
90
src/Module/Api/Mastodon/Accounts/Followers.php
Normal file
90
src/Module/Api/Mastodon/Accounts/Followers.php
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Accounts;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/
|
||||||
|
*/
|
||||||
|
class Followers extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!DBA::exists('contact', ['id' => $id, 'uid' => 0])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return results older than this id
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
// Return results newer than this id
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
// Maximum number of results to return. Defaults to 20.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 20 : $_REQUEST['limit'];
|
||||||
|
|
||||||
|
|
||||||
|
$params = ['order' => ['cid' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ['relation-cid' => $id, 'follows' => true];
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`cid` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['cid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$followers = DBA::select('contact-relation', ['cid'], $condition, $parameters);
|
||||||
|
while ($follower = DBA::fetch($followers)) {
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($followers);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($accounts);
|
||||||
|
}
|
||||||
|
}
|
90
src/Module/Api/Mastodon/Accounts/Following.php
Normal file
90
src/Module/Api/Mastodon/Accounts/Following.php
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Accounts;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/
|
||||||
|
*/
|
||||||
|
class Following extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!DBA::exists('contact', ['id' => $id, 'uid' => 0])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return results older than this id
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
// Return results newer than this id
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
// Maximum number of results to return. Defaults to 20.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 20 : $_REQUEST['limit'];
|
||||||
|
|
||||||
|
|
||||||
|
$params = ['order' => ['relation-cid' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ['cid' => $id, 'follows' => true];
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`relation-cid` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['cid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$followers = DBA::select('contact-relation', ['relation-cid'], $condition, $parameters);
|
||||||
|
while ($follower = DBA::fetch($followers)) {
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['relation-cid'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($followers);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($accounts);
|
||||||
|
}
|
||||||
|
}
|
42
src/Module/Api/Mastodon/Accounts/IdentityProofs.php
Normal file
42
src/Module/Api/Mastodon/Accounts/IdentityProofs.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Accounts;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/
|
||||||
|
*/
|
||||||
|
class IdentityProofs extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
|
||||||
|
System::jsonExit([]);
|
||||||
|
}
|
||||||
|
}
|
66
src/Module/Api/Mastodon/Accounts/Lists.php
Normal file
66
src/Module/Api/Mastodon/Accounts/Lists.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Accounts;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/
|
||||||
|
*/
|
||||||
|
class Lists extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!DBA::exists('contact', ['id' => $id, 'uid' => 0])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
$lists = [];
|
||||||
|
|
||||||
|
$cdata = Contact::getPublicAndUserContacID($id, $uid);
|
||||||
|
if (!empty($cdata['user'])) {
|
||||||
|
$groups = DBA::select('group_member', ['gid'], ['contact-id' => $cdata['user']]);
|
||||||
|
while ($group = DBA::fetch($groups)) {
|
||||||
|
$lists[] = DI::mstdnList()->createFromGroupId($group['gid']);
|
||||||
|
}
|
||||||
|
DBA::close($groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($lists);
|
||||||
|
}
|
||||||
|
}
|
101
src/Module/Api/Mastodon/Accounts/Search.php
Normal file
101
src/Module/Api/Mastodon/Accounts/Search.php
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Accounts;
|
||||||
|
|
||||||
|
use Friendica\Core\Search as CoreSearch;
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Object\Search\ContactResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/
|
||||||
|
*/
|
||||||
|
class Search extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
// What to search for
|
||||||
|
$q = (int)!isset($_REQUEST['q']) ? 0 : $_REQUEST['q'];
|
||||||
|
// Maximum number of results. Defaults to 40.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit'];
|
||||||
|
// Attempt WebFinger lookup. Defaults to false. Use this when q is an exact address.
|
||||||
|
$resolve = (int)!isset($_REQUEST['resolve']) ? 0 : $_REQUEST['resolve'];
|
||||||
|
// Only who the user is following. Defaults to false.
|
||||||
|
$following = (int)!isset($_REQUEST['following']) ? 0 : $_REQUEST['following'];
|
||||||
|
|
||||||
|
$accounts = [];
|
||||||
|
|
||||||
|
if (!$following) {
|
||||||
|
if ((strrpos($q, '@') > 0) && $resolve) {
|
||||||
|
$results = CoreSearch::getContactsFromProbe($q);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($results)) {
|
||||||
|
if (DI::config()->get('system', 'poco_local_search')) {
|
||||||
|
$results = CoreSearch::getContactsFromLocalDirectory($q, CoreSearch::TYPE_ALL, 0, $limit);
|
||||||
|
} elseif (!empty(DI::config()->get('system', 'directory'))) {
|
||||||
|
$results = CoreSearch::getContactsFromGlobalDirectory($q, CoreSearch::TYPE_ALL, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($results)) {
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($results->getResults() as $result) {
|
||||||
|
if (++$counter > $limit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($result instanceof ContactResult) {
|
||||||
|
$id = Contact::getIdForURL($result->getUrl(), 0, false);
|
||||||
|
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($id, $uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$contacts = Contact::searchByName($q, '', $uid);
|
||||||
|
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($contacts as $contact) {
|
||||||
|
if (!in_array($contact['rel'], [Contact::SHARING, Contact::FRIEND])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (++$counter > $limit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($contact['id'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($contacts);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($accounts);
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ class Statuses extends BaseApi
|
||||||
public static function rawContent(array $parameters = [])
|
public static function rawContent(array $parameters = [])
|
||||||
{
|
{
|
||||||
if (empty($parameters['id'])) {
|
if (empty($parameters['id'])) {
|
||||||
DI::mstdnError()->RecordNotFound();
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $parameters['id'];
|
$id = $parameters['id'];
|
||||||
|
@ -52,7 +52,7 @@ class Statuses extends BaseApi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show only statuses with media attached? Defaults to false.
|
// Show only statuses with media attached? Defaults to false.
|
||||||
$only_media = (bool)!isset($_REQUEST['only_media']) ? false : ($_REQUEST['only_media'] == 'true'); // Currently not supported
|
$only_media = (bool)!isset($_REQUEST['only_media']) ? false : ($_REQUEST['only_media'] == 'true');
|
||||||
// Return results older than this id
|
// Return results older than this id
|
||||||
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
// Return results newer than this id
|
// Return results newer than this id
|
||||||
|
@ -64,12 +64,23 @@ class Statuses extends BaseApi
|
||||||
|
|
||||||
$params = ['order' => ['uri-id' => true], 'limit' => $limit];
|
$params = ['order' => ['uri-id' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (!$uid) {
|
||||||
$condition = ['author-id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED],
|
$condition = ['author-id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED],
|
||||||
'uid' => 0, 'network' => Protocol::FEDERATED];
|
'uid' => 0, 'network' => Protocol::FEDERATED];
|
||||||
|
} else {
|
||||||
|
$condition = ["`author-id` = ? AND (`uid` = 0 OR (`uid` = ? AND NOT `global`))", $id, $uid];
|
||||||
|
}
|
||||||
|
|
||||||
$condition = DBA::mergeConditions($condition, ["(`gravity` IN (?, ?) OR (`gravity` = ? AND `vid` = ?))",
|
$condition = DBA::mergeConditions($condition, ["(`gravity` IN (?, ?) OR (`gravity` = ? AND `vid` = ?))",
|
||||||
GRAVITY_PARENT, GRAVITY_COMMENT, GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE)]);
|
GRAVITY_PARENT, GRAVITY_COMMENT, GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE)]);
|
||||||
|
|
||||||
|
if ($only_media) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))",
|
||||||
|
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($max_id)) {
|
if (!empty($max_id)) {
|
||||||
$condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $max_id]);
|
$condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $max_id]);
|
||||||
}
|
}
|
||||||
|
@ -83,11 +94,11 @@ class Statuses extends BaseApi
|
||||||
$params['order'] = ['uri-id'];
|
$params['order'] = ['uri-id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$items = Post::selectForUser(0, ['uri-id', 'uid'], $condition, $params);
|
$items = Post::selectForUser($uid, ['uri-id'], $condition, $params);
|
||||||
|
|
||||||
$statuses = [];
|
$statuses = [];
|
||||||
while ($item = Post::fetch($items)) {
|
while ($item = Post::fetch($items)) {
|
||||||
$statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $item['uid']);
|
$statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid);
|
||||||
}
|
}
|
||||||
DBA::close($items);
|
DBA::close($items);
|
||||||
|
|
||||||
|
|
58
src/Module/Api/Mastodon/Accounts/VerifyCredentials.php
Normal file
58
src/Module/Api/Mastodon/Accounts/VerifyCredentials.php
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Accounts;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Model\User;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/
|
||||||
|
*/
|
||||||
|
class VerifyCredentials extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
$self = User::getOwnerDataById($uid);
|
||||||
|
if (empty($self)) {
|
||||||
|
DI::mstdnError()->InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cdata = Contact::getPublicAndUserContacID($self['id'], $uid);
|
||||||
|
if (empty($cdata)) {
|
||||||
|
DI::mstdnError()->InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Support the source property,
|
||||||
|
$account = DI::mstdnAccount()->createFromContactId($cdata['user'], $uid);
|
||||||
|
System::jsonExit($account);
|
||||||
|
}
|
||||||
|
}
|
43
src/Module/Api/Mastodon/Announcements.php
Normal file
43
src/Module/Api/Mastodon/Announcements.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/announcements/
|
||||||
|
*/
|
||||||
|
class Announcements extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
|
||||||
|
// @todo Possibly use the message from the pageheader addon for this
|
||||||
|
System::jsonExit([]);
|
||||||
|
}
|
||||||
|
}
|
84
src/Module/Api/Mastodon/Apps.php
Normal file
84
src/Module/Api/Mastodon/Apps.php
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Util\Network;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apps class to register new OAuth clients
|
||||||
|
*/
|
||||||
|
class Apps extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function post(array $parameters = [])
|
||||||
|
{
|
||||||
|
// Workaround for AndStatus, see issue https://github.com/andstatus/andstatus/issues/538
|
||||||
|
if (empty($_REQUEST['client_name']) || empty($_REQUEST['redirect_uris'])) {
|
||||||
|
$postdata = Network::postdata();
|
||||||
|
if (!empty($postdata)) {
|
||||||
|
$_REQUEST = json_decode($postdata, true);
|
||||||
|
if (empty($_REQUEST)) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Missing parameters'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $_REQUEST['client_name'] ?? '';
|
||||||
|
$redirect = $_REQUEST['redirect_uris'] ?? '';
|
||||||
|
$scopes = $_REQUEST['scopes'] ?? 'read';
|
||||||
|
$website = $_REQUEST['website'] ?? '';
|
||||||
|
|
||||||
|
if (empty($name) || empty($redirect)) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Missing parameters'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$client_id = bin2hex(random_bytes(32));
|
||||||
|
$client_secret = bin2hex(random_bytes(32));
|
||||||
|
|
||||||
|
$fields = ['client_id' => $client_id, 'client_secret' => $client_secret, 'name' => $name, 'redirect_uri' => $redirect];
|
||||||
|
|
||||||
|
if (!empty($scopes)) {
|
||||||
|
$fields['scopes'] = $scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields['read'] = (stripos($scopes, 'read') !== false);
|
||||||
|
$fields['write'] = (stripos($scopes, 'write') !== false);
|
||||||
|
$fields['follow'] = (stripos($scopes, 'follow') !== false);
|
||||||
|
|
||||||
|
if (!empty($website)) {
|
||||||
|
$fields['website'] = $website;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DBA::insert('application', $fields)) {
|
||||||
|
DI::mstdnError()->InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit(DI::mstdnApplication()->createFromApplicationId(DBA::lastInsertId()));
|
||||||
|
}
|
||||||
|
}
|
89
src/Module/Api/Mastodon/Blocks.php
Normal file
89
src/Module/Api/Mastodon/Blocks.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/blocks/
|
||||||
|
*/
|
||||||
|
class Blocks extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!DBA::exists('contact', ['id' => $id, 'uid' => 0])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return results older than this id
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
// Return results newer than this id
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
// Maximum number of results. Defaults to 40.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit'];
|
||||||
|
|
||||||
|
$params = ['order' => ['cid' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ['cid' => $id, 'blocked' => true, 'uid' => $uid];
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`cid` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['cid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$followers = DBA::select('user-contact', ['cid'], $condition, $parameters);
|
||||||
|
while ($follower = DBA::fetch($followers)) {
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($followers);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($accounts);
|
||||||
|
}
|
||||||
|
}
|
86
src/Module/Api/Mastodon/Bookmarks.php
Normal file
86
src/Module/Api/Mastodon/Bookmarks.php
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Network\HTTPException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/bookmarks/
|
||||||
|
*/
|
||||||
|
class Bookmarks extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
// Maximum number of results to return. Defaults to 20.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 20 : $_REQUEST['limit'];
|
||||||
|
// Return results older than id
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
// Return results newer than id
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
// Return results immediately newer than id
|
||||||
|
$min_id = (int)!isset($_REQUEST['min_id']) ? 0 : $_REQUEST['min_id'];
|
||||||
|
|
||||||
|
$params = ['order' => ['uri-id' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ['pinned' => true, 'uid' => $uid];
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['uri-id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = Post::selectThreadForUser($uid, ['uri-id'], $condition, $params);
|
||||||
|
|
||||||
|
$statuses = [];
|
||||||
|
while ($item = Post::fetch($items)) {
|
||||||
|
$statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($items);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($statuses);
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,7 +44,7 @@ class Directory extends BaseApi
|
||||||
{
|
{
|
||||||
$offset = (int)!isset($_REQUEST['offset']) ? 0 : $_REQUEST['offset'];
|
$offset = (int)!isset($_REQUEST['offset']) ? 0 : $_REQUEST['offset'];
|
||||||
$limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit'];
|
$limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit'];
|
||||||
$order = !isset($_REQUEST['order']) ? 'active' : $_REQUEST['order'];
|
$order = $_REQUEST['order'] ?? 'active';
|
||||||
$local = (bool)!isset($_REQUEST['local']) ? false : ($_REQUEST['local'] == 'true');
|
$local = (bool)!isset($_REQUEST['local']) ? false : ($_REQUEST['local'] == 'true');
|
||||||
|
|
||||||
Logger::info('directory', ['offset' => $offset, 'limit' => $limit, 'order' => $order, 'local' => $local]);
|
Logger::info('directory', ['offset' => $offset, 'limit' => $limit, 'order' => $order, 'local' => $local]);
|
||||||
|
|
40
src/Module/Api/Mastodon/Endorsements.php
Normal file
40
src/Module/Api/Mastodon/Endorsements.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/endorsements/
|
||||||
|
*/
|
||||||
|
class Endorsements extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
System::jsonExit([]);
|
||||||
|
}
|
||||||
|
}
|
81
src/Module/Api/Mastodon/Favourited.php
Normal file
81
src/Module/Api/Mastodon/Favourited.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Network\HTTPException;
|
||||||
|
use Friendica\Protocol\Activity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/favourites/
|
||||||
|
*/
|
||||||
|
class Favourited extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
// Maximum number of results to return. Defaults to 20.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 20 : $_REQUEST['limit'];
|
||||||
|
// Return results immediately newer than id
|
||||||
|
$min_id = (int)!isset($_REQUEST['min_id']) ? 0 : $_REQUEST['min_id'];
|
||||||
|
// Return results older than id
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
|
||||||
|
$params = ['order' => ['thr-parent-id' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ['gravity' => GRAVITY_ACTIVITY, 'verb' => Activity::LIKE, 'uid' => $uid];
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`thr-parent-id` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`thr-parent-id` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['thr-parent-id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = Post::selectForUser($uid, ['thr-parent-id'], $condition, $params);
|
||||||
|
|
||||||
|
$statuses = [];
|
||||||
|
while ($item = Post::fetch($items)) {
|
||||||
|
$statuses[] = DI::mstdnStatus()->createFromUriId($item['thr-parent-id'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($items);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($statuses);
|
||||||
|
}
|
||||||
|
}
|
115
src/Module/Api/Mastodon/Lists.php
Normal file
115
src/Module/Api/Mastodon/Lists.php
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Model\Group;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/timelines/lists/
|
||||||
|
*/
|
||||||
|
class Lists extends BaseApi
|
||||||
|
{
|
||||||
|
public static function delete(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Group::exists($parameters['id'], $uid)) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Group::remove($parameters['id'])) {
|
||||||
|
DI::mstdnError()->InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function post(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
$title = $_REQUEST['title'] ?? '';
|
||||||
|
|
||||||
|
if (empty($title)) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
Group::create($uid, $title);
|
||||||
|
|
||||||
|
$id = Group::getIdByName($uid, $title);
|
||||||
|
if (!$id) {
|
||||||
|
DI::mstdnError()->InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit(DI::mstdnList()->createFromGroupId($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function put(array $parameters = [])
|
||||||
|
{
|
||||||
|
$data = self::getPutData();
|
||||||
|
|
||||||
|
if (empty($data['title']) || empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
Group::update($parameters['id'], $data['title']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
$lists = [];
|
||||||
|
|
||||||
|
$groups = Group::getByUserId($uid);
|
||||||
|
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
$lists[] = DI::mstdnList()->createFromGroupId($group['id']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$id = $parameters['id'];
|
||||||
|
|
||||||
|
if (!Group::exists($id, $uid)) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
$lists = DI::mstdnList()->createFromGroupId($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($lists);
|
||||||
|
}
|
||||||
|
}
|
110
src/Module/Api/Mastodon/Lists/Accounts.php
Normal file
110
src/Module/Api/Mastodon/Lists/Accounts.php
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Lists;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/timelines/lists/
|
||||||
|
*
|
||||||
|
* Currently the output will be unordered since we use public contact ids in the api and not user contact ids.
|
||||||
|
*/
|
||||||
|
class Accounts extends BaseApi
|
||||||
|
{
|
||||||
|
public static function delete(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::unsupported('delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function post(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::unsupported('post');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!DBA::exists('group', ['id' => $id, 'uid' => $uid])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return results older than this id
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
// Return results newer than this id
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
// Maximum number of results. Defaults to 40. Max 40.
|
||||||
|
// Set to 0 in order to get all accounts without pagination.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit'];
|
||||||
|
|
||||||
|
|
||||||
|
$params = ['order' => ['contact-id' => true]];
|
||||||
|
|
||||||
|
if ($limit != 0) {
|
||||||
|
$params['limit'] = $limit;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$condition = ['gid' => $id];
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`contact-id` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`contact-id` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`contact-id` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['contact-id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$accounts = [];
|
||||||
|
|
||||||
|
$members = DBA::select('group_member', ['contact-id'], $condition, $params);
|
||||||
|
while ($member = DBA::fetch($members)) {
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($member['contact-id'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($members);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($accounts);
|
||||||
|
}
|
||||||
|
}
|
47
src/Module/Api/Mastodon/Markers.php
Normal file
47
src/Module/Api/Mastodon/Markers.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/timelines/markers/
|
||||||
|
*/
|
||||||
|
class Markers extends BaseApi
|
||||||
|
{
|
||||||
|
public static function post(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::unsupported('post');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
|
||||||
|
System::jsonExit([]);
|
||||||
|
}
|
||||||
|
}
|
60
src/Module/Api/Mastodon/Media.php
Normal file
60
src/Module/Api/Mastodon/Media.php
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Photo;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/statuses/media/
|
||||||
|
*/
|
||||||
|
class Media extends BaseApi
|
||||||
|
{
|
||||||
|
public static function put(array $parameters = [])
|
||||||
|
{
|
||||||
|
$data = self::getPutData();
|
||||||
|
self::unsupported('put');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!Photo::exists(['id' => $id, 'uid' => $uid])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit(DI::mstdnAttachment()->createFromPhoto($id));
|
||||||
|
}
|
||||||
|
}
|
89
src/Module/Api/Mastodon/Mutes.php
Normal file
89
src/Module/Api/Mastodon/Mutes.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/mutes/
|
||||||
|
*/
|
||||||
|
class Mutes extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!DBA::exists('contact', ['id' => $id, 'uid' => 0])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return results older than this id
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
// Return results newer than this id
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
// Maximum number of results. Defaults to 40.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit'];
|
||||||
|
|
||||||
|
$params = ['order' => ['cid' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ['cid' => $id, 'ignored' => true, 'uid' => $uid];
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`cid` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['cid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$followers = DBA::select('user-contact', ['cid'], $condition, $parameters);
|
||||||
|
while ($follower = DBA::fetch($followers)) {
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($followers);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($accounts);
|
||||||
|
}
|
||||||
|
}
|
122
src/Module/Api/Mastodon/Notifications.php
Normal file
122
src/Module/Api/Mastodon/Notifications.php
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Model\Notification;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/mutes/
|
||||||
|
*/
|
||||||
|
class Notifications extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (!empty($parameters['id'])) {
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!DBA::exists('notify', ['id' => $id, 'uid' => $uid])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
System::jsonExit(DI::mstdnNotification()->createFromNotifyId($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return results older than this ID
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
|
||||||
|
// Return results newer than this ID
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
|
||||||
|
// Return results immediately newer than this ID
|
||||||
|
$min_id = (int)!isset($_REQUEST['min_id']) ? 0 : $_REQUEST['min_id'];
|
||||||
|
|
||||||
|
// Maximum number of results to return (default 20)
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 20 : $_REQUEST['limit'];
|
||||||
|
|
||||||
|
// Array of types to exclude (follow, favourite, reblog, mention, poll, follow_request)
|
||||||
|
$exclude_types = $_REQUEST['exclude_types'] ?? [];
|
||||||
|
|
||||||
|
// Return only notifications received from this account
|
||||||
|
$account_id = (int)!isset($_REQUEST['account_id']) ? 0 : $_REQUEST['account_id'];
|
||||||
|
|
||||||
|
$params = ['order' => ['id' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ['uid' => $uid, 'seen' => false, 'type' => []];
|
||||||
|
|
||||||
|
if (!empty($account_id)) {
|
||||||
|
$contact = Contact::getById($account_id, ['url']);
|
||||||
|
if (!empty($contact['url'])) {
|
||||||
|
$condition['url'] = $contact['url'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array('follow_request', $exclude_types)) {
|
||||||
|
$condition['type'] = array_merge($condition['type'], [Notification\Type::INTRO]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array('mention', $exclude_types)) {
|
||||||
|
$condition['type'] = array_merge($condition['type'],
|
||||||
|
[Notification\Type::WALL, Notification\Type::COMMENT, Notification\Type::MAIL, Notification\Type::TAG_SELF, Notification\Type::POKE]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array('status', $exclude_types)) {
|
||||||
|
$condition['type'] = array_merge($condition['type'], [Notification\Type::SHARE]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`id` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`id` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`id` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$notifications = [];
|
||||||
|
|
||||||
|
$notify = DBA::select('notify', ['id'], $condition, $params);
|
||||||
|
while ($notification = DBA::fetch($notify)) {
|
||||||
|
$notifications[] = DI::mstdnNotification()->createFromNotifyId($notification['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($notifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($notifications);
|
||||||
|
}
|
||||||
|
}
|
61
src/Module/Api/Mastodon/Preferences.php
Normal file
61
src/Module/Api/Mastodon/Preferences.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\User;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/preferences/
|
||||||
|
*/
|
||||||
|
class Preferences extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
$user = User::getById($uid, ['language', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']);
|
||||||
|
if (!empty($user['allow_cid']) || !empty($user['allow_gid']) || !empty($user['deny_cid']) || !empty($user['deny_gid'])) {
|
||||||
|
$visibility = 'private';
|
||||||
|
} elseif (DI::pConfig()->get($uid, 'system', 'unlisted')) {
|
||||||
|
$visibility = 'unlisted';
|
||||||
|
} else {
|
||||||
|
$visibility = 'public';
|
||||||
|
}
|
||||||
|
|
||||||
|
$sensitive = false;
|
||||||
|
$language = $user['language'];
|
||||||
|
$media = DI::pConfig()->get($uid, 'nsfw', 'disable') ? 'show_all' : 'default';
|
||||||
|
$spoilers = DI::pConfig()->get($uid, 'system', 'disable_cw');
|
||||||
|
|
||||||
|
$preferences = new \Friendica\Object\Api\Mastodon\Preferences($visibility, $sensitive, $language, $media, $spoilers);
|
||||||
|
|
||||||
|
System::jsonExit($preferences);
|
||||||
|
}
|
||||||
|
}
|
40
src/Module/Api/Mastodon/Proofs.php
Normal file
40
src/Module/Api/Mastodon/Proofs.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/proofs/
|
||||||
|
*/
|
||||||
|
class Proofs extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
System::jsonError(404, ['error' => 'Record not found']);
|
||||||
|
}
|
||||||
|
}
|
40
src/Module/Api/Mastodon/ScheduledStatuses.php
Normal file
40
src/Module/Api/Mastodon/ScheduledStatuses.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/
|
||||||
|
*/
|
||||||
|
class ScheduledStatuses extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
System::jsonExit([]);
|
||||||
|
}
|
||||||
|
}
|
56
src/Module/Api/Mastodon/Statuses.php
Normal file
56
src/Module/Api/Mastodon/Statuses.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/statuses/
|
||||||
|
*/
|
||||||
|
class Statuses extends BaseApi
|
||||||
|
{
|
||||||
|
public static function post(array $parameters = [])
|
||||||
|
{
|
||||||
|
$data = self::getJsonPostData();
|
||||||
|
self::unsupported('post');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function delete(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::unsupported('delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit(DI::mstdnStatus()->createFromUriId($parameters['id'], self::getCurrentUserID()));
|
||||||
|
}
|
||||||
|
}
|
115
src/Module/Api/Mastodon/Statuses/Context.php
Normal file
115
src/Module/Api/Mastodon/Statuses/Context.php
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Statuses;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/statuses/
|
||||||
|
*/
|
||||||
|
class Context extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
|
||||||
|
$parent = Post::selectFirst(['parent-uri-id'], ['uri-id' => $id]);
|
||||||
|
if (!DBA::isResult($parent)) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
$parents = [];
|
||||||
|
$children = [];
|
||||||
|
|
||||||
|
$posts = Post::select(['uri-id', 'thr-parent-id'],
|
||||||
|
['parent-uri-id' => $parent['parent-uri-id'], 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]], [], false);
|
||||||
|
while ($post = Post::fetch($posts)) {
|
||||||
|
if ($post['uri-id'] == $post['thr-parent-id']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$parents[$post['uri-id']] = $post['thr-parent-id'];
|
||||||
|
|
||||||
|
$children[$post['thr-parent-id']][] = $post['uri-id'];
|
||||||
|
}
|
||||||
|
DBA::close($posts);
|
||||||
|
|
||||||
|
$statuses = ['ancestors' => [], 'descendants' => []];
|
||||||
|
|
||||||
|
$ancestors = [];
|
||||||
|
foreach (self::getParents($id, $parents) as $ancestor) {
|
||||||
|
$ancestors[$ancestor] = DI::mstdnStatus()->createFromUriId($ancestor, $uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($ancestors);
|
||||||
|
foreach ($ancestors as $ancestor) {
|
||||||
|
$statuses['ancestors'][] = $ancestor;
|
||||||
|
}
|
||||||
|
|
||||||
|
$descendants = [];
|
||||||
|
foreach (self::getChildren($id, $children) as $descendant) {
|
||||||
|
$descendants[] = DI::mstdnStatus()->createFromUriId($descendant, $uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($descendants);
|
||||||
|
foreach ($descendants as $descendant) {
|
||||||
|
$statuses['descendants'][] = $descendant;
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getParents(int $id, array $parents, array $list = [])
|
||||||
|
{
|
||||||
|
if (!empty($parents[$id])) {
|
||||||
|
$list[] = $parents[$id];
|
||||||
|
|
||||||
|
$list = self::getParents($parents[$id], $parents, $list);
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getChildren(int $id, array $children, array $list = [])
|
||||||
|
{
|
||||||
|
if (!empty($children[$id])) {
|
||||||
|
foreach ($children[$id] as $child) {
|
||||||
|
$list[] = $child;
|
||||||
|
|
||||||
|
$list = self::getChildren($child, $children, $list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
}
|
62
src/Module/Api/Mastodon/Statuses/FavouritedBy.php
Normal file
62
src/Module/Api/Mastodon/Statuses/FavouritedBy.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Statuses;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Protocol\Activity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/statuses/
|
||||||
|
*/
|
||||||
|
class FavouritedBy extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!Post::exists(['uri-id' => $id, 'uid' => [0, $uid]])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
$activities = Post::select(['author-id'], ['thr-parent-id' => $id, 'gravity' => GRAVITY_ACTIVITY, 'verb' => Activity::LIKE], [], false);
|
||||||
|
|
||||||
|
$accounts = [];
|
||||||
|
|
||||||
|
while ($activity = Post::fetch($activities)) {
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($activity['author-id'], $uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($accounts);
|
||||||
|
}
|
||||||
|
}
|
62
src/Module/Api/Mastodon/Statuses/RebloggedBy.php
Normal file
62
src/Module/Api/Mastodon/Statuses/RebloggedBy.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Statuses;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Protocol\Activity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/statuses/
|
||||||
|
*/
|
||||||
|
class RebloggedBy extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $parameters['id'];
|
||||||
|
if (!Post::exists(['uri-id' => $id, 'uid' => [0, $uid]])) {
|
||||||
|
DI::mstdnError()->RecordNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
$activities = Post::select(['author-id'], ['thr-parent-id' => $id, 'gravity' => GRAVITY_ACTIVITY, 'verb' => Activity::ANNOUNCE], [], false);
|
||||||
|
|
||||||
|
$accounts = [];
|
||||||
|
|
||||||
|
while ($activity = Post::fetch($activities)) {
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($activity['author-id'], $uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($accounts);
|
||||||
|
}
|
||||||
|
}
|
56
src/Module/Api/Mastodon/Suggestions.php
Normal file
56
src/Module/Api/Mastodon/Suggestions.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/accounts/suggestions/
|
||||||
|
*/
|
||||||
|
class Suggestions extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
// Maximum number of results to return. Defaults to 40.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit'];
|
||||||
|
|
||||||
|
$suggestions = Contact\Relation::getSuggestions($uid, 0, $limit);
|
||||||
|
|
||||||
|
$accounts = [];
|
||||||
|
|
||||||
|
foreach ($suggestions as $suggestion) {
|
||||||
|
$accounts[] = DI::mstdnAccount()->createFromContactId($suggestion['id'], $uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($accounts);
|
||||||
|
}
|
||||||
|
}
|
92
src/Module/Api/Mastodon/Timelines/Home.php
Normal file
92
src/Module/Api/Mastodon/Timelines/Home.php
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Timelines;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Network\HTTPException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/timelines/
|
||||||
|
*/
|
||||||
|
class Home extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
// Return results older than id
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
// Return results newer than id
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
// Return results immediately newer than id
|
||||||
|
$min_id = (int)!isset($_REQUEST['min_id']) ? 0 : $_REQUEST['min_id'];
|
||||||
|
// Maximum number of results to return. Defaults to 20.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 20 : $_REQUEST['limit'];
|
||||||
|
// Return only local statuses? Defaults to false.
|
||||||
|
$local = (bool)!isset($_REQUEST['local']) ? false : ($_REQUEST['local'] == 'true');
|
||||||
|
|
||||||
|
$params = ['order' => ['uri-id' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ['gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'uid' => $uid];
|
||||||
|
|
||||||
|
if ($local) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin`)"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['uri-id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = Post::selectForUser($uid, ['uri-id'], $condition, $params);
|
||||||
|
|
||||||
|
$statuses = [];
|
||||||
|
while ($item = Post::fetch($items)) {
|
||||||
|
$statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($items);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($statuses);
|
||||||
|
}
|
||||||
|
}
|
91
src/Module/Api/Mastodon/Timelines/ListTimeline.php
Normal file
91
src/Module/Api/Mastodon/Timelines/ListTimeline.php
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Timelines;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Network\HTTPException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/timelines/
|
||||||
|
*/
|
||||||
|
class ListTimeline extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['id'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return results older than id
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
// Return results newer than id
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
// Return results immediately newer than id
|
||||||
|
$min_id = (int)!isset($_REQUEST['min_id']) ? 0 : $_REQUEST['min_id'];
|
||||||
|
// Maximum number of results to return. Defaults to 20.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 20 : $_REQUEST['limit'];
|
||||||
|
|
||||||
|
$params = ['order' => ['uri-id' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `contact-id` IN (SELECT `contact-id` FROM `group_member` WHERE `gid` = ?)",
|
||||||
|
$uid, GRAVITY_PARENT, GRAVITY_COMMENT, $parameters['id']];
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['uri-id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = Post::selectForUser($uid, ['uri-id'], $condition, $params);
|
||||||
|
|
||||||
|
$statuses = [];
|
||||||
|
while ($item = Post::fetch($items)) {
|
||||||
|
$statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($items);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($statuses);
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,7 +46,7 @@ class PublicTimeline extends BaseApi
|
||||||
// Show only remote statuses? Defaults to false.
|
// Show only remote statuses? Defaults to false.
|
||||||
$remote = (bool)!isset($_REQUEST['remote']) ? false : ($_REQUEST['remote'] == 'true');
|
$remote = (bool)!isset($_REQUEST['remote']) ? false : ($_REQUEST['remote'] == 'true');
|
||||||
// Show only statuses with media attached? Defaults to false.
|
// Show only statuses with media attached? Defaults to false.
|
||||||
$only_media = (bool)!isset($_REQUEST['only_media']) ? false : ($_REQUEST['only_media'] == 'true'); // Currently not supported
|
$only_media = (bool)!isset($_REQUEST['only_media']) ? false : ($_REQUEST['only_media'] == 'true');
|
||||||
// Return results older than this id
|
// Return results older than this id
|
||||||
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
// Return results newer than this id
|
// Return results newer than this id
|
||||||
|
@ -69,6 +69,11 @@ class PublicTimeline extends BaseApi
|
||||||
$condition = DBA::mergeConditions($condition, ["NOT `uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin`)"]);
|
$condition = DBA::mergeConditions($condition, ["NOT `uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin`)"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($only_media) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))",
|
||||||
|
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($max_id)) {
|
if (!empty($max_id)) {
|
||||||
$condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $max_id]);
|
$condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $max_id]);
|
||||||
}
|
}
|
||||||
|
|
106
src/Module/Api/Mastodon/Timelines/Tag.php
Normal file
106
src/Module/Api/Mastodon/Timelines/Tag.php
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\Api\Mastodon\Timelines;
|
||||||
|
|
||||||
|
use Friendica\Core\Protocol;
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
use Friendica\Network\HTTPException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/methods/timelines/
|
||||||
|
*/
|
||||||
|
class Tag extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::login();
|
||||||
|
$uid = self::getCurrentUserID();
|
||||||
|
|
||||||
|
if (empty($parameters['hashtag'])) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If true, return only local statuses. Defaults to false.
|
||||||
|
$local = (bool)!isset($_REQUEST['local']) ? false : ($_REQUEST['local'] == 'true');
|
||||||
|
// If true, return only statuses with media attachments. Defaults to false.
|
||||||
|
$only_media = (bool)!isset($_REQUEST['only_media']) ? false : ($_REQUEST['only_media'] == 'true');
|
||||||
|
// Return results older than this ID.
|
||||||
|
$max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
|
||||||
|
// Return results newer than this ID.
|
||||||
|
$since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
|
||||||
|
// Return results immediately newer than this ID.
|
||||||
|
$min_id = (int)!isset($_REQUEST['min_id']) ? 0 : $_REQUEST['min_id'];
|
||||||
|
// Maximum number of results to return. Defaults to 20.
|
||||||
|
$limit = (int)!isset($_REQUEST['limit']) ? 20 : $_REQUEST['limit'];
|
||||||
|
|
||||||
|
$params = ['order' => ['uri-id' => true], 'limit' => $limit];
|
||||||
|
|
||||||
|
$condition = ["`name` = ? AND (`uid` = ? OR (`uid` = ? AND NOT `global`))
|
||||||
|
AND (`network` IN (?, ?, ?, ?) OR (`uid` = ? AND `uid` != ?))",
|
||||||
|
$parameters['hashtag'], 0, $uid, Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, $uid, 0];
|
||||||
|
|
||||||
|
if ($local) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin`)"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($only_media) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))",
|
||||||
|
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($max_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $max_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($since_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $since_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $min_id]);
|
||||||
|
|
||||||
|
$params['order'] = ['uri-id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = DBA::select('tag-search-view', ['uri-id'], $condition, $params);
|
||||||
|
|
||||||
|
$statuses = [];
|
||||||
|
while ($item = Post::fetch($items)) {
|
||||||
|
$statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid);
|
||||||
|
}
|
||||||
|
DBA::close($items);
|
||||||
|
|
||||||
|
if (!empty($min_id)) {
|
||||||
|
array_reverse($statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::jsonExit($statuses);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,9 +21,6 @@
|
||||||
|
|
||||||
namespace Friendica\Module\Api\Mastodon;
|
namespace Friendica\Module\Api\Mastodon;
|
||||||
|
|
||||||
use Friendica\Core\Logger;
|
|
||||||
use Friendica\Core\System;
|
|
||||||
use Friendica\DI;
|
|
||||||
use Friendica\Module\BaseApi;
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,17 +28,48 @@ use Friendica\Module\BaseApi;
|
||||||
*/
|
*/
|
||||||
class Unimplemented extends BaseApi
|
class Unimplemented extends BaseApi
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function delete(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::unsupported('delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function patch(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::unsupported('patch');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function post(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::unsupported('post');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function put(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::unsupported('put');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $parameters
|
* @param array $parameters
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
*/
|
*/
|
||||||
public static function rawContent(array $parameters = [])
|
public static function rawContent(array $parameters = [])
|
||||||
{
|
{
|
||||||
$path = DI::args()->getQueryString();
|
self::unsupported('get');
|
||||||
Logger::info('Unimplemented API call', ['path' => $path]);
|
|
||||||
$error = DI::l10n()->t('API endpoint "%s" is not implemented', $path);
|
|
||||||
$error_description = DI::l10n()->t('The API endpoint is currently not implemented but might be in the future.');;
|
|
||||||
$errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
|
|
||||||
System::jsonError(501, $errorobj->toArray());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,14 @@
|
||||||
namespace Friendica\Module;
|
namespace Friendica\Module;
|
||||||
|
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\Database;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Network\HTTPException;
|
use Friendica\Network\HTTPException;
|
||||||
|
use Friendica\Util\DateTimeFormat;
|
||||||
|
use Friendica\Util\Network;
|
||||||
|
|
||||||
require_once __DIR__ . '/../../include/api.php';
|
require_once __DIR__ . '/../../include/api.php';
|
||||||
|
|
||||||
|
@ -53,6 +59,32 @@ class BaseApi extends BaseModule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function delete(array $parameters = [])
|
||||||
|
{
|
||||||
|
if (!api_user()) {
|
||||||
|
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Permission denied.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$a = DI::app();
|
||||||
|
|
||||||
|
if (!empty($a->user['uid']) && $a->user['uid'] != api_user()) {
|
||||||
|
throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function patch(array $parameters = [])
|
||||||
|
{
|
||||||
|
if (!api_user()) {
|
||||||
|
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Permission denied.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$a = DI::app();
|
||||||
|
|
||||||
|
if (!empty($a->user['uid']) && $a->user['uid'] != api_user()) {
|
||||||
|
throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function post(array $parameters = [])
|
public static function post(array $parameters = [])
|
||||||
{
|
{
|
||||||
if (!api_user()) {
|
if (!api_user()) {
|
||||||
|
@ -66,6 +98,74 @@ class BaseApi extends BaseModule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function put(array $parameters = [])
|
||||||
|
{
|
||||||
|
if (!api_user()) {
|
||||||
|
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Permission denied.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$a = DI::app();
|
||||||
|
|
||||||
|
if (!empty($a->user['uid']) && $a->user['uid'] != api_user()) {
|
||||||
|
throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quit execution with the message that the endpoint isn't implemented
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function unsupported(string $method = 'all')
|
||||||
|
{
|
||||||
|
$path = DI::args()->getQueryString();
|
||||||
|
Logger::info('Unimplemented API call', ['method' => $method, 'path' => $path, 'agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'request' => $_REQUEST ?? []]);
|
||||||
|
$error = DI::l10n()->t('API endpoint %s %s is not implemented', strtoupper($method), $path);
|
||||||
|
$error_description = DI::l10n()->t('The API endpoint is currently not implemented but might be in the future.');
|
||||||
|
$errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
|
||||||
|
System::jsonError(501, $errorobj->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get post data that is transmitted as JSON
|
||||||
|
*
|
||||||
|
* @return array request data
|
||||||
|
*/
|
||||||
|
public static function getJsonPostData()
|
||||||
|
{
|
||||||
|
$postdata = Network::postdata();
|
||||||
|
if (empty($postdata)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($postdata, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get request data for put requests
|
||||||
|
*
|
||||||
|
* @return array request data
|
||||||
|
*/
|
||||||
|
public static function getPutData()
|
||||||
|
{
|
||||||
|
$rawdata = Network::postdata();
|
||||||
|
if (empty($rawdata)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$putdata = [];
|
||||||
|
|
||||||
|
foreach (explode('&', $rawdata) as $value) {
|
||||||
|
$data = explode('=', $value);
|
||||||
|
if (count($data) == 2) {
|
||||||
|
$putdata[$data[0]] = urldecode($data[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $putdata;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log in user via OAuth1 or Simple HTTP Auth.
|
* Log in user via OAuth1 or Simple HTTP Auth.
|
||||||
*
|
*
|
||||||
|
@ -84,13 +184,138 @@ class BaseApi extends BaseModule
|
||||||
*/
|
*/
|
||||||
protected static function login()
|
protected static function login()
|
||||||
{
|
{
|
||||||
|
if (empty(self::$current_user_id)) {
|
||||||
|
self::$current_user_id = self::getUserByBearer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty(self::$current_user_id)) {
|
||||||
|
// The execution stops here if no one is logged in
|
||||||
api_login(DI::app());
|
api_login(DI::app());
|
||||||
|
}
|
||||||
|
|
||||||
self::$current_user_id = api_user();
|
self::$current_user_id = api_user();
|
||||||
|
|
||||||
return (bool)self::$current_user_id;
|
return (bool)self::$current_user_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current user id, returns 0 if not logged in
|
||||||
|
*
|
||||||
|
* @return int User ID
|
||||||
|
*/
|
||||||
|
protected static function getCurrentUserID()
|
||||||
|
{
|
||||||
|
if (empty(self::$current_user_id)) {
|
||||||
|
self::$current_user_id = self::getUserByBearer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty(self::$current_user_id)) {
|
||||||
|
// Fetch the user id if logged in - but don't fail if not
|
||||||
|
api_login(DI::app(), false);
|
||||||
|
|
||||||
|
self::$current_user_id = api_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)self::$current_user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user id via the Bearer token
|
||||||
|
*
|
||||||
|
* @return int User-ID
|
||||||
|
*/
|
||||||
|
private static function getUserByBearer()
|
||||||
|
{
|
||||||
|
$authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
|
||||||
|
|
||||||
|
if (substr($authorization, 0, 7) != 'Bearer ') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bearer = trim(substr($authorization, 7));
|
||||||
|
$condition = ['access_token' => $bearer];
|
||||||
|
$token = DBA::selectFirst('application-token', ['uid'], $condition);
|
||||||
|
if (!DBA::isResult($token)) {
|
||||||
|
Logger::warning('Token not found', $condition);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Logger::info('Token found', $token);
|
||||||
|
return $token['uid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the application record via the proved request header fields
|
||||||
|
*
|
||||||
|
* @param string $client_id
|
||||||
|
* @param string $client_secret
|
||||||
|
* @param string $redirect_uri
|
||||||
|
* @return array application record
|
||||||
|
*/
|
||||||
|
public static function getApplication(string $client_id, string $client_secret, string $redirect_uri)
|
||||||
|
{
|
||||||
|
$condition = ['client_id' => $client_id];
|
||||||
|
if (!empty($client_secret)) {
|
||||||
|
$condition['client_secret'] = $client_secret;
|
||||||
|
}
|
||||||
|
if (!empty($redirect_uri)) {
|
||||||
|
$condition['redirect_uri'] = $redirect_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$application = DBA::selectFirst('application', [], $condition);
|
||||||
|
if (!DBA::isResult($application)) {
|
||||||
|
Logger::warning('Application not found', $condition);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return $application;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an token for the application and user exists
|
||||||
|
*
|
||||||
|
* @param array $application
|
||||||
|
* @param integer $uid
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function existsTokenForUser(array $application, int $uid)
|
||||||
|
{
|
||||||
|
return DBA::exists('application-token', ['application-id' => $application['id'], 'uid' => $uid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the token for the given application and user
|
||||||
|
*
|
||||||
|
* @param array $application
|
||||||
|
* @param integer $uid
|
||||||
|
* @return array application record
|
||||||
|
*/
|
||||||
|
public static function getTokenForUser(array $application, int $uid)
|
||||||
|
{
|
||||||
|
return DBA::selectFirst('application-token', [], ['application-id' => $application['id'], 'uid' => $uid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and fetch an token for the application and user
|
||||||
|
*
|
||||||
|
* @param array $application
|
||||||
|
* @param integer $uid
|
||||||
|
* @param string $scope
|
||||||
|
* @return array application record
|
||||||
|
*/
|
||||||
|
public static function createTokenForUser(array $application, int $uid, string $scope)
|
||||||
|
{
|
||||||
|
$code = bin2hex(random_bytes(32));
|
||||||
|
$access_token = bin2hex(random_bytes(32));
|
||||||
|
|
||||||
|
$fields = ['application-id' => $application['id'], 'uid' => $uid, 'code' => $code, 'access_token' => $access_token, 'scopes' => $scope,
|
||||||
|
'read' => (stripos($scope, 'read') !== false), 'write' => (stripos($scope, 'write') !== false),
|
||||||
|
'follow' => (stripos($scope, 'follow') !== false), 'created_at' => DateTimeFormat::utcNow(DateTimeFormat::MYSQL)];
|
||||||
|
if (!DBA::insert('application-token', $fields, Database::INSERT_UPDATE)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return DBA::selectFirst('application-token', [], ['application-id' => $application['id'], 'uid' => $uid]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user info array.
|
* Get user info array.
|
||||||
*
|
*
|
||||||
|
|
|
@ -413,6 +413,10 @@ class Contact extends BaseModule
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($cmd === 'block') {
|
if ($cmd === 'block') {
|
||||||
|
if (public_contact() === $contact_id) {
|
||||||
|
throw new BadRequestException(DI::l10n()->t('You can\'t block yourself'));
|
||||||
|
}
|
||||||
|
|
||||||
self::blockContact($contact_id);
|
self::blockContact($contact_id);
|
||||||
|
|
||||||
$blocked = Model\Contact\User::isBlocked($contact_id, local_user());
|
$blocked = Model\Contact\User::isBlocked($contact_id, local_user());
|
||||||
|
@ -423,6 +427,10 @@ class Contact extends BaseModule
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($cmd === 'ignore') {
|
if ($cmd === 'ignore') {
|
||||||
|
if (public_contact() === $contact_id) {
|
||||||
|
throw new BadRequestException(DI::l10n()->t('You can\'t ignore yourself'));
|
||||||
|
}
|
||||||
|
|
||||||
self::ignoreContact($contact_id);
|
self::ignoreContact($contact_id);
|
||||||
|
|
||||||
$ignored = Model\Contact\User::isIgnored($contact_id, local_user());
|
$ignored = Model\Contact\User::isIgnored($contact_id, local_user());
|
||||||
|
|
53
src/Module/OAuth/Acknowledge.php
Normal file
53
src/Module/OAuth/Acknowledge.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\OAuth;
|
||||||
|
|
||||||
|
use Friendica\Core\Renderer;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledgement of OAuth requests
|
||||||
|
*/
|
||||||
|
class Acknowledge extends BaseApi
|
||||||
|
{
|
||||||
|
public static function post(array $parameters = [])
|
||||||
|
{
|
||||||
|
DI::session()->set('oauth_acknowledge', true);
|
||||||
|
DI::app()->redirect(DI::session()->get('return_path'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function content(array $parameters = [])
|
||||||
|
{
|
||||||
|
DI::session()->set('return_path', $_REQUEST['return_path'] ?? '');
|
||||||
|
|
||||||
|
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('oauth_authorize.tpl'), [
|
||||||
|
'$title' => DI::l10n()->t('Authorize application connection'),
|
||||||
|
'$app' => ['name' => $_REQUEST['application'] ?? ''],
|
||||||
|
'$authorize' => DI::l10n()->t('Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?'),
|
||||||
|
'$yes' => DI::l10n()->t('Yes'),
|
||||||
|
'$no' => DI::l10n()->t('No'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $o;
|
||||||
|
}
|
||||||
|
}
|
90
src/Module/OAuth/Authorize.php
Normal file
90
src/Module/OAuth/Authorize.php
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\OAuth;
|
||||||
|
|
||||||
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/spec/oauth/
|
||||||
|
* @see https://aaronparecki.com/oauth-2-simplified/
|
||||||
|
*/
|
||||||
|
class Authorize extends BaseApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $parameters
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function rawContent(array $parameters = [])
|
||||||
|
{
|
||||||
|
$response_type = $_REQUEST['response_type'] ?? '';
|
||||||
|
$client_id = $_REQUEST['client_id'] ?? '';
|
||||||
|
$client_secret = $_REQUEST['client_secret'] ?? ''; // Isn't normally provided. We will use it if present.
|
||||||
|
$redirect_uri = $_REQUEST['redirect_uri'] ?? '';
|
||||||
|
$scope = $_REQUEST['scope'] ?? 'read';
|
||||||
|
$state = $_REQUEST['state'] ?? '';
|
||||||
|
|
||||||
|
if ($response_type != 'code') {
|
||||||
|
Logger::warning('Unsupported or missing response type', ['request' => $_REQUEST]);
|
||||||
|
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Unsupported or missing response type'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($client_id) || empty($redirect_uri)) {
|
||||||
|
Logger::warning('Incomplete request data', ['request' => $_REQUEST]);
|
||||||
|
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Incomplete request data'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$application = self::getApplication($client_id, $client_secret, $redirect_uri);
|
||||||
|
if (empty($application)) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Compare the application scope and requested scope
|
||||||
|
|
||||||
|
$request = $_REQUEST;
|
||||||
|
unset($request['pagename']);
|
||||||
|
$redirect = 'oauth/authorize?' . http_build_query($request);
|
||||||
|
|
||||||
|
$uid = local_user();
|
||||||
|
if (empty($uid)) {
|
||||||
|
Logger::info('Redirect to login');
|
||||||
|
DI::app()->redirect('login?return_path=' . urlencode($redirect));
|
||||||
|
} else {
|
||||||
|
Logger::info('Already logged in user', ['uid' => $uid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self::existsTokenForUser($application, $uid) && !DI::session()->get('oauth_acknowledge')) {
|
||||||
|
Logger::info('Redirect to acknowledge');
|
||||||
|
DI::app()->redirect('oauth/acknowledge?' . http_build_query(['return_path' => $redirect, 'application' => $application['name']]));
|
||||||
|
}
|
||||||
|
|
||||||
|
DI::session()->remove('oauth_acknowledge');
|
||||||
|
|
||||||
|
$token = self::createTokenForUser($application, $uid, $scope);
|
||||||
|
if (!$token) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
DI::app()->redirect($application['redirect_uri'] . (strpos($application['redirect_uri'], '?') ? '&' : '?') . http_build_query(['code' => $token['code'], 'state' => $state]));
|
||||||
|
}
|
||||||
|
}
|
35
src/Module/OAuth/Revoke.php
Normal file
35
src/Module/OAuth/Revoke.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\OAuth;
|
||||||
|
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/spec/oauth/
|
||||||
|
*/
|
||||||
|
class Revoke extends BaseApi
|
||||||
|
{
|
||||||
|
public static function post(array $parameters = [])
|
||||||
|
{
|
||||||
|
self::unsupported('post');
|
||||||
|
}
|
||||||
|
}
|
86
src/Module/OAuth/Token.php
Normal file
86
src/Module/OAuth/Token.php
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Module\OAuth;
|
||||||
|
|
||||||
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\Core\System;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\BaseApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://docs.joinmastodon.org/spec/oauth/
|
||||||
|
* @see https://aaronparecki.com/oauth-2-simplified/
|
||||||
|
*/
|
||||||
|
class Token extends BaseApi
|
||||||
|
{
|
||||||
|
public static function post(array $parameters = [])
|
||||||
|
{
|
||||||
|
$grant_type = $_REQUEST['grant_type'] ?? '';
|
||||||
|
$code = $_REQUEST['code'] ?? '';
|
||||||
|
$redirect_uri = $_REQUEST['redirect_uri'] ?? '';
|
||||||
|
$client_id = $_REQUEST['client_id'] ?? '';
|
||||||
|
$client_secret = $_REQUEST['client_secret'] ?? '';
|
||||||
|
|
||||||
|
// AndStatus transmits the client data in the AUTHORIZATION header field, see https://github.com/andstatus/andstatus/issues/530
|
||||||
|
if (empty($client_id) && !empty($_SERVER['HTTP_AUTHORIZATION']) && (substr($_SERVER['HTTP_AUTHORIZATION'], 0, 6) == 'Basic ')) {
|
||||||
|
$datapair = explode(':', base64_decode(trim(substr($_SERVER['HTTP_AUTHORIZATION'], 6))));
|
||||||
|
if (count($datapair) == 2) {
|
||||||
|
$client_id = $datapair[0];
|
||||||
|
$client_secret = $datapair[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($client_id) || empty($client_secret)) {
|
||||||
|
Logger::warning('Incomplete request data', ['request' => $_REQUEST]);
|
||||||
|
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Incomplete request data'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$application = self::getApplication($client_id, $client_secret, $redirect_uri);
|
||||||
|
if (empty($application)) {
|
||||||
|
DI::mstdnError()->UnprocessableEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($grant_type == 'client_credentials') {
|
||||||
|
// the "client_credentials" are used as a token for the application itself.
|
||||||
|
// see https://aaronparecki.com/oauth-2-simplified/#client-credentials
|
||||||
|
$token = self::createTokenForUser($application, 0, '');
|
||||||
|
} elseif ($grant_type == 'authorization_code') {
|
||||||
|
// For security reasons only allow freshly created tokens
|
||||||
|
$condition = ["`redirect_uri` = ? AND `id` = ? AND `code` = ? AND `created_at` > UTC_TIMESTAMP() - INTERVAL ? MINUTE",
|
||||||
|
$redirect_uri, $application['id'], $code, 5];
|
||||||
|
|
||||||
|
$token = DBA::selectFirst('application-view', ['access_token', 'created_at'], $condition);
|
||||||
|
if (!DBA::isResult($token)) {
|
||||||
|
Logger::warning('Token not found or outdated', $condition);
|
||||||
|
DI::mstdnError()->Unauthorized();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger::warning('Unsupported or missing grant type', ['request' => $_REQUEST]);
|
||||||
|
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Unsupported or missing grant type'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$object = new \Friendica\Object\Api\Mastodon\Token($token['access_token'], 'Bearer', $application['scopes'], $token['created_at']);
|
||||||
|
|
||||||
|
System::jsonExit($object->toArray());
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,6 @@ use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
use Friendica\Model\Item;
|
use Friendica\Model\Item;
|
||||||
use Friendica\Model\ItemContent;
|
|
||||||
use Friendica\Model\Post;
|
use Friendica\Model\Post;
|
||||||
use Friendica\Model\Tag;
|
use Friendica\Model\Tag;
|
||||||
use Friendica\Module\BaseSearch;
|
use Friendica\Module\BaseSearch;
|
||||||
|
|
|
@ -36,8 +36,12 @@ class Login extends BaseModule
|
||||||
{
|
{
|
||||||
public static function content(array $parameters = [])
|
public static function content(array $parameters = [])
|
||||||
{
|
{
|
||||||
|
$return_path = $_REQUEST['return_path'] ?? '' ;
|
||||||
|
|
||||||
if (local_user()) {
|
if (local_user()) {
|
||||||
DI::baseUrl()->redirect();
|
DI::baseUrl()->redirect($return_path);
|
||||||
|
} elseif (!empty($return_path)) {
|
||||||
|
Session::set('return_path', $return_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::form(Session::get('return_path'), intval(DI::config()->get('config', 'register_policy')) !== \Friendica\Module\Register::CLOSED);
|
return self::form(Session::get('return_path'), intval(DI::config()->get('config', 'register_policy')) !== \Friendica\Module\Register::CLOSED);
|
||||||
|
|
|
@ -30,8 +30,18 @@ use Friendica\BaseDataTransferObject;
|
||||||
*/
|
*/
|
||||||
class Application extends BaseDataTransferObject
|
class Application extends BaseDataTransferObject
|
||||||
{
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $client_id;
|
||||||
|
/** @var string */
|
||||||
|
protected $client_secret;
|
||||||
|
/** @var int */
|
||||||
|
protected $id;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $name;
|
protected $name;
|
||||||
|
/** @var string */
|
||||||
|
protected $redirect_uri;
|
||||||
|
/** @var string */
|
||||||
|
protected $website;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an application entry
|
* Creates an application entry
|
||||||
|
@ -39,8 +49,36 @@ class Application extends BaseDataTransferObject
|
||||||
* @param array $item
|
* @param array $item
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
*/
|
*/
|
||||||
public function __construct(string $name)
|
public function __construct(string $name, string $client_id = null, string $client_secret = null, int $id = null, string $redirect_uri = null, string $website = null)
|
||||||
{
|
{
|
||||||
|
$this->client_id = $client_id;
|
||||||
|
$this->client_secret = $client_secret;
|
||||||
|
$this->id = $id;
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
$this->redirect_uri = $redirect_uri;
|
||||||
|
$this->website = $website;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current entity as an array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
$application = parent::toArray();
|
||||||
|
|
||||||
|
if (empty($application['id'])) {
|
||||||
|
unset($application['client_id']);
|
||||||
|
unset($application['client_secret']);
|
||||||
|
unset($application['id']);
|
||||||
|
unset($application['redirect_uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($application['website'])) {
|
||||||
|
unset($application['website']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $application;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,9 +39,17 @@ class Card extends BaseDataTransferObject
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $type;
|
protected $type;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
|
protected $author_name;
|
||||||
|
/** @var string */
|
||||||
|
protected $author_url;
|
||||||
|
/** @var string */
|
||||||
protected $provider_name;
|
protected $provider_name;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $provider_url;
|
protected $provider_url;
|
||||||
|
/** @var int */
|
||||||
|
protected $width;
|
||||||
|
/** @var int */
|
||||||
|
protected $height;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $image;
|
protected $image;
|
||||||
|
|
||||||
|
@ -57,9 +65,13 @@ class Card extends BaseDataTransferObject
|
||||||
$this->title = $attachment['title'] ?? '';
|
$this->title = $attachment['title'] ?? '';
|
||||||
$this->description = $attachment['description'] ?? '';
|
$this->description = $attachment['description'] ?? '';
|
||||||
$this->type = $attachment['type'] ?? '';
|
$this->type = $attachment['type'] ?? '';
|
||||||
$this->image = $attachment['image'] ?? '';
|
$this->author_name = $attachment['author_name'] ?? '';
|
||||||
|
$this->author_url = $attachment['author_url'] ?? '';
|
||||||
$this->provider_name = $attachment['provider_name'] ?? '';
|
$this->provider_name = $attachment['provider_name'] ?? '';
|
||||||
$this->provider_url = $attachment['provider_url'] ?? '';
|
$this->provider_url = $attachment['provider_url'] ?? '';
|
||||||
|
$this->width = $attachment['width'] ?? 0;
|
||||||
|
$this->height = $attachment['height'] ?? 0;
|
||||||
|
$this->image = $attachment['image'] ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -80,7 +80,7 @@ class Instance extends BaseDataTransferObject
|
||||||
$instance->description = DI::config()->get('config', 'info');
|
$instance->description = DI::config()->get('config', 'info');
|
||||||
$instance->email = DI::config()->get('config', 'admin_email');
|
$instance->email = DI::config()->get('config', 'admin_email');
|
||||||
$instance->version = FRIENDICA_VERSION;
|
$instance->version = FRIENDICA_VERSION;
|
||||||
$instance->urls = []; // Not supported
|
$instance->urls = null; // Not supported
|
||||||
$instance->stats = Stats::get();
|
$instance->stats = Stats::get();
|
||||||
$instance->thumbnail = $baseUrl->get() . (DI::config()->get('system', 'shortcut_icon') ?? 'images/friendica-32.png');
|
$instance->thumbnail = $baseUrl->get() . (DI::config()->get('system', 'shortcut_icon') ?? 'images/friendica-32.png');
|
||||||
$instance->languages = [DI::config()->get('system', 'language')];
|
$instance->languages = [DI::config()->get('system', 'language')];
|
||||||
|
|
51
src/Object/Api/Mastodon/ListEntity.php
Normal file
51
src/Object/Api/Mastodon/ListEntity.php
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Object\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\BaseDataTransferObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ListEntity
|
||||||
|
*
|
||||||
|
* @see https://docs.joinmastodon.org/entities/list/
|
||||||
|
*/
|
||||||
|
class ListEntity extends BaseDataTransferObject
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $id;
|
||||||
|
/** @var string */
|
||||||
|
protected $title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an list record
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @param string $title
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function __construct(int $id, string $title, string $policy)
|
||||||
|
{
|
||||||
|
$this->id = (string)$id;
|
||||||
|
$this->title = $title;
|
||||||
|
$this->replies_policy = $policy;
|
||||||
|
}
|
||||||
|
}
|
78
src/Object/Api/Mastodon/Notification.php
Normal file
78
src/Object/Api/Mastodon/Notification.php
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Object\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\BaseDataTransferObject;
|
||||||
|
use Friendica\Util\DateTimeFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Notification
|
||||||
|
*
|
||||||
|
* @see https://docs.joinmastodon.org/entities/notification/
|
||||||
|
*/
|
||||||
|
class Notification extends BaseDataTransferObject
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $id;
|
||||||
|
/** @var string (Enumerable oneOf) */
|
||||||
|
protected $type;
|
||||||
|
/** @var string (Datetime) */
|
||||||
|
protected $created_at;
|
||||||
|
/** @var Account */
|
||||||
|
protected $account;
|
||||||
|
/** @var Status|null */
|
||||||
|
protected $status = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a notification record
|
||||||
|
*
|
||||||
|
* @param array $item
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function __construct(int $id, string $type, string $created_at, Account $account = null, Status $status = null)
|
||||||
|
{
|
||||||
|
$this->id = (string)$id;
|
||||||
|
$this->type = $type;
|
||||||
|
$this->created_at = DateTimeFormat::utc($created_at, DateTimeFormat::ATOM);
|
||||||
|
$this->account = $account->toArray();
|
||||||
|
|
||||||
|
if (!empty($status)) {
|
||||||
|
$this->status = $status->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current entity as an array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
$notification = parent::toArray();
|
||||||
|
|
||||||
|
if (!$notification['status']) {
|
||||||
|
unset($notification['status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $notification;
|
||||||
|
}
|
||||||
|
}
|
62
src/Object/Api/Mastodon/Preferences.php
Normal file
62
src/Object/Api/Mastodon/Preferences.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Object\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\App\BaseURL;
|
||||||
|
use Friendica\BaseDataTransferObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Preferences
|
||||||
|
*
|
||||||
|
* @see https://docs.joinmastodon.org/entities/preferences/
|
||||||
|
*/
|
||||||
|
class Preferences extends BaseDataTransferObject
|
||||||
|
{
|
||||||
|
// /** @var string (Enumerable, oneOf) */
|
||||||
|
// protected $posting_default_visibility;
|
||||||
|
// /** @var bool */
|
||||||
|
// protected $posting_default_sensitive;
|
||||||
|
// /** @var string (ISO 639-1 language two-letter code), or null*/
|
||||||
|
// protected $posting_default_language;
|
||||||
|
// /** @var string (Enumerable, oneOf) */
|
||||||
|
// protected $reading_expand_media;
|
||||||
|
// /** @var bool */
|
||||||
|
// protected $reading_expand_spoilers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a preferences record.
|
||||||
|
*
|
||||||
|
* @param BaseURL $baseUrl
|
||||||
|
* @param array $publicContact Full contact table record with uid = 0
|
||||||
|
* @param array $apcontact Optional full apcontact table record
|
||||||
|
* @param array $userContact Optional full contact table record with uid != 0
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function __construct(string $visibility, bool $sensitive, string $language, string $media, bool $spoilers)
|
||||||
|
{
|
||||||
|
$this->{'posting:default:visibility'} = $visibility;
|
||||||
|
$this->{'posting:default:sensitive'} = $sensitive;
|
||||||
|
$this->{'posting:default:language'} = $language;
|
||||||
|
$this->{'reading:expand:media'} = $media;
|
||||||
|
$this->{'reading:expand:spoilers'} = $spoilers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -126,7 +126,7 @@ class Status extends BaseDataTransferObject
|
||||||
$this->muted = $userAttributes->muted;
|
$this->muted = $userAttributes->muted;
|
||||||
$this->bookmarked = $userAttributes->bookmarked;
|
$this->bookmarked = $userAttributes->bookmarked;
|
||||||
$this->pinned = $userAttributes->pinned;
|
$this->pinned = $userAttributes->pinned;
|
||||||
$this->content = BBCode::convert($item['raw-body'] ?? $item['body'], false);
|
$this->content = BBCode::convert($item['raw-body'] ?? $item['body'], false, BBCode::API);
|
||||||
$this->reblog = $reblog;
|
$this->reblog = $reblog;
|
||||||
$this->application = $application->toArray();
|
$this->application = $application->toArray();
|
||||||
$this->account = $account->toArray();
|
$this->account = $account->toArray();
|
||||||
|
@ -134,7 +134,7 @@ class Status extends BaseDataTransferObject
|
||||||
$this->mentions = $mentions;
|
$this->mentions = $mentions;
|
||||||
$this->tags = $tags;
|
$this->tags = $tags;
|
||||||
$this->emojis = [];
|
$this->emojis = [];
|
||||||
$this->card = $card->toArray();
|
$this->card = $card->toArray() ?: null;
|
||||||
$this->poll = null;
|
$this->poll = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
58
src/Object/Api/Mastodon/Token.php
Normal file
58
src/Object/Api/Mastodon/Token.php
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Object\Api\Mastodon;
|
||||||
|
|
||||||
|
use Friendica\BaseDataTransferObject;
|
||||||
|
use Friendica\Util\DateTimeFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Error
|
||||||
|
*
|
||||||
|
* @see https://docs.joinmastodon.org/entities/error
|
||||||
|
*/
|
||||||
|
class Token extends BaseDataTransferObject
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $access_token;
|
||||||
|
/** @var string */
|
||||||
|
protected $token_type;
|
||||||
|
/** @var string */
|
||||||
|
protected $scope;
|
||||||
|
/** @var string (Datetime) */
|
||||||
|
protected $created_at;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a token record
|
||||||
|
*
|
||||||
|
* @param string $access_token
|
||||||
|
* @param string $token_type
|
||||||
|
* @param string $scope
|
||||||
|
* @param string $created_at
|
||||||
|
*/
|
||||||
|
public function __construct(string $access_token, string $token_type, string $scope, string $created_at)
|
||||||
|
{
|
||||||
|
$this->access_token = $access_token;
|
||||||
|
$this->token_type = $token_type;
|
||||||
|
$this->scope = $scope;
|
||||||
|
$this->created_at = DateTimeFormat::utc($created_at, DateTimeFormat::ATOM);
|
||||||
|
}
|
||||||
|
}
|
|
@ -211,7 +211,7 @@ class Post
|
||||||
$origin = $item['origin'] || $item['parent-origin'];
|
$origin = $item['origin'] || $item['parent-origin'];
|
||||||
|
|
||||||
if ($item['pinned']) {
|
if ($item['pinned']) {
|
||||||
$pinned = DI::l10n()->t('pinned item');
|
$pinned = DI::l10n()->t('Pinned item');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Showing the one or the other text, depending upon if we can only hide it or really delete it.
|
// Showing the one or the other text, depending upon if we can only hide it or really delete it.
|
||||||
|
@ -226,6 +226,9 @@ class Post
|
||||||
'select' => DI::l10n()->t('Select'),
|
'select' => DI::l10n()->t('Select'),
|
||||||
'delete' => $delete,
|
'delete' => $delete,
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$item['self']) {
|
||||||
$block = [
|
$block = [
|
||||||
'blocking' => true,
|
'blocking' => true,
|
||||||
'block' => DI::l10n()->t('Block %s', $item['author-name']),
|
'block' => DI::l10n()->t('Block %s', $item['author-name']),
|
||||||
|
@ -233,7 +236,7 @@ class Post
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$filer = (($conv->getProfileOwner() == local_user() && ($item['uid'] != 0)) ? DI::l10n()->t("save to folder") : false);
|
$filer = (($conv->getProfileOwner() == local_user() && ($item['uid'] != 0)) ? DI::l10n()->t('Save to folder') : false);
|
||||||
|
|
||||||
$profile_name = $item['author-name'];
|
$profile_name = $item['author-name'];
|
||||||
if (!empty($item['author-link']) && empty($item['author-name'])) {
|
if (!empty($item['author-link']) && empty($item['author-name'])) {
|
||||||
|
@ -297,12 +300,12 @@ class Post
|
||||||
$ignored = PostModel\ThreadUser::getIgnored($item['uri-id'], local_user());
|
$ignored = PostModel\ThreadUser::getIgnored($item['uri-id'], local_user());
|
||||||
if ($item['mention'] || $ignored) {
|
if ($item['mention'] || $ignored) {
|
||||||
$ignore = [
|
$ignore = [
|
||||||
'do' => DI::l10n()->t("ignore thread"),
|
'do' => DI::l10n()->t('Ignore thread'),
|
||||||
'undo' => DI::l10n()->t("unignore thread"),
|
'undo' => DI::l10n()->t('Unignore thread'),
|
||||||
'toggle' => DI::l10n()->t("toggle ignore status"),
|
'toggle' => DI::l10n()->t('Toggle ignore status'),
|
||||||
'classdo' => $ignored ? "hidden" : "",
|
'classdo' => $ignored ? "hidden" : "",
|
||||||
'classundo' => $ignored ? "" : "hidden",
|
'classundo' => $ignored ? "" : "hidden",
|
||||||
'ignored' => DI::l10n()->t('ignored'),
|
'ignored' => DI::l10n()->t('Ignored'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,28 +314,28 @@ class Post
|
||||||
$ispinned = ($item['pinned'] ? 'pinned' : 'unpinned');
|
$ispinned = ($item['pinned'] ? 'pinned' : 'unpinned');
|
||||||
|
|
||||||
$pin = [
|
$pin = [
|
||||||
'do' => DI::l10n()->t('pin'),
|
'do' => DI::l10n()->t('Pin'),
|
||||||
'undo' => DI::l10n()->t('unpin'),
|
'undo' => DI::l10n()->t('Unpin'),
|
||||||
'toggle' => DI::l10n()->t('toggle pin status'),
|
'toggle' => DI::l10n()->t('Toggle pin status'),
|
||||||
'classdo' => $item['pinned'] ? 'hidden' : '',
|
'classdo' => $item['pinned'] ? 'hidden' : '',
|
||||||
'classundo' => $item['pinned'] ? '' : 'hidden',
|
'classundo' => $item['pinned'] ? '' : 'hidden',
|
||||||
'pinned' => DI::l10n()->t('pinned'),
|
'pinned' => DI::l10n()->t('Pinned'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$isstarred = (($item['starred']) ? "starred" : "unstarred");
|
$isstarred = (($item['starred']) ? "starred" : "unstarred");
|
||||||
|
|
||||||
$star = [
|
$star = [
|
||||||
'do' => DI::l10n()->t("add star"),
|
'do' => DI::l10n()->t('Add star'),
|
||||||
'undo' => DI::l10n()->t("remove star"),
|
'undo' => DI::l10n()->t('Remove star'),
|
||||||
'toggle' => DI::l10n()->t("toggle star status"),
|
'toggle' => DI::l10n()->t('Toggle star status'),
|
||||||
'classdo' => $item['starred'] ? "hidden" : "",
|
'classdo' => $item['starred'] ? "hidden" : "",
|
||||||
'classundo' => $item['starred'] ? "" : "hidden",
|
'classundo' => $item['starred'] ? "" : "hidden",
|
||||||
'starred' => DI::l10n()->t('starred'),
|
'starred' => DI::l10n()->t('Starred'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$tagger = [
|
$tagger = [
|
||||||
'add' => DI::l10n()->t("add tag"),
|
'add' => DI::l10n()->t('Add tag'),
|
||||||
'class' => "",
|
'class' => "",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -342,8 +345,8 @@ class Post
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($conv->isWritable()) {
|
if ($conv->isWritable()) {
|
||||||
$buttons['like'] = [DI::l10n()->t("I like this \x28toggle\x29") , DI::l10n()->t("like")];
|
$buttons['like'] = [DI::l10n()->t("I like this \x28toggle\x29") , DI::l10n()->t('Like')];
|
||||||
$buttons['dislike'] = [DI::l10n()->t("I don't like this \x28toggle\x29"), DI::l10n()->t("dislike")];
|
$buttons['dislike'] = [DI::l10n()->t("I don't like this \x28toggle\x29"), DI::l10n()->t('Dislike')];
|
||||||
if ($shareable) {
|
if ($shareable) {
|
||||||
$buttons['share'] = [DI::l10n()->t('Quote share this'), DI::l10n()->t('Quote Share')];
|
$buttons['share'] = [DI::l10n()->t('Quote share this'), DI::l10n()->t('Quote Share')];
|
||||||
}
|
}
|
||||||
|
@ -399,7 +402,7 @@ class Post
|
||||||
|
|
||||||
// Fetching of Diaspora posts doesn't always work. There are issues with reshares and possibly comments
|
// Fetching of Diaspora posts doesn't always work. There are issues with reshares and possibly comments
|
||||||
if (($item['network'] != Protocol::DIASPORA) && empty($comment) && !empty(Session::get('remote_comment'))) {
|
if (($item['network'] != Protocol::DIASPORA) && empty($comment) && !empty(Session::get('remote_comment'))) {
|
||||||
$remote_comment = [DI::l10n()->t('Comment this item on your system'), DI::l10n()->t('remote comment'),
|
$remote_comment = [DI::l10n()->t('Comment this item on your system'), DI::l10n()->t('Remote comment'),
|
||||||
str_replace('{uri}', urlencode($item['uri']), Session::get('remote_comment'))];
|
str_replace('{uri}', urlencode($item['uri']), Session::get('remote_comment'))];
|
||||||
} else {
|
} else {
|
||||||
$remote_comment = '';
|
$remote_comment = '';
|
||||||
|
@ -424,6 +427,8 @@ class Post
|
||||||
$tmp_item = [
|
$tmp_item = [
|
||||||
'template' => $this->getTemplate(),
|
'template' => $this->getTemplate(),
|
||||||
'type' => implode("", array_slice(explode("/", $item['verb']), -1)),
|
'type' => implode("", array_slice(explode("/", $item['verb']), -1)),
|
||||||
|
'comment_firstcollapsed' => false,
|
||||||
|
'comment_lastcollapsed' => false,
|
||||||
'suppress_tags' => DI::config()->get('system', 'suppress_tags'),
|
'suppress_tags' => DI::config()->get('system', 'suppress_tags'),
|
||||||
'tags' => $tags['tags'],
|
'tags' => $tags['tags'],
|
||||||
'hashtags' => $tags['hashtags'],
|
'hashtags' => $tags['hashtags'],
|
||||||
|
@ -540,10 +545,7 @@ class Post
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isToplevel()) {
|
$result['total_comments_num'] = $this->isToplevel() ? $total_children : 0;
|
||||||
$result['total_comments_num'] = "$total_children";
|
|
||||||
$result['total_comments_text'] = DI::l10n()->tt('comment', 'comments', $total_children);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result['private'] = $item['private'];
|
$result['private'] = $item['private'];
|
||||||
$result['toplevel'] = ($this->isToplevel() ? 'toplevel_item' : '');
|
$result['toplevel'] = ($this->isToplevel() ? 'toplevel_item' : '');
|
||||||
|
@ -884,8 +886,13 @@ class Post
|
||||||
|
|
||||||
$terms = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]);
|
$terms = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]);
|
||||||
foreach ($terms as $term) {
|
foreach ($terms as $term) {
|
||||||
|
if (!$term['url']) {
|
||||||
|
DI::logger()->warning('Mention term with no URL', ['term' => $term]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$profile = Contact::getByURL($term['url'], false, ['addr', 'contact-type']);
|
$profile = Contact::getByURL($term['url'], false, ['addr', 'contact-type']);
|
||||||
if (!empty($profile['addr']) && ((($profile['contact-type'] ?? '') ?: Contact::TYPE_UNKNOWN) != Contact::TYPE_COMMUNITY) &&
|
if (!empty($profile['addr']) && (($profile['contact-type'] ?? Contact::TYPE_UNKNOWN) != Contact::TYPE_COMMUNITY) &&
|
||||||
($profile['addr'] != $owner['addr']) && !strstr($text, $profile['addr'])) {
|
($profile['addr'] != $owner['addr']) && !strstr($text, $profile['addr'])) {
|
||||||
$text .= '@' . $profile['addr'] . ' ';
|
$text .= '@' . $profile['addr'] . ' ';
|
||||||
}
|
}
|
||||||
|
@ -942,9 +949,9 @@ class Post
|
||||||
'$qcomment' => $qcomment,
|
'$qcomment' => $qcomment,
|
||||||
'$default' => $default_text,
|
'$default' => $default_text,
|
||||||
'$profile_uid' => $uid,
|
'$profile_uid' => $uid,
|
||||||
'$mylink' => DI::baseUrl()->remove($a->contact['url']),
|
'$mylink' => DI::baseUrl()->remove($a->contact['url'] ?? ''),
|
||||||
'$mytitle' => DI::l10n()->t('This is you'),
|
'$mytitle' => DI::l10n()->t('This is you'),
|
||||||
'$myphoto' => DI::baseUrl()->remove($a->contact['thumb']),
|
'$myphoto' => DI::baseUrl()->remove($a->contact['thumb'] ?? ''),
|
||||||
'$comment' => DI::l10n()->t('Comment'),
|
'$comment' => DI::l10n()->t('Comment'),
|
||||||
'$submit' => DI::l10n()->t('Submit'),
|
'$submit' => DI::l10n()->t('Submit'),
|
||||||
'$loading' => DI::l10n()->t('Loading...'),
|
'$loading' => DI::l10n()->t('Loading...'),
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
namespace Friendica\Protocol\ActivityPub;
|
namespace Friendica\Protocol\ActivityPub;
|
||||||
|
|
||||||
use Friendica\Content\PageInfo;
|
|
||||||
use Friendica\Content\Text\BBCode;
|
use Friendica\Content\Text\BBCode;
|
||||||
use Friendica\Content\Text\HTML;
|
use Friendica\Content\Text\HTML;
|
||||||
use Friendica\Content\Text\Markdown;
|
use Friendica\Content\Text\Markdown;
|
||||||
|
@ -464,6 +463,7 @@ class Processor
|
||||||
if (!empty($activity['source'])) {
|
if (!empty($activity['source'])) {
|
||||||
$item['body'] = $activity['source'];
|
$item['body'] = $activity['source'];
|
||||||
$item['raw-body'] = $content;
|
$item['raw-body'] = $content;
|
||||||
|
$item['body'] = Item::improveSharedDataInBody($item);
|
||||||
} else {
|
} else {
|
||||||
if (empty($activity['directmessage']) && ($item['thr-parent'] != $item['uri']) && ($item['gravity'] == GRAVITY_COMMENT)) {
|
if (empty($activity['directmessage']) && ($item['thr-parent'] != $item['uri']) && ($item['gravity'] == GRAVITY_COMMENT)) {
|
||||||
$item_private = !in_array(0, $activity['item_receiver']);
|
$item_private = !in_array(0, $activity['item_receiver']);
|
||||||
|
|
|
@ -111,9 +111,12 @@ class Receiver
|
||||||
}
|
}
|
||||||
|
|
||||||
$http_signer = HTTPSignature::getSigner($body, $header);
|
$http_signer = HTTPSignature::getSigner($body, $header);
|
||||||
if (empty($http_signer)) {
|
if ($http_signer === false) {
|
||||||
Logger::warning('Invalid HTTP signature, message will be discarded.');
|
Logger::warning('Invalid HTTP signature, message will be discarded.');
|
||||||
return;
|
return;
|
||||||
|
} elseif (empty($http_signer)) {
|
||||||
|
Logger::info('Signer is a tombstone. The message will be discarded, the signer account is deleted.');
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
Logger::info('Valid HTTP signature', ['signer' => $http_signer]);
|
Logger::info('Valid HTTP signature', ['signer' => $http_signer]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1260,54 +1260,61 @@ class Transmitter
|
||||||
{
|
{
|
||||||
$attachments = [];
|
$attachments = [];
|
||||||
|
|
||||||
// Currently deactivated, since it creates side effects on Mastodon and Pleroma.
|
$uriids = [$item['uri-id']];
|
||||||
// It will be reactivated, once this cleared.
|
$shared = BBCode::fetchShareAttributes($item['body']);
|
||||||
/*
|
if (!empty($shared['guid'])) {
|
||||||
$attach_data = BBCode::getAttachmentData($item['body']);
|
$shared_item = Post::selectFirst(['uri-id'], ['guid' => $shared['guid']]);
|
||||||
if (!empty($attach_data['url'])) {
|
if (!empty($shared_item['uri-id'])) {
|
||||||
$attachment = ['type' => 'Page',
|
$uriids[] = $shared_item['uri-id'];
|
||||||
'mediaType' => 'text/html',
|
|
||||||
'url' => $attach_data['url']];
|
|
||||||
|
|
||||||
if (!empty($attach_data['title'])) {
|
|
||||||
$attachment['name'] = $attach_data['title'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($attach_data['description'])) {
|
|
||||||
$attachment['summary'] = $attach_data['description'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($attach_data['image'])) {
|
|
||||||
$imgdata = Images::getInfoFromURLCached($attach_data['image']);
|
|
||||||
if ($imgdata) {
|
|
||||||
$attachment['icon'] = ['type' => 'Image',
|
|
||||||
'mediaType' => $imgdata['mime'],
|
|
||||||
'width' => $imgdata[0],
|
|
||||||
'height' => $imgdata[1],
|
|
||||||
'url' => $attach_data['image']];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$attachments[] = $attachment;
|
$urls = [];
|
||||||
|
foreach ($uriids as $uriid) {
|
||||||
|
foreach (Post\Media::getByURIId($uriid, [Post\Media::DOCUMENT, Post\Media::TORRENT]) as $attachment) {
|
||||||
|
if (in_array($attachment['url'], $urls)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
*/
|
$urls[] = $attachment['url'];
|
||||||
foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT, Post\Media::TORRENT, Post\Media::UNKNOWN]) as $attachment) {
|
|
||||||
$attachments[] = ['type' => 'Document',
|
$attachments[] = ['type' => 'Document',
|
||||||
'mediaType' => $attachment['mimetype'],
|
'mediaType' => $attachment['mimetype'],
|
||||||
'url' => $attachment['url'],
|
'url' => $attachment['url'],
|
||||||
'name' => $attachment['description']];
|
'name' => $attachment['description']];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($type != 'Note') {
|
if ($type != 'Note') {
|
||||||
return $attachments;
|
return $attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]) as $attachment) {
|
foreach ($uriids as $uriid) {
|
||||||
|
foreach (Post\Media::getByURIId($uriid, [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]) as $attachment) {
|
||||||
|
if (in_array($attachment['url'], $urls)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$urls[] = $attachment['url'];
|
||||||
|
|
||||||
$attachments[] = ['type' => 'Document',
|
$attachments[] = ['type' => 'Document',
|
||||||
'mediaType' => $attachment['mimetype'],
|
'mediaType' => $attachment['mimetype'],
|
||||||
'url' => $attachment['url'],
|
'url' => $attachment['url'],
|
||||||
'name' => $attachment['description']];
|
'name' => $attachment['description']];
|
||||||
}
|
}
|
||||||
|
// Currently deactivated, since it creates side effects on Mastodon and Pleroma.
|
||||||
|
// It will be activated, once this cleared.
|
||||||
|
/*
|
||||||
|
foreach (Post\Media::getByURIId($uriid, [Post\Media::HTML]) as $attachment) {
|
||||||
|
if (in_array($attachment['url'], $urls)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$urls[] = $attachment['url'];
|
||||||
|
|
||||||
|
$attachments[] = ['type' => 'Page',
|
||||||
|
'mediaType' => $attachment['mimetype'],
|
||||||
|
'url' => $attachment['url'],
|
||||||
|
'name' => $attachment['description']];
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
return $attachments;
|
return $attachments;
|
||||||
}
|
}
|
||||||
|
@ -1543,7 +1550,7 @@ class Transmitter
|
||||||
$richbody = preg_replace_callback($regexp, ['self', 'mentionCallback'], $item['body']);
|
$richbody = preg_replace_callback($regexp, ['self', 'mentionCallback'], $item['body']);
|
||||||
$richbody = BBCode::removeAttachment($richbody);
|
$richbody = BBCode::removeAttachment($richbody);
|
||||||
|
|
||||||
$data['contentMap'][$language] = BBCode::convert($richbody, false);
|
$data['contentMap'][$language] = BBCode::convert($richbody, false, BBCode::EXTERNAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data['source'] = ['content' => $item['body'], 'mediaType' => "text/bbcode"];
|
$data['source'] = ['content' => $item['body'], 'mediaType' => "text/bbcode"];
|
||||||
|
|
|
@ -899,10 +899,10 @@ class DFRN
|
||||||
$entry->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET);
|
$entry->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$body = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
|
||||||
|
|
||||||
if ($item['private'] == Item::PRIVATE) {
|
if ($item['private'] == Item::PRIVATE) {
|
||||||
$body = Item::fixPrivatePhotos($item['body'], $owner['uid'], $item, $cid);
|
$body = Item::fixPrivatePhotos($body, $owner['uid'], $item, $cid);
|
||||||
} else {
|
|
||||||
$body = $item['body'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the abstract element. It is only locally important.
|
// Remove the abstract element. It is only locally important.
|
||||||
|
@ -1272,7 +1272,7 @@ class DFRN
|
||||||
|
|
||||||
switch ($rino_remote_version) {
|
switch ($rino_remote_version) {
|
||||||
case 1:
|
case 1:
|
||||||
$key = openssl_random_pseudo_bytes(16);
|
$key = random_bytes(16);
|
||||||
$data = self::aesEncrypt($postvars['data'], $key);
|
$data = self::aesEncrypt($postvars['data'], $key);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1441,8 +1441,8 @@ class DFRN
|
||||||
/**
|
/**
|
||||||
* Fetch the author data from head or entry items
|
* Fetch the author data from head or entry items
|
||||||
*
|
*
|
||||||
* @param object $xpath XPath object
|
* @param \DOMXPath $xpath XPath object
|
||||||
* @param object $context In which context should the data be searched
|
* @param \DOMNode $context In which context should the data be searched
|
||||||
* @param array $importer Record of the importer user mixed with contact of the content
|
* @param array $importer Record of the importer user mixed with contact of the content
|
||||||
* @param string $element Element name from which the data is fetched
|
* @param string $element Element name from which the data is fetched
|
||||||
* @param bool $onlyfetch Should the data only be fetched or should it update the contact record as well
|
* @param bool $onlyfetch Should the data only be fetched or should it update the contact record as well
|
||||||
|
@ -1453,7 +1453,7 @@ class DFRN
|
||||||
* @throws \ImagickException
|
* @throws \ImagickException
|
||||||
* @todo Find good type-hints for all parameter
|
* @todo Find good type-hints for all parameter
|
||||||
*/
|
*/
|
||||||
private static function fetchauthor($xpath, $context, $importer, $element, $onlyfetch, $xml = "")
|
private static function fetchauthor(\DOMXPath $xpath, \DOMNode $context, $importer, $element, $onlyfetch, $xml = "")
|
||||||
{
|
{
|
||||||
$author = [];
|
$author = [];
|
||||||
$author["name"] = XML::getFirstNodeValue($xpath, $element."/atom:name/text()", $context);
|
$author["name"] = XML::getFirstNodeValue($xpath, $element."/atom:name/text()", $context);
|
||||||
|
@ -1609,12 +1609,14 @@ class DFRN
|
||||||
}
|
}
|
||||||
|
|
||||||
// "dfrn:birthday" contains the birthday converted to UTC
|
// "dfrn:birthday" contains the birthday converted to UTC
|
||||||
$birthday = XML::getFirstNodeValue($xpath, $element . "/poco:birthday/text()", $context);
|
$birthday = XML::getFirstNodeValue($xpath, $element . "/dfrn:birthday/text()", $context);
|
||||||
|
try {
|
||||||
if (strtotime($birthday) > time()) {
|
$birthday_date = new \DateTime($birthday);
|
||||||
$bd_timestamp = strtotime($birthday);
|
if ($birthday_date > new \DateTime()) {
|
||||||
|
$poco["bdyear"] = $birthday_date->format("Y");
|
||||||
$poco["bdyear"] = date("Y", $bd_timestamp);
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Invalid birthday
|
||||||
}
|
}
|
||||||
|
|
||||||
// "poco:birthday" is the birthday in the format "yyyy-mm-dd"
|
// "poco:birthday" is the birthday in the format "yyyy-mm-dd"
|
||||||
|
@ -2245,6 +2247,8 @@ class DFRN
|
||||||
|
|
||||||
$item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
|
$item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
|
||||||
|
|
||||||
|
$item["body"] = Item::improveSharedDataInBody($item);
|
||||||
|
|
||||||
Tag::storeFromBody($item['uri-id'], $item["body"]);
|
Tag::storeFromBody($item['uri-id'], $item["body"]);
|
||||||
|
|
||||||
// We store the data from "dfrn:diaspora_signature" in a different table, this is done in "Item::insert"
|
// We store the data from "dfrn:diaspora_signature" in a different table, this is done in "Item::insert"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue