mirror of https://github.com/nextcloud/bookmarks
Merge branch 'master' into feature/778-custom-order
This commit is contained in:
commit
e440212183
|
@ -25,8 +25,9 @@ jobs:
|
|||
# do not stop on another job's failure
|
||||
fail-fast: false
|
||||
matrix:
|
||||
floccus-ref: ['master', 'develop']
|
||||
node-versions: [13.x]
|
||||
server-versions: ['18']
|
||||
server-versions: ['19']
|
||||
floccus-adapter:
|
||||
- nextcloud-folders
|
||||
test-name:
|
||||
|
@ -36,17 +37,17 @@ jobs:
|
|||
- benchmark parallel-root
|
||||
browsers: ['chrome']
|
||||
include:
|
||||
- server-versions: 18
|
||||
- server-versions: 19
|
||||
floccus-adapter: nextcloud-folders
|
||||
test-name: benchmark standard-root
|
||||
browsers: chrome
|
||||
- server-versions: 18
|
||||
- server-versions: 19
|
||||
floccus-adapter: nextcloud-folders
|
||||
test-name: benchmark parallel-root
|
||||
browsers: chrome
|
||||
|
||||
|
||||
name: ${{matrix.floccus-adapter}}:${{ matrix.test-name}}
|
||||
name: floccus@${{matrix.floccus-ref}} ${{matrix.floccus-adapter}}:${{ matrix.test-name}} ${{matrix.browsers}}
|
||||
|
||||
services:
|
||||
hub:
|
||||
|
@ -70,7 +71,7 @@ jobs:
|
|||
MYSQL_HOST: mysql
|
||||
NEXTCLOUD_TRUSTED_DOMAINS: nextcloud
|
||||
volumes:
|
||||
- apps:/home/runner/work/floccus/floccus/apps
|
||||
- /home/runner/work/bookmarks/bookmarks/apps:/var/www/html/custom_apps
|
||||
options: --name nextcloud
|
||||
mysql:
|
||||
image: mariadb:latest
|
||||
|
@ -82,24 +83,37 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: marcelklehr/floccus
|
||||
ref: master
|
||||
ref: ${{matrix.floccus-ref}}
|
||||
path: floccus
|
||||
|
||||
- name: Checkout bookmarks app
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
path: ${{ env.APP_NAME }}
|
||||
if: matrix.floccus-adapter == 'nextcloud-folders'
|
||||
|
||||
- name: Enable bookmarks app
|
||||
shell: bash
|
||||
run: |
|
||||
cd ${{ env.APP_NAME }}
|
||||
composer install
|
||||
if: matrix.floccus-adapter == 'nextcloud-folders'
|
||||
|
||||
- name: Enable bookmarks app
|
||||
shell: bash
|
||||
run: |
|
||||
sudo cp -R ${{ env.APP_NAME }} apps/
|
||||
NEXT_WAIT_TIME=0
|
||||
until [ $NEXT_WAIT_TIME -eq 25 ] || docker exec --user www-data nextcloud php occ app:enable ${{ env.APP_NAME }}; do
|
||||
sleep $(( NEXT_WAIT_TIME++ ))
|
||||
done
|
||||
[ $NEXT_WAIT_TIME -lt 25 ]
|
||||
if: matrix.floccus-adapter != 'fake'
|
||||
if: matrix.floccus-adapter == 'nextcloud-folders'
|
||||
|
||||
- name: List apps
|
||||
shell: bash
|
||||
run: |
|
||||
docker exec --user www-data nextcloud php occ app:list
|
||||
|
||||
- name: Enable APCu
|
||||
run: |
|
||||
|
@ -111,18 +125,6 @@ jobs:
|
|||
with:
|
||||
node-version: ${{ matrix.node-versions }}
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v1
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies & build
|
||||
working-directory: floccus
|
||||
run: |
|
||||
|
|
|
@ -79,6 +79,7 @@ jobs:
|
|||
- name: Set up Nextcloud and install app
|
||||
if: ${{ matrix.databases != 'pgsql'}}
|
||||
run: |
|
||||
sleep 25
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$MYSQL_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
|
||||
./occ app:enable -vvv -f ${{ env.APP_NAME }}
|
||||
|
@ -87,6 +88,7 @@ jobs:
|
|||
- name: Set up Nextcloud and install app
|
||||
if: ${{ matrix.databases == 'pgsql'}}
|
||||
run: |
|
||||
sleep 25
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$PGSQL_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
|
||||
./occ app:enable -vvv -f ${{ env.APP_NAME }}
|
||||
|
|
39
CHANGELOG.md
39
CHANGELOG.md
|
@ -4,7 +4,36 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [unreleased]
|
||||
## [3.3.0] - 2020-07-23
|
||||
|
||||
### New
|
||||
- Implement Activity app integration
|
||||
|
||||
### Fixed
|
||||
- Add repair step for duplicate shared folders
|
||||
- Fix untagged search (on postgres)
|
||||
- UI: Fix rename Tag
|
||||
- Build: Don't ship source maps
|
||||
- Repair steps: Add debug output
|
||||
- Bookmarks: Additionally always sort by ID to make ordering stable
|
||||
- Fix deletion of bookmarks that are not in tree
|
||||
- Update dependencies
|
||||
|
||||
## [3.2.5] - 2020-07-17
|
||||
|
||||
### Fixed
|
||||
- UI: Open URLs in new tabs
|
||||
- UI: Fix infinite scroll
|
||||
- Update documentation of API responses
|
||||
- Fix changing bookmark's folders
|
||||
- Fix tags API
|
||||
|
||||
## [3.2.4] - 2020-06-29
|
||||
|
||||
### Fixed
|
||||
- API: Fix PUT bookmark requests
|
||||
|
||||
## [3.2.2] - 2020-06-28
|
||||
|
||||
### New
|
||||
- In grid view the entire bookmark behaves like a link
|
||||
|
@ -15,6 +44,8 @@ E.g. in case of a browser sync as source.
|
|||
- Horizontal breadcrumb text alignment
|
||||
- Checkbox size in grid view
|
||||
- Menu toggle position
|
||||
- Deletion of tags
|
||||
- API: Don't send folder IDs that the client cannot access
|
||||
|
||||
## [3.2.1] - 2020-06-16
|
||||
|
||||
|
@ -276,7 +307,11 @@ Supported are NC 15 and 16, provided you are using PHP v7.1 and have gmp, intl a
|
|||
- FIX folder collapse css
|
||||
- FIX: Speed up findBookmarks SQL query
|
||||
|
||||
[3.2.1]: https://github.com/nextcloud/bookmarks/compare/v3.1.0...v3.2.1
|
||||
[3.3.0]: https://github.com/nextcloud/bookmarks/compare/v3.2.5...v3.3.0
|
||||
[3.2.5]: https://github.com/nextcloud/bookmarks/compare/v3.2.4...v3.2.5
|
||||
[3.2.4]: https://github.com/nextcloud/bookmarks/compare/v3.2.2...v3.2.4
|
||||
[3.2.2]: https://github.com/nextcloud/bookmarks/compare/v3.2.1...v3.2.2
|
||||
[3.2.1]: https://github.com/nextcloud/bookmarks/compare/v3.2.0...v3.2.1
|
||||
[3.2.0]: https://github.com/nextcloud/bookmarks/compare/v3.1.1...v3.2.0
|
||||
[3.1.1]: https://github.com/nextcloud/bookmarks/compare/v3.1.0...v3.1.1
|
||||
[3.1.0]: https://github.com/nextcloud/bookmarks/compare/v3.0.13...v3.1.0
|
||||
|
|
37
Makefile
37
Makefile
|
@ -7,7 +7,7 @@ source_dir=$(build_dir)/source
|
|||
sign_dir=$(build_dir)/sign
|
||||
package_name=$(app_name)
|
||||
cert_dir=$(HOME)/.nextcloud/certificates
|
||||
version+=3.2.1
|
||||
version+=3.3.0
|
||||
|
||||
all: dev-setup build-js-production test test-php
|
||||
|
||||
|
@ -60,25 +60,24 @@ clean-dev:
|
|||
appstore:
|
||||
mkdir -p $(sign_dir)
|
||||
rsync -a \
|
||||
--exclude=/build \
|
||||
--exclude=/docs \
|
||||
--exclude=/translationfiles \
|
||||
--exclude=/.tx \
|
||||
--exclude=/tests \
|
||||
--exclude=/screenshots \
|
||||
--exclude=/.git \
|
||||
--exclude=/.github \
|
||||
--exclude=/l10n/l10n.pl \
|
||||
--exclude=/CONTRIBUTING.md \
|
||||
--exclude=/issue_template.md \
|
||||
--exclude=/README.md \
|
||||
--exclude=/.gitattributes \
|
||||
--exclude=/.gitignore \
|
||||
--exclude=/.scrutinizer.yml \
|
||||
--exclude=/.travis.yml \
|
||||
--exclude=/Makefile \
|
||||
--exclude=/node_modules \
|
||||
--include=/js \
|
||||
--include=/vendor \
|
||||
--include=/CHANGELOG.md \
|
||||
--include=/README.md \
|
||||
--include=/composer.json \
|
||||
--include=/composer.lock \
|
||||
--include=/vendor \
|
||||
--include=/tests \
|
||||
--include=/templates \
|
||||
--include=/package.json \
|
||||
--include=/package-lock.json \
|
||||
--include=/src \
|
||||
--include=/lib \
|
||||
--include=/l10n \
|
||||
--include=/img \
|
||||
--include=/appinfo \
|
||||
--exclude=**/*.map \
|
||||
--exclude=/* \
|
||||
$(project_dir)/ $(sign_dir)/$(app_name)
|
||||
tar -czf $(build_dir)/$(app_name)-$(version).tar.gz \
|
||||
-C $(sign_dir) $(app_name)
|
||||
|
|
|
@ -63,7 +63,7 @@ npm run build
|
|||
- [QOwnNotes](https://www.qownnotes.org/) - Plain-text file markdown note taking desktop application (no sync, just importing bookmarks)
|
||||
|
||||
### iOS
|
||||
- [Nextbookmark](https://gitlab.com/altepizza/nextbookmark) - A minimal client for iOS
|
||||
- [Nextbookmark](https://gitlab.com/altepizza/nextbookmark) - A minimal client for iOS ([App Store entry](https://apps.apple.com/de/app/nextbookmark/id1500340092))
|
||||
|
||||
### Other
|
||||
- [uMarks](https://open-store.io/app/umarks.ernesst) - App for Ubuntu touch
|
||||
|
|
|
@ -43,10 +43,22 @@ Requirements:
|
|||
</settings>
|
||||
<repair-steps>
|
||||
<post-migration>
|
||||
<step>OCA\Bookmarks\Migration\DeduplicateSharedFoldersRepairStep</step>
|
||||
<step>OCA\Bookmarks\Migration\SuperfluousSharedFoldersRepairStep</step>
|
||||
<step>OCA\Bookmarks\Migration\OrphanedSharesRepairStep</step>
|
||||
<step>OCA\Bookmarks\Migration\OrphanedTreeItemsRepairStep</step>
|
||||
<step>OCA\Bookmarks\Migration\SuperfluousSharedFoldersRepairStep</step>
|
||||
<step>OCA\Bookmarks\Migration\GroupSharesUpdateRepairStep</step>
|
||||
</post-migration>
|
||||
</repair-steps>
|
||||
<activity>
|
||||
<settings>
|
||||
<setting>OCA\Bookmarks\Activity\Setting</setting>
|
||||
</settings>
|
||||
<filters>
|
||||
<filter>OCA\Bookmarks\Activity\Filter</filter>
|
||||
</filters>
|
||||
<providers>
|
||||
<provider>OCA\Bookmarks\Activity\Provider</provider>
|
||||
</providers>
|
||||
</activity>
|
||||
</info>
|
||||
|
|
|
@ -85,6 +85,9 @@ return [
|
|||
['name' => 'tags#full_tags', 'url' => '/public/rest/v2/tag', 'verb' => 'GET'],
|
||||
['name' => 'tags#rename_tag', 'url' => '/public/rest/v2/tag', 'verb' => 'POST'],
|
||||
['name' => 'tags#delete_tag', 'url' => '/public/rest/v2/tag', 'verb' => 'DELETE'],
|
||||
['name' => 'tags#rename_tag', 'url' => '/tag/{old_name}', 'verb' => 'POST'],
|
||||
['name' => 'tags#rename_tag', 'url' => '/tag/{old_name}', 'verb' => 'PUT'],
|
||||
['name' => 'tags#delete_tag', 'url' => '/tag/{old_name}', 'verb' => 'DELETE'],
|
||||
['name' => 'folders#get_folders', 'url' => '/public/rest/v2/folder', 'verb' => 'GET'],
|
||||
['name' => 'folders#get_folder', 'url' => '/public/rest/v2/folder/{folderId}', 'verb' => 'GET'],
|
||||
['name' => 'folders#add_folder', 'url' => '/public/rest/v2/folder', 'verb' => 'POST'],
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "dc5278c906cd69a3cc7219bc2aa71f4e",
|
||||
|
@ -54,20 +54,20 @@
|
|||
},
|
||||
{
|
||||
"name": "doctrine/inflector",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/inflector.git",
|
||||
"reference": "4111f6853aea6f28b2b1dcfdde83d12dd3d5e6e3"
|
||||
"reference": "4650c8b30c753a76bf44fb2ed00117d6f367490c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/inflector/zipball/4111f6853aea6f28b2b1dcfdde83d12dd3d5e6e3",
|
||||
"reference": "4111f6853aea6f28b2b1dcfdde83d12dd3d5e6e3",
|
||||
"url": "https://api.github.com/repos/doctrine/inflector/zipball/4650c8b30c753a76bf44fb2ed00117d6f367490c",
|
||||
"reference": "4650c8b30c753a76bf44fb2ed00117d6f367490c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2"
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^7.0",
|
||||
|
@ -128,7 +128,7 @@
|
|||
"uppercase",
|
||||
"words"
|
||||
],
|
||||
"time": "2020-05-09T15:09:09+00:00"
|
||||
"time": "2020-05-29T07:19:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
|
@ -302,16 +302,16 @@
|
|||
},
|
||||
{
|
||||
"name": "marcelklehr/link-preview",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/marcelklehr/link-preview.git",
|
||||
"reference": "2b79492e2cba41f0464265c09b93263c0276fc07"
|
||||
"reference": "b86dad0620da330ff1c20e13a153f27570d1bc73"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/marcelklehr/link-preview/zipball/2b79492e2cba41f0464265c09b93263c0276fc07",
|
||||
"reference": "2b79492e2cba41f0464265c09b93263c0276fc07",
|
||||
"url": "https://api.github.com/repos/marcelklehr/link-preview/zipball/b86dad0620da330ff1c20e13a153f27570d1bc73",
|
||||
"reference": "b86dad0620da330ff1c20e13a153f27570d1bc73",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -352,7 +352,7 @@
|
|||
"scraping",
|
||||
"url"
|
||||
],
|
||||
"time": "2018-09-01T21:10:51+00:00"
|
||||
"time": "2020-07-19T18:44:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
|
@ -619,29 +619,144 @@
|
|||
"time": "2019-03-08T08:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rowbot/url",
|
||||
"version": "3.0.1",
|
||||
"name": "rowbot/idna",
|
||||
"version": "0.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/TRowbotham/URL-Parser.git",
|
||||
"reference": "4c2e6d1c4efe47f42f62da034e6eaa8d719444cd"
|
||||
"url": "https://github.com/TRowbotham/idna.git",
|
||||
"reference": "400f6523b9b9fc0b7c301312fedb95ed42434478"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/TRowbotham/URL-Parser/zipball/4c2e6d1c4efe47f42f62da034e6eaa8d719444cd",
|
||||
"reference": "4c2e6d1c4efe47f42f62da034e6eaa8d719444cd",
|
||||
"url": "https://api.github.com/repos/TRowbotham/idna/zipball/400f6523b9b9fc0b7c301312fedb95ed42434478",
|
||||
"reference": "400f6523b9b9fc0b7c301312fedb95ed42434478",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"rowbot/punycode": "^1.0",
|
||||
"symfony/polyfill-intl-normalizer": "^1.18"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^6.5",
|
||||
"phpstan/phpstan": "^0.12.20",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.12.2",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.5.1",
|
||||
"symfony/cache": "^4.3 || ^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"idna": {
|
||||
"unicode_version": "13.0.0"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Rowbot\\Idna\\": "src/",
|
||||
"Rowbot\\Idna\\Resource\\": "resources/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Trevor Rowbotham"
|
||||
}
|
||||
],
|
||||
"description": "An implementation of UTS#46 Unicode IDNA Compatibility Processing.",
|
||||
"keywords": [
|
||||
"idn",
|
||||
"idna",
|
||||
"international domain names",
|
||||
"iri",
|
||||
"tr46",
|
||||
"unicode",
|
||||
"uts46"
|
||||
],
|
||||
"time": "2020-07-15T21:33:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rowbot/punycode",
|
||||
"version": "1.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/TRowbotham/punycode.git",
|
||||
"reference": "2327f31c2f0703edf8bcb6f500d7b012c2722552"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/TRowbotham/punycode/zipball/2327f31c2f0703edf8bcb6f500d7b012c2722552",
|
||||
"reference": "2327f31c2f0703edf8bcb6f500d7b012c2722552",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-mbstring": "*",
|
||||
"phpstan/phpstan": "^0.12.20",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.12.2",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.5.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Rowbot\\Punycode\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Trevor Rowbotham",
|
||||
"homepage": "https://trowbotham.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA).",
|
||||
"keywords": [
|
||||
"punycode",
|
||||
"rfc-3492",
|
||||
"rfc3492"
|
||||
],
|
||||
"time": "2020-07-15T21:39:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rowbot/url",
|
||||
"version": "3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/TRowbotham/URL-Parser.git",
|
||||
"reference": "bb021eea7e8a8cad26155c75981ca432a92cc719"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/TRowbotham/URL-Parser/zipball/bb021eea7e8a8cad26155c75981ca432a92cc719",
|
||||
"reference": "bb021eea7e8a8cad26155c75981ca432a92cc719",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.8.13",
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"lib-icu": ">=4.6",
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.1",
|
||||
"rowbot/idna": "^0.1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^6.3",
|
||||
"phpstan/phpstan": "^0.12.20",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.12.2",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.5.1",
|
||||
"symfony/cache": "^4.1 || ^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
|
@ -657,7 +772,7 @@
|
|||
"authors": [
|
||||
{
|
||||
"name": "Trevor Rowbotham",
|
||||
"homepage": "http://trowbotham.com",
|
||||
"homepage": "https://trowbotham.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
|
@ -671,11 +786,11 @@
|
|||
"url-parser",
|
||||
"url-parsing"
|
||||
],
|
||||
"time": "2020-02-29T21:31:27+00:00"
|
||||
"time": "2020-07-16T03:41:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.4.40",
|
||||
"version": "v3.4.42",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
|
@ -728,16 +843,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/dom-crawler",
|
||||
"version": "v3.4.40",
|
||||
"version": "v3.4.42",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dom-crawler.git",
|
||||
"reference": "ceacdab4abf7695ef6bec77c8b7983e1544c6358"
|
||||
"reference": "c3086a58a66b2a519c0b7ac80539a3727609ea9c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/ceacdab4abf7695ef6bec77c8b7983e1544c6358",
|
||||
"reference": "ceacdab4abf7695ef6bec77c8b7983e1544c6358",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/c3086a58a66b2a519c0b7ac80539a3727609ea9c",
|
||||
"reference": "c3086a58a66b2a519c0b7ac80539a3727609ea9c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -781,20 +896,20 @@
|
|||
],
|
||||
"description": "Symfony DomCrawler Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2020-03-16T08:31:04+00:00"
|
||||
"time": "2020-05-22T19:35:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.17.0",
|
||||
"version": "v1.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9"
|
||||
"reference": "1c302646f6efc070cd46856e600e5e0684d6b454"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
|
||||
"reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454",
|
||||
"reference": "1c302646f6efc070cd46856e600e5e0684d6b454",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -806,7 +921,11 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.17-dev"
|
||||
"dev-master": "1.18-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -839,20 +958,87 @@
|
|||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"time": "2020-05-12T16:14:59+00:00"
|
||||
"time": "2020-07-14T12:35:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.16.0",
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "a54881ec0ab3b2005c406aed0023c062879031e7"
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a54881ec0ab3b2005c406aed0023c062879031e7",
|
||||
"reference": "a54881ec0ab3b2005c406aed0023c062879031e7",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
|
||||
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.18-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for intl's Normalizer class and related functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"intl",
|
||||
"normalizer",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2020-07-14T12:35:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
|
||||
"reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -864,7 +1050,11 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.16-dev"
|
||||
"dev-master": "1.18-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -898,26 +1088,26 @@
|
|||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2020-05-08T16:50:20+00:00"
|
||||
"time": "2020-07-14T12:35:20+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/instantiator.git",
|
||||
"reference": "ae466f726242e637cebdd526a7d991b9433bacf1"
|
||||
"reference": "f350df0268e904597e3bd9c4685c53e0e333feea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1",
|
||||
"reference": "ae466f726242e637cebdd526a7d991b9433bacf1",
|
||||
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea",
|
||||
"reference": "f350df0268e904597e3bd9c4685c53e0e333feea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^6.0",
|
||||
|
@ -956,24 +1146,24 @@
|
|||
"constructor",
|
||||
"instantiate"
|
||||
],
|
||||
"time": "2019-10-21T16:45:58+00:00"
|
||||
"time": "2020-05-29T17:27:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.9.5",
|
||||
"version": "1.10.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef"
|
||||
"reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef",
|
||||
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
|
||||
"reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"myclabs/deep-copy": "self.version"
|
||||
|
@ -1004,7 +1194,7 @@
|
|||
"object",
|
||||
"object graph"
|
||||
],
|
||||
"time": "2020-01-17T21:11:47+00:00"
|
||||
"time": "2020-06-29T13:22:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
|
@ -1110,25 +1300,25 @@
|
|||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
|
||||
"reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b"
|
||||
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
|
||||
"reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
|
||||
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.x-dev"
|
||||
"dev-2.x": "2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1155,7 +1345,7 @@
|
|||
"reflection",
|
||||
"static analysis"
|
||||
],
|
||||
"time": "2020-04-27T09:25:28+00:00"
|
||||
"time": "2020-06-27T09:03:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-docblock",
|
||||
|
@ -1212,30 +1402,29 @@
|
|||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.1.0",
|
||||
"version": "1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "7462d5f123dfc080dfdf26897032a6513644fc95"
|
||||
"reference": "e878a14a65245fbe78f8080eba03b47c3b705651"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95",
|
||||
"reference": "7462d5f123dfc080dfdf26897032a6513644fc95",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e878a14a65245fbe78f8080eba03b47c3b705651",
|
||||
"reference": "e878a14a65245fbe78f8080eba03b47c3b705651",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"php": "^7.2 || ^8.0",
|
||||
"phpdocumentor/reflection-common": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-tokenizer": "^7.2",
|
||||
"mockery/mockery": "~1"
|
||||
"ext-tokenizer": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
"dev-1.x": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1254,37 +1443,37 @@
|
|||
}
|
||||
],
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"time": "2020-02-18T18:59:58+00:00"
|
||||
"time": "2020-06-27T10:12:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "v1.10.3",
|
||||
"version": "1.11.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "451c3cd1418cf640de218914901e51b064abb093"
|
||||
"reference": "b20034be5efcdab4fb60ca3a29cba2949aead160"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
|
||||
"reference": "451c3cd1418cf640de218914901e51b064abb093",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/b20034be5efcdab4fb60ca3a29cba2949aead160",
|
||||
"reference": "b20034be5efcdab4fb60ca3a29cba2949aead160",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.2",
|
||||
"php": "^5.3|^7.0",
|
||||
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
|
||||
"sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
|
||||
"sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
|
||||
"doctrine/instantiator": "^1.2",
|
||||
"php": "^7.2",
|
||||
"phpdocumentor/reflection-docblock": "^5.0",
|
||||
"sebastian/comparator": "^3.0 || ^4.0",
|
||||
"sebastian/recursion-context": "^3.0 || ^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "^2.5 || ^3.2",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
|
||||
"phpspec/phpspec": "^6.0",
|
||||
"phpunit/phpunit": "^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.10.x-dev"
|
||||
"dev-master": "1.11.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1317,7 +1506,7 @@
|
|||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"time": "2020-03-05T15:02:03+00:00"
|
||||
"time": "2020-07-08T12:44:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
@ -1573,16 +1762,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "8.5.5",
|
||||
"version": "8.5.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "63dda3b212a0025d380a745f91bdb4d8c985adb7"
|
||||
"reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/63dda3b212a0025d380a745f91bdb4d8c985adb7",
|
||||
"reference": "63dda3b212a0025d380a745f91bdb4d8c985adb7",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/34c18baa6a44f1d1fbf0338907139e9dce95b997",
|
||||
"reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1652,7 +1841,7 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2020-05-22T13:51:52+00:00"
|
||||
"time": "2020-06-22T07:06:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
|
@ -2271,23 +2460,23 @@
|
|||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/theseer/tokenizer.git",
|
||||
"reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
|
||||
"reference": "75a63c33a8577608444246075ea0af0d052e452a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
|
||||
"reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
|
||||
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a",
|
||||
"reference": "75a63c33a8577608444246075ea0af0d052e452a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"php": "^7.0"
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -2307,27 +2496,28 @@
|
|||
}
|
||||
],
|
||||
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
||||
"time": "2019-06-13T22:48:21+00:00"
|
||||
"time": "2020-07-12T23:59:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
"version": "1.8.0",
|
||||
"version": "1.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/webmozart/assert.git",
|
||||
"reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6"
|
||||
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
|
||||
"reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
|
||||
"url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
|
||||
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.3 || ^7.0",
|
||||
"php": "^5.3.3 || ^7.0 || ^8.0",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan": "<0.12.20",
|
||||
"vimeo/psalm": "<3.9.1"
|
||||
},
|
||||
"require-dev": {
|
||||
|
@ -2355,7 +2545,7 @@
|
|||
"check",
|
||||
"validate"
|
||||
],
|
||||
"time": "2020-04-18T12:12:48+00:00"
|
||||
"time": "2020-07-08T17:02:28+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
@ -56,7 +56,7 @@ Query bookmarks
|
|||
|
||||
{
|
||||
"status": "success",
|
||||
"data": [{ "id": "7", "title": "Google", "tags": ["firsttag"] }]
|
||||
"data": [{ "id": 7, "title": "Google", "tags": ["firsttag"] }]
|
||||
}
|
||||
|
||||
Create a bookmark
|
||||
|
@ -102,12 +102,12 @@ Create a bookmark
|
|||
{
|
||||
"status": "success",
|
||||
"item": {
|
||||
"id": "7",
|
||||
"id": 7,
|
||||
"url": "http://google.com",
|
||||
"title": "Google",
|
||||
"description":"in case i forget",
|
||||
"tags": ["search-engines", "uselessbookmark"],
|
||||
"folders": ["-1"]
|
||||
"folders": [-1]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,12 +142,12 @@ Get a bookmark
|
|||
{
|
||||
"status": "success",
|
||||
"item": {
|
||||
"id": "7",
|
||||
"id": 7,
|
||||
"url": "http://google.com",
|
||||
"title": "Google",
|
||||
"description":"in case i forget",
|
||||
"tags": ["search-engines", "uselessbookmark"],
|
||||
"folders": ["-1"]
|
||||
"folders": [-1]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,12 +190,12 @@ Edit a bookmark
|
|||
{
|
||||
"status": "success",
|
||||
"item": {
|
||||
"id": "7",
|
||||
"id": 7,
|
||||
"url": "http://google.com",
|
||||
"title": "Boogle",
|
||||
"description":"in case i forget",
|
||||
"tags": ["search-engines", "uselessbookmark"],
|
||||
"folders": ["-1"]
|
||||
"folders": [-1]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,10 +55,10 @@ Get full hierarchy
|
|||
|
||||
{
|
||||
"status": "success", "data": [
|
||||
{"id": "1", "title": "work", "parent_folder": "-1"},
|
||||
{"id": "2", "title": "personal", "parent_folder": "-1", "children": [
|
||||
{"id": "3", "title": "garden", "parent_folder": "2"},
|
||||
{"id": "4", "title": "music", "parent_folder": "2"}
|
||||
{"id": 1, "title": "work", "parent_folder": -1},
|
||||
{"id": 2, "title": "personal", "parent_folder": -1, "children": [
|
||||
{"id": 3, "title": "garden", "parent_folder": 2},
|
||||
{"id": 4, "title": "music", "parent_folder": 2}
|
||||
]},
|
||||
]
|
||||
}
|
||||
|
@ -93,9 +93,9 @@ Get single folder
|
|||
{
|
||||
"status": "success",
|
||||
"item": {
|
||||
"id": "2",
|
||||
"id": 2,
|
||||
"title": "My Personal Bookmarks",
|
||||
"parent_folder": "-1"
|
||||
"parent_folder": -1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ Edit a folder
|
|||
"item": {
|
||||
"id": 5,
|
||||
"title": "optional physical activity",
|
||||
"parent_folder": "-1"
|
||||
"parent_folder": -1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,10 +375,10 @@ Get folder's content order
|
|||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{"type": "folder", "id": "17"},
|
||||
{"type": "bookmark", "id": "204"},
|
||||
{"type": "bookmark", "id": "192"},
|
||||
{"type": "bookmark", "id": "210"}
|
||||
{"type": "folder", "id": 17},
|
||||
{"type": "bookmark", "id": 204},
|
||||
{"type": "bookmark", "id": 192},
|
||||
{"type": "bookmark", "id": 210}
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -409,10 +409,10 @@ Set folder's content order
|
|||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{"type": "folder", "id": "17"},
|
||||
{"type": "bookmark", "id": "204"},
|
||||
{"type": "bookmark", "id": "192"},
|
||||
{"type": "bookmark", "id": "210"}
|
||||
{"type": "folder", "id": 17},
|
||||
{"type": "bookmark", "id": 204},
|
||||
{"type": "bookmark", "id": 192},
|
||||
{"type": "bookmark", "id": 210}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
17
l10n/br.js
17
l10n/br.js
|
@ -3,10 +3,27 @@ OC.L10N.register(
|
|||
{
|
||||
"Details" : "Munudoù",
|
||||
"Rename" : "Adenvel",
|
||||
"Move" : "Diplasañ",
|
||||
"Delete" : "Dilemel",
|
||||
"Save" : "Entilañ",
|
||||
"New folder" : "Heuliad nevez",
|
||||
"Grid view" : "Diskwell ar roued",
|
||||
"List view" : "Gwelidik listenn",
|
||||
"Create" : "Krouiñ",
|
||||
"Cancel" : "Arrest",
|
||||
"Public" : "Publik",
|
||||
"Recently added" : "Ouzpennet n'eus ket pel zo",
|
||||
"Clear data" : "Lemel ar roadennoù",
|
||||
"URL" : "URL",
|
||||
"Tags" : "Klavioù",
|
||||
"Sharing" : "Rannan",
|
||||
"Share link" : "Rannan liamm",
|
||||
"Copy link" : "Kopiañ al liamm",
|
||||
"Allow editing" : "Cheñchamentoù aotreet",
|
||||
"Link copied" : "Liamm eilet",
|
||||
"Select" : "Dibab",
|
||||
"Privacy" : "Prevezder",
|
||||
"Link" : "Liamm",
|
||||
"Folder" : "Teuliad"
|
||||
},
|
||||
"nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > 19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 && n % 1000000 == 0) ? 3 : 4);");
|
||||
|
|
17
l10n/br.json
17
l10n/br.json
|
@ -1,10 +1,27 @@
|
|||
{ "translations": {
|
||||
"Details" : "Munudoù",
|
||||
"Rename" : "Adenvel",
|
||||
"Move" : "Diplasañ",
|
||||
"Delete" : "Dilemel",
|
||||
"Save" : "Entilañ",
|
||||
"New folder" : "Heuliad nevez",
|
||||
"Grid view" : "Diskwell ar roued",
|
||||
"List view" : "Gwelidik listenn",
|
||||
"Create" : "Krouiñ",
|
||||
"Cancel" : "Arrest",
|
||||
"Public" : "Publik",
|
||||
"Recently added" : "Ouzpennet n'eus ket pel zo",
|
||||
"Clear data" : "Lemel ar roadennoù",
|
||||
"URL" : "URL",
|
||||
"Tags" : "Klavioù",
|
||||
"Sharing" : "Rannan",
|
||||
"Share link" : "Rannan liamm",
|
||||
"Copy link" : "Kopiañ al liamm",
|
||||
"Allow editing" : "Cheñchamentoù aotreet",
|
||||
"Link copied" : "Liamm eilet",
|
||||
"Select" : "Dibab",
|
||||
"Privacy" : "Prevezder",
|
||||
"Link" : "Liamm",
|
||||
"Folder" : "Teuliad"
|
||||
},"pluralForm" :"nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > 19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 && n % 1000000 == 0) ? 3 : 4);"
|
||||
}
|
|
@ -56,7 +56,7 @@ OC.L10N.register(
|
|||
"RSS Feed" : "Fonte RSS",
|
||||
"This is an RSS feed of the current result set with access restricted to you." : "Isto é unha fonre RSS do conxunto de resultados actual con acceso restrinxido para vostede.",
|
||||
"Clear data" : "Limpar os datos",
|
||||
"Permanently remove all bookmarks from your account. There is no going back!" : "Retirar permanentemente todos os marcadores da súa conta. Isto non ten volta atrás!",
|
||||
"Permanently remove all bookmarks from your account. There is no going back!" : "Retirar de xeito permanente todos os marcadores da súa conta. Isto non ten volta atrás!",
|
||||
"Delete all bookmarks" : "Eliminar todos os marcadores",
|
||||
"Bookmarklet" : "Miniaplicación ligada",
|
||||
"Drag this to your browser bookmarks and click it to quickly bookmark a webpage" : "Arrastre isto cara aos marcadores do seu navegador e prema para marcar rapidamente unha páxina web",
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
"RSS Feed" : "Fonte RSS",
|
||||
"This is an RSS feed of the current result set with access restricted to you." : "Isto é unha fonre RSS do conxunto de resultados actual con acceso restrinxido para vostede.",
|
||||
"Clear data" : "Limpar os datos",
|
||||
"Permanently remove all bookmarks from your account. There is no going back!" : "Retirar permanentemente todos os marcadores da súa conta. Isto non ten volta atrás!",
|
||||
"Permanently remove all bookmarks from your account. There is no going back!" : "Retirar de xeito permanente todos os marcadores da súa conta. Isto non ten volta atrás!",
|
||||
"Delete all bookmarks" : "Eliminar todos os marcadores",
|
||||
"Bookmarklet" : "Miniaplicación ligada",
|
||||
"Drag this to your browser bookmarks and click it to quickly bookmark a webpage" : "Arrastre isto cara aos marcadores do seu navegador e prema para marcar rapidamente unha páxina web",
|
||||
|
|
|
@ -23,13 +23,17 @@ OC.L10N.register(
|
|||
"List view" : "Prikaz popisa",
|
||||
"Move selection" : "Premjesti odabir",
|
||||
"Delete selection" : "Izbriši odabir",
|
||||
"Open all selected" : "Otvori sve odabrano",
|
||||
"Select all visible" : "Odaberi sve vidljivo",
|
||||
"Cancel selection" : "Poništi odabir",
|
||||
"Selected {folders} folders and {bookmarks} bookmarks" : "Odabrane mape {folders} i knjižne oznake {bookmarks}",
|
||||
"_Selected %n bookmark_::_Selected %n bookmarks_" : ["Odabrana %n knjižna oznaka","Odabrane %n knjižne oznake","Odabrano %n knjižnih oznaka"],
|
||||
"_Selected %n folder_::_Selected %n folders_" : ["Odabrana %n mapa","Odabrane %n mape","Odabrano %n mapa"],
|
||||
"Enter a link" : "Unesite poveznicu",
|
||||
"Create" : "Stvori",
|
||||
"Cancel" : "Odustani",
|
||||
"Enter folder title" : "Unesite naslov mape",
|
||||
"Select folder" : "Odaberi mapu",
|
||||
"Shared by {user}" : "Dijeli {user}",
|
||||
"Shared privately" : "Privatno dijeljenje",
|
||||
"Public" : "Javna",
|
||||
|
@ -71,12 +75,14 @@ OC.L10N.register(
|
|||
"Select a user or group" : "Odaberite korisnika ili grupu",
|
||||
"Share link" : "Dijeli poveznicu",
|
||||
"Copy link" : "Kopiraj poveznicu",
|
||||
"Copy RSS feed" : "Kopiraj sažetak događaja RSS",
|
||||
"Delete link" : "Izbriši poveznicu",
|
||||
"Create public link" : "Stvori javnu poveznicu",
|
||||
"Allow editing" : "Dopusti uređivanje",
|
||||
"Allow sharing" : "Dopusti dijeljenje",
|
||||
"Remove share" : "Ukloni dijeljenje",
|
||||
"Link copied" : "Poveznica je kopirana",
|
||||
"RSS feed copied" : "Sažetak događaja RSS kopiran",
|
||||
"Select" : "Odaberi",
|
||||
"Previews" : "Pretpregledi",
|
||||
"In order to display real screenshots of your bookmarked websites, Bookmarks can use a third-party service to generate those." : "Kako biste prikazali stvarne snimke zaslona označenih mrežnih mjesta, možete se koristiti servisom treće strane za generiranje tih knjižnih oznaka.",
|
||||
|
@ -98,6 +104,7 @@ OC.L10N.register(
|
|||
"Link" : "Poveznica",
|
||||
"Enter bookmark URL" : "Unesite URL knjižne oznake",
|
||||
"Folder" : "Mapa",
|
||||
"Root Folder" : "Korijenska mapa",
|
||||
"Failed to find existing bookmark" : "Knjižna oznaka nije pronađena",
|
||||
"Failed to create bookmark" : "Stvaranje knjižne oznake nije uspjelo",
|
||||
"Failed to save bookmark" : "Spremanje knjižne oznake nije uspjelo",
|
||||
|
|
|
@ -21,13 +21,17 @@
|
|||
"List view" : "Prikaz popisa",
|
||||
"Move selection" : "Premjesti odabir",
|
||||
"Delete selection" : "Izbriši odabir",
|
||||
"Open all selected" : "Otvori sve odabrano",
|
||||
"Select all visible" : "Odaberi sve vidljivo",
|
||||
"Cancel selection" : "Poništi odabir",
|
||||
"Selected {folders} folders and {bookmarks} bookmarks" : "Odabrane mape {folders} i knjižne oznake {bookmarks}",
|
||||
"_Selected %n bookmark_::_Selected %n bookmarks_" : ["Odabrana %n knjižna oznaka","Odabrane %n knjižne oznake","Odabrano %n knjižnih oznaka"],
|
||||
"_Selected %n folder_::_Selected %n folders_" : ["Odabrana %n mapa","Odabrane %n mape","Odabrano %n mapa"],
|
||||
"Enter a link" : "Unesite poveznicu",
|
||||
"Create" : "Stvori",
|
||||
"Cancel" : "Odustani",
|
||||
"Enter folder title" : "Unesite naslov mape",
|
||||
"Select folder" : "Odaberi mapu",
|
||||
"Shared by {user}" : "Dijeli {user}",
|
||||
"Shared privately" : "Privatno dijeljenje",
|
||||
"Public" : "Javna",
|
||||
|
@ -69,12 +73,14 @@
|
|||
"Select a user or group" : "Odaberite korisnika ili grupu",
|
||||
"Share link" : "Dijeli poveznicu",
|
||||
"Copy link" : "Kopiraj poveznicu",
|
||||
"Copy RSS feed" : "Kopiraj sažetak događaja RSS",
|
||||
"Delete link" : "Izbriši poveznicu",
|
||||
"Create public link" : "Stvori javnu poveznicu",
|
||||
"Allow editing" : "Dopusti uređivanje",
|
||||
"Allow sharing" : "Dopusti dijeljenje",
|
||||
"Remove share" : "Ukloni dijeljenje",
|
||||
"Link copied" : "Poveznica je kopirana",
|
||||
"RSS feed copied" : "Sažetak događaja RSS kopiran",
|
||||
"Select" : "Odaberi",
|
||||
"Previews" : "Pretpregledi",
|
||||
"In order to display real screenshots of your bookmarked websites, Bookmarks can use a third-party service to generate those." : "Kako biste prikazali stvarne snimke zaslona označenih mrežnih mjesta, možete se koristiti servisom treće strane za generiranje tih knjižnih oznaka.",
|
||||
|
@ -96,6 +102,7 @@
|
|||
"Link" : "Poveznica",
|
||||
"Enter bookmark URL" : "Unesite URL knjižne oznake",
|
||||
"Folder" : "Mapa",
|
||||
"Root Folder" : "Korijenska mapa",
|
||||
"Failed to find existing bookmark" : "Knjižna oznaka nije pronađena",
|
||||
"Failed to create bookmark" : "Stvaranje knjižne oznake nije uspjelo",
|
||||
"Failed to save bookmark" : "Spremanje knjižne oznake nije uspjelo",
|
||||
|
|
|
@ -23,13 +23,17 @@ OC.L10N.register(
|
|||
"List view" : "Приказ листе",
|
||||
"Move selection" : "Премести одабир",
|
||||
"Delete selection" : "Обриши одабир",
|
||||
"Open all selected" : "Отвори све одабране",
|
||||
"Select all visible" : "Одабери све видљиве",
|
||||
"Cancel selection" : "Поништи одабир",
|
||||
"Selected {folders} folders and {bookmarks} bookmarks" : "Одабрано {folders} фасцикла(е) и {bookmarks} обележивач(а)",
|
||||
"_Selected %n bookmark_::_Selected %n bookmarks_" : ["Одабрано %n обележивач","Одабрано %n обележивача","Одабрано %n обележивача"],
|
||||
"_Selected %n folder_::_Selected %n folders_" : ["Одабрана %n фасцикла","Одабране %n фасцикле","Одабрано %n фасцикли"],
|
||||
"Enter a link" : "Унесите везу",
|
||||
"Create" : "Направи",
|
||||
"Cancel" : "Поништи",
|
||||
"Enter folder title" : "Унесите назив фасцикле",
|
||||
"Select folder" : "Одабери фасциклу",
|
||||
"Shared by {user}" : "Поделио {user}",
|
||||
"Shared privately" : "Подељено приватно",
|
||||
"Public" : "Јавно",
|
||||
|
@ -71,12 +75,14 @@ OC.L10N.register(
|
|||
"Select a user or group" : "Одаберите корисника или групу",
|
||||
"Share link" : "Веза дељења",
|
||||
"Copy link" : "Копирај везу",
|
||||
"Copy RSS feed" : "Копирај RSS довод",
|
||||
"Delete link" : "Обриши везу",
|
||||
"Create public link" : "Направи јавну везу",
|
||||
"Allow editing" : "Дозволи уређивање",
|
||||
"Allow sharing" : "Дозволи дељење",
|
||||
"Remove share" : "Уклони дељење",
|
||||
"Link copied" : "Веза ископирана",
|
||||
"RSS feed copied" : "RSS довод копиран",
|
||||
"Select" : "Изабери",
|
||||
"Previews" : "Прегледи",
|
||||
"In order to display real screenshots of your bookmarked websites, Bookmarks can use a third-party service to generate those." : "Да би приказале прави изглед сајта, Обележивач може користити сервис треће стране да би их генерисале.",
|
||||
|
@ -98,6 +104,7 @@ OC.L10N.register(
|
|||
"Link" : "Веза",
|
||||
"Enter bookmark URL" : "Унеси адресу за бележење",
|
||||
"Folder" : "Фасцикла",
|
||||
"Root Folder" : "Корена фасцикла",
|
||||
"Failed to find existing bookmark" : "Неспешно тражење постојећег обележивача",
|
||||
"Failed to create bookmark" : "Грешка при креирању обележивача",
|
||||
"Failed to save bookmark" : "Грешка при чувању обележивача",
|
||||
|
|
|
@ -21,13 +21,17 @@
|
|||
"List view" : "Приказ листе",
|
||||
"Move selection" : "Премести одабир",
|
||||
"Delete selection" : "Обриши одабир",
|
||||
"Open all selected" : "Отвори све одабране",
|
||||
"Select all visible" : "Одабери све видљиве",
|
||||
"Cancel selection" : "Поништи одабир",
|
||||
"Selected {folders} folders and {bookmarks} bookmarks" : "Одабрано {folders} фасцикла(е) и {bookmarks} обележивач(а)",
|
||||
"_Selected %n bookmark_::_Selected %n bookmarks_" : ["Одабрано %n обележивач","Одабрано %n обележивача","Одабрано %n обележивача"],
|
||||
"_Selected %n folder_::_Selected %n folders_" : ["Одабрана %n фасцикла","Одабране %n фасцикле","Одабрано %n фасцикли"],
|
||||
"Enter a link" : "Унесите везу",
|
||||
"Create" : "Направи",
|
||||
"Cancel" : "Поништи",
|
||||
"Enter folder title" : "Унесите назив фасцикле",
|
||||
"Select folder" : "Одабери фасциклу",
|
||||
"Shared by {user}" : "Поделио {user}",
|
||||
"Shared privately" : "Подељено приватно",
|
||||
"Public" : "Јавно",
|
||||
|
@ -69,12 +73,14 @@
|
|||
"Select a user or group" : "Одаберите корисника или групу",
|
||||
"Share link" : "Веза дељења",
|
||||
"Copy link" : "Копирај везу",
|
||||
"Copy RSS feed" : "Копирај RSS довод",
|
||||
"Delete link" : "Обриши везу",
|
||||
"Create public link" : "Направи јавну везу",
|
||||
"Allow editing" : "Дозволи уређивање",
|
||||
"Allow sharing" : "Дозволи дељење",
|
||||
"Remove share" : "Уклони дељење",
|
||||
"Link copied" : "Веза ископирана",
|
||||
"RSS feed copied" : "RSS довод копиран",
|
||||
"Select" : "Изабери",
|
||||
"Previews" : "Прегледи",
|
||||
"In order to display real screenshots of your bookmarked websites, Bookmarks can use a third-party service to generate those." : "Да би приказале прави изглед сајта, Обележивач може користити сервис треће стране да би их генерисале.",
|
||||
|
@ -96,6 +102,7 @@
|
|||
"Link" : "Веза",
|
||||
"Enter bookmark URL" : "Унеси адресу за бележење",
|
||||
"Folder" : "Фасцикла",
|
||||
"Root Folder" : "Корена фасцикла",
|
||||
"Failed to find existing bookmark" : "Неспешно тражење постојећег обележивача",
|
||||
"Failed to create bookmark" : "Грешка при креирању обележивача",
|
||||
"Failed to save bookmark" : "Грешка при чувању обележивача",
|
||||
|
|
12
l10n/vi.js
12
l10n/vi.js
|
@ -1,14 +1,20 @@
|
|||
OC.L10N.register(
|
||||
"bookmarks",
|
||||
{
|
||||
"Select bookmark" : "Chọn bookmark",
|
||||
"Details" : "Thông tin",
|
||||
"Rename" : "Đổi tên",
|
||||
"Move" : "Dịch chuyển",
|
||||
"Delete" : "Xóa",
|
||||
"Enter bookmark title" : "Nhập tiêu đề bookmark",
|
||||
"Save" : "Lưu",
|
||||
"Select one or more tags" : "Chọn một hoặc nhiều thẻ",
|
||||
"New folder" : "Tạo thư mục",
|
||||
"Grid view" : "Xem dạng Lưới",
|
||||
"List view" : "Xem dạng Danh sách",
|
||||
"Create" : "Tạo mới",
|
||||
"Cancel" : "Hủy bỏ",
|
||||
"Enter folder title" : "Nhập tiêu đề thư mục",
|
||||
"Public" : "Công khai",
|
||||
"Import" : "Nhập vào",
|
||||
"Export" : "Xuất ra",
|
||||
|
@ -25,8 +31,12 @@ OC.L10N.register(
|
|||
"Allow editing" : "Cho phép chỉnh sửa",
|
||||
"Select" : "Select",
|
||||
"Privacy" : "Riêng tư",
|
||||
"Failed to load settings" : "Tải cấu hình thất bại",
|
||||
"Failed to save settings" : "Lưu cấu hình thất bại",
|
||||
"Add a bookmark" : "Thêm một bookmark",
|
||||
"Title" : "Tên",
|
||||
"Link" : "Liên kết",
|
||||
"Folder" : "Thư mục"
|
||||
"Folder" : "Thư mục",
|
||||
"Root Folder" : "Thư mục gốc"
|
||||
},
|
||||
"nplurals=1; plural=0;");
|
||||
|
|
12
l10n/vi.json
12
l10n/vi.json
|
@ -1,12 +1,18 @@
|
|||
{ "translations": {
|
||||
"Select bookmark" : "Chọn bookmark",
|
||||
"Details" : "Thông tin",
|
||||
"Rename" : "Đổi tên",
|
||||
"Move" : "Dịch chuyển",
|
||||
"Delete" : "Xóa",
|
||||
"Enter bookmark title" : "Nhập tiêu đề bookmark",
|
||||
"Save" : "Lưu",
|
||||
"Select one or more tags" : "Chọn một hoặc nhiều thẻ",
|
||||
"New folder" : "Tạo thư mục",
|
||||
"Grid view" : "Xem dạng Lưới",
|
||||
"List view" : "Xem dạng Danh sách",
|
||||
"Create" : "Tạo mới",
|
||||
"Cancel" : "Hủy bỏ",
|
||||
"Enter folder title" : "Nhập tiêu đề thư mục",
|
||||
"Public" : "Công khai",
|
||||
"Import" : "Nhập vào",
|
||||
"Export" : "Xuất ra",
|
||||
|
@ -23,8 +29,12 @@
|
|||
"Allow editing" : "Cho phép chỉnh sửa",
|
||||
"Select" : "Select",
|
||||
"Privacy" : "Riêng tư",
|
||||
"Failed to load settings" : "Tải cấu hình thất bại",
|
||||
"Failed to save settings" : "Lưu cấu hình thất bại",
|
||||
"Add a bookmark" : "Thêm một bookmark",
|
||||
"Title" : "Tên",
|
||||
"Link" : "Liên kết",
|
||||
"Folder" : "Thư mục"
|
||||
"Folder" : "Thư mục",
|
||||
"Root Folder" : "Thư mục gốc"
|
||||
},"pluralForm" :"nplurals=1; plural=0;"
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace OCA\Bookmarks\Activity;
|
||||
|
||||
|
||||
use OCA\Bookmarks\Db\Bookmark;
|
||||
use OCA\Bookmarks\Db\BookmarkMapper;
|
||||
use OCA\Bookmarks\Db\Folder;
|
||||
use OCA\Bookmarks\Db\FolderMapper;
|
||||
use OCA\Bookmarks\Db\SharedFolder;
|
||||
use OCA\Bookmarks\Db\SharedFolderMapper;
|
||||
use OCA\Bookmarks\Db\TreeMapper;
|
||||
use OCA\Bookmarks\Events\BeforeDeleteEvent;
|
||||
use OCA\Bookmarks\Events\ChangeEvent;
|
||||
use OCA\Bookmarks\Events\CreateEvent;
|
||||
use OCA\Bookmarks\Events\MoveEvent;
|
||||
use OCA\Bookmarks\Service\Authorizer;
|
||||
use OCP\Activity\IManager;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use OCP\IL10N;
|
||||
|
||||
class ActivityPublisher implements IEventListener {
|
||||
/**
|
||||
* @var IManager
|
||||
*/
|
||||
private $activityManager;
|
||||
|
||||
private $appName;
|
||||
/**
|
||||
* @var IL10N
|
||||
*/
|
||||
private $l;
|
||||
/**
|
||||
* @var SharedFolderMapper
|
||||
*/
|
||||
private $sharedFolderMapper;
|
||||
/**
|
||||
* @var FolderMapper
|
||||
*/
|
||||
private $folderMapper;
|
||||
/**
|
||||
* @var BookmarkMapper
|
||||
*/
|
||||
private $bookmarkMapper;
|
||||
/**
|
||||
* @var TreeMapper
|
||||
*/
|
||||
private $treeMapper;
|
||||
/**
|
||||
* @var Authorizer
|
||||
*/
|
||||
private $authorizer;
|
||||
|
||||
public function __construct($appName, IManager $activityManager, IL10N $l, SharedFolderMapper $sharedFolderMapper, FolderMapper $folderMapper, BookmarkMapper $bookmarkMapper, TreeMapper $treeMapper, Authorizer $authorizer) {
|
||||
$this->appName = $appName;
|
||||
$this->activityManager = $activityManager;
|
||||
$this->l = $l;
|
||||
$this->sharedFolderMapper = $sharedFolderMapper;
|
||||
$this->folderMapper = $folderMapper;
|
||||
$this->bookmarkMapper = $bookmarkMapper;
|
||||
$this->treeMapper = $treeMapper;
|
||||
$this->authorizer = $authorizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle events
|
||||
*
|
||||
* @param Event $event
|
||||
*/
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof ChangeEvent)) {
|
||||
return;
|
||||
}
|
||||
switch ($event->getType()) {
|
||||
case TreeMapper::TYPE_FOLDER:
|
||||
$this->publishFolder($event);
|
||||
break;
|
||||
case TreeMapper::TYPE_BOOKMARK:
|
||||
$this->publishBookmark($event);
|
||||
break;
|
||||
case TreeMapper::TYPE_SHARE:
|
||||
$this->publishShare($event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function publishShare(ChangeEvent $event) {
|
||||
$activity = $this->activityManager->generateEvent();
|
||||
$activity->setApp($this->appName);
|
||||
$activity->setType('bookmarks');
|
||||
|
||||
$activity->setAuthor($this->authorizer->getUserId());
|
||||
$activity->setTimestamp(time());
|
||||
|
||||
/**
|
||||
* @var $sharedFolder SharedFolder
|
||||
*/
|
||||
try {
|
||||
$sharedFolder = $this->sharedFolderMapper->find($event->getId());
|
||||
} catch (DoesNotExistException $e) {
|
||||
return;
|
||||
} catch (MultipleObjectsReturnedException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$activity->setObject(TreeMapper::TYPE_FOLDER, $sharedFolder->getFolderId());
|
||||
|
||||
if ($event instanceof CreateEvent) {
|
||||
$activity->setSubject('share_created', ['folder' => $sharedFolder->getTitle(), 'sharee' => $sharedFolder->getUserId()]);
|
||||
} elseif ($event instanceof BeforeDeleteEvent) {
|
||||
$activity->setSubject('share_deleted', ['folder' => $sharedFolder->getTitle(), 'sharee' => $sharedFolder->getUserId()]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach([$activity->getAuthor(), $sharedFolder->getUserId()] as $user) {
|
||||
$activity->setAffectedUser($user);
|
||||
$this->activityManager->publish($activity);
|
||||
}
|
||||
}
|
||||
|
||||
public function publishFolder(ChangeEvent $event) {
|
||||
$activity = $this->activityManager->generateEvent();
|
||||
$activity->setApp($this->appName);
|
||||
$activity->setType('bookmarks');
|
||||
|
||||
$activity->setAuthor($this->authorizer->getUserId());
|
||||
$activity->setTimestamp(time());
|
||||
|
||||
/**
|
||||
* @var $folder Folder
|
||||
*/
|
||||
try {
|
||||
$folder = $this->folderMapper->find($event->getId());
|
||||
} catch (DoesNotExistException $e) {
|
||||
return;
|
||||
} catch (MultipleObjectsReturnedException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$activity->setObject(TreeMapper::TYPE_FOLDER, $folder->getId());
|
||||
if ($event instanceof CreateEvent) {
|
||||
$activity->setSubject('folder_created', ['folder' => $folder->getTitle()]);
|
||||
} elseif ($event instanceof BeforeDeleteEvent) {
|
||||
$activity->setSubject('folder_deleted', ['folder' => $folder->getTitle()]);
|
||||
} elseif ($event instanceof MoveEvent) {
|
||||
$activity->setSubject('folder_moved', ['folder' => $folder->getTitle()]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var $shares SharedFolder[]
|
||||
*/
|
||||
$shares = $this->sharedFolderMapper->findByOwner($this->authorizer->getUserId());
|
||||
$shares = array_merge($shares, $this->sharedFolderMapper->findByUser($this->authorizer->getUserId()));
|
||||
$affectedShares = array_filter($shares, function($sharedFolder) use ($folder){
|
||||
return $this->treeMapper->hasDescendant($sharedFolder->getFolderId(), TreeMapper::TYPE_FOLDER, $folder->getId());
|
||||
});
|
||||
$affectedUsers = array_map(static function($sharedFolder) {
|
||||
return $sharedFolder->getUserId();
|
||||
}, $affectedShares);
|
||||
$affectedUsers[] = $folder->getUserId();
|
||||
$affectedUsers[] = $this->authorizer->getUserId();
|
||||
|
||||
$affectedUsers = array_unique($affectedUsers);
|
||||
|
||||
foreach($affectedUsers as $user) {
|
||||
$activity->setAffectedUser($user);
|
||||
$this->activityManager->publish($activity);
|
||||
}
|
||||
}
|
||||
|
||||
public function publishBookmark(ChangeEvent $event) {
|
||||
$activity = $this->activityManager->generateEvent();
|
||||
$activity->setApp($this->appName);
|
||||
$activity->setType('bookmarks');
|
||||
|
||||
$activity->setAuthor($this->authorizer->getUserId());
|
||||
$activity->setTimestamp(time());
|
||||
|
||||
/**
|
||||
* @var $bookmark Bookmark
|
||||
*/
|
||||
try {
|
||||
$bookmark = $this->bookmarkMapper->find($event->getId());
|
||||
} catch (DoesNotExistException $e) {
|
||||
return;
|
||||
} catch (MultipleObjectsReturnedException $e) {
|
||||
return;
|
||||
}
|
||||
$activity->setObject(TreeMapper::TYPE_BOOKMARK, $bookmark->getId());
|
||||
|
||||
if ($event instanceof CreateEvent) {
|
||||
$activity->setSubject('bookmark_created', ['bookmark' => $bookmark->getTitle()]);
|
||||
} elseif ($event instanceof BeforeDeleteEvent) {
|
||||
$activity->setSubject('bookmark_deleted', ['bookmark' => $bookmark->getTitle()]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var $shares SharedFolder[]
|
||||
*/
|
||||
$shares = $this->sharedFolderMapper->findByOwner($this->authorizer->getUserId());
|
||||
$shares = array_merge($shares, $this->sharedFolderMapper->findByUser($this->authorizer->getUserId()));
|
||||
$affectedShares = array_filter($shares, function($sharedFolder) use ($bookmark) {
|
||||
return $this->treeMapper->hasDescendant($sharedFolder->getFolderId(), TreeMapper::TYPE_BOOKMARK, $bookmark->getId());
|
||||
});
|
||||
$affectedUsers = array_map(static function($sharedFolder) {
|
||||
return $sharedFolder->getUserId();
|
||||
}, $affectedShares);
|
||||
$affectedUsers[] = $bookmark->getUserId();
|
||||
$affectedUsers[] = $this->authorizer->getUserId();
|
||||
|
||||
$affectedUsers = array_unique($affectedUsers);
|
||||
|
||||
foreach($affectedUsers as $user) {
|
||||
$activity->setAffectedUser($user);
|
||||
$this->activityManager->publish($activity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace OCA\Bookmarks\Activity;
|
||||
|
||||
|
||||
use OCP\Activity\IFilter;
|
||||
|
||||
class Filter implements IFilter {
|
||||
/**
|
||||
* @var \OCP\IL10N
|
||||
*/
|
||||
private $l;
|
||||
/**
|
||||
* @var \OCP\IURLGenerator
|
||||
*/
|
||||
private $urlGenerator;
|
||||
|
||||
public function __construct(\OCP\IL10N $l, \OCP\IURLGenerator $urlGenerator) {
|
||||
$this->l = $l;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getIdentifier() {
|
||||
return 'bookmarks';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->l->t('Bookmarks');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getPriority() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getIcon() {
|
||||
return $this->urlGenerator->imagePath('bookmarks', 'bookmarks-black.svg');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function filterTypes(array $types) {
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function allowedApps() {
|
||||
return ['bookmarks'];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
<?php
|
||||
namespace OCA\Bookmarks\Activity;
|
||||
|
||||
|
||||
use OCA\Bookmarks\Db\Folder;
|
||||
use OCA\Bookmarks\Db\TreeMapper;
|
||||
use OCP\Activity\IEvent;
|
||||
use OCP\Activity\IManager;
|
||||
use OCP\Activity\IProvider;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\L10N\IFactory;
|
||||
|
||||
class Provider implements IProvider {
|
||||
|
||||
/**
|
||||
* @var IFactory
|
||||
*/
|
||||
private $languageFactory;
|
||||
/**
|
||||
* @var IURLGenerator
|
||||
*/
|
||||
private $url;
|
||||
/**
|
||||
* @var IUserManager
|
||||
*/
|
||||
private $userManager;
|
||||
/**
|
||||
* @var IManager
|
||||
*/
|
||||
private $activityManager;
|
||||
/**
|
||||
* @var \OCP\IL10N
|
||||
*/
|
||||
private $l;
|
||||
/**
|
||||
* @var TreeMapper
|
||||
*/
|
||||
private $treeMapper;
|
||||
|
||||
public function __construct(IFactory $languageFactory, IURLGenerator $url, IUserManager $userManager, IManager $activityManager, TreeMapper $treeMapper) {
|
||||
$this->languageFactory = $languageFactory;
|
||||
$this->url = $url;
|
||||
$this->userManager = $userManager;
|
||||
$this->activityManager = $activityManager;
|
||||
$this->treeMapper = $treeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function parse($language, IEvent $event, IEvent $previousEvent = null) {
|
||||
if ($event->getApp() !== 'bookmarks') {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
|
||||
$this->l = $this->languageFactory->get('bookmarks', $language);
|
||||
|
||||
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('bookmarks', 'bookmarks-black.svg')));
|
||||
|
||||
$subjectParameters = $event->getSubjectParameters();
|
||||
|
||||
$isSharee = $event->getAffectedUser() === $this->activityManager->getCurrentUserId();
|
||||
$sharee = $this->userManager->get($subjectParameters['sharee']);
|
||||
if ($sharee !== null) {
|
||||
$shareeName = $sharee->getDisplayName();
|
||||
}else{
|
||||
$shareeName = null;
|
||||
}
|
||||
|
||||
$isAuthor = $event->getAuthor() === $this->activityManager->getCurrentUserId();
|
||||
$author = $this->userManager->get($event->getAuthor());
|
||||
if ($author !== null) {
|
||||
$authorName = $author->getDisplayName();
|
||||
}else{
|
||||
$authorName = null;
|
||||
}
|
||||
|
||||
switch($event->getSubject()) {
|
||||
case 'bookmark_created':
|
||||
if ($isAuthor) {
|
||||
$event->setParsedSubject($this->l->t('You bookmarked "%s"', [
|
||||
$subjectParameters['bookmark']
|
||||
]));
|
||||
}elseif ($authorName){
|
||||
$event->setParsedSubject($this->l->t('%1$s bookmarked "%2$s"', [
|
||||
$authorName,
|
||||
$subjectParameters['bookmark'],
|
||||
]));
|
||||
}else {
|
||||
$event->setParsedSubject($this->l->t('Someone bookmarked "%s"', [
|
||||
$subjectParameters['bookmark']
|
||||
]));
|
||||
}
|
||||
break;
|
||||
case 'bookmark_deleted':
|
||||
if ($isAuthor) {
|
||||
$event->setParsedSubject($this->l->t('You deleted "%s"', [
|
||||
$subjectParameters['bookmark']
|
||||
]));
|
||||
}elseif ($authorName){
|
||||
$event->setParsedSubject($this->l->t('%1$s deleted "%1$s"', [
|
||||
$authorName,
|
||||
$subjectParameters['bookmark'],
|
||||
]));
|
||||
}else {
|
||||
$event->setParsedSubject($this->l->t('Someone deleted "%s"', [
|
||||
$subjectParameters['bookmark']
|
||||
]));
|
||||
}
|
||||
break;
|
||||
case 'folder_created':
|
||||
if ($isAuthor) {
|
||||
$event->setParsedSubject($this->l->t('You created folder "%s"', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}elseif ($authorName){
|
||||
$event->setParsedSubject($this->l->t('%1$s created folder "%2$s"', [
|
||||
$authorName,
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}else {
|
||||
$event->setParsedSubject($this->l->t('Someone created folder "%s"', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}
|
||||
break;
|
||||
case 'folder_moved':
|
||||
if ($isAuthor) {
|
||||
$event->setParsedSubject($this->l->t('You moved folder "%s"', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}elseif ($authorName){
|
||||
$event->setParsedSubject($this->l->t('%1$s moved folder "%2$s"', [
|
||||
$authorName,
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}else {
|
||||
$event->setParsedSubject($this->l->t('Someone moved folder "%s"', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}
|
||||
break;
|
||||
case 'folder_deleted':
|
||||
if ($isAuthor) {
|
||||
$event->setParsedSubject($this->l->t('You deleted folder "%s"', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}elseif ($authorName){
|
||||
$event->setParsedSubject($this->l->t('%1$s deleted folder "%2$s"', [
|
||||
$authorName,
|
||||
$subjectParameters['folder'],
|
||||
]));
|
||||
}else {
|
||||
$event->setParsedSubject($this->l->t('Someone deleted folder "%s"', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}
|
||||
break;
|
||||
case 'share_created':
|
||||
if ($isAuthor && $shareeName !== null) {
|
||||
$event->setParsedSubject($this->l->t('You shared folder "%1$s" with %2$s', [
|
||||
$subjectParameters['folder'],
|
||||
$shareeName
|
||||
]));
|
||||
}elseif ($isAuthor){
|
||||
$event->setParsedSubject($this->l->t('You shared folder "%s" with someone', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}elseif ($authorName && $isSharee) {
|
||||
$event->setParsedSubject($this->l->t('%1$s shared folder "%2$s" with you', [
|
||||
$authorName,
|
||||
$subjectParameters['folder'],
|
||||
]));
|
||||
}elseif ($isSharee) {
|
||||
$event->setParsedSubject($this->l->t('Someone shared folder "%s" with you', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}
|
||||
break;
|
||||
case 'share_deleted':
|
||||
if ($isAuthor && $shareeName) {
|
||||
$event->setParsedSubject($this->l->t('You unshared folder "%1$s" with %2$s', [
|
||||
$subjectParameters['folder'],
|
||||
$shareeName
|
||||
]));
|
||||
}elseif ($isAuthor){
|
||||
$event->setParsedSubject($this->l->t('You unshared folder "%s" with someone', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}elseif ($authorName && $isSharee) {
|
||||
$event->setParsedSubject($this->l->t('%1$s unshared folder "%2$s" with you', [
|
||||
$subjectParameters['folder'],
|
||||
$authorName
|
||||
]));
|
||||
}elseif ($isSharee) {
|
||||
$event->setParsedSubject($this->l->t('Someone unshared folder "%s" with you', [
|
||||
$subjectParameters['folder']
|
||||
]));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
|
||||
if ($event->getObjectType() === TreeMapper::TYPE_FOLDER && !str_contains($event->getSubject(), 'deleted')) {
|
||||
$event->setLink($this->url->linkToRouteAbsolute('bookmarks.web_view.indexfolder', ['folder' => $event->getObjectId()]));
|
||||
}
|
||||
if ($event->getObjectType() === TreeMapper::TYPE_BOOKMARK && !str_contains($event->getSubject(), 'deleted')) {
|
||||
/**
|
||||
* @var $folders Folder[]
|
||||
*/
|
||||
$folders = $this->treeMapper->findParentsOf(TreeMapper::TYPE_BOOKMARK, $event->getObjectId());
|
||||
$folders = array_filter($folders, function($folder) {
|
||||
return $folder->getUserId() === $this->activityManager->getCurrentUserId();
|
||||
});
|
||||
if (isset($folders[0])) {
|
||||
$event->setLink($this->url->linkToRouteAbsolute('bookmarks.web_view.indexfolder', ['folder' => $folders[0]->getId()]));
|
||||
}
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace OCA\Bookmarks\Activity;
|
||||
|
||||
use OCP\Activity\ISetting;
|
||||
use OCP\IL10N;
|
||||
|
||||
class Setting implements ISetting {
|
||||
|
||||
/** @var IL10N */
|
||||
protected $l;
|
||||
|
||||
/**
|
||||
* @param IL10N $l
|
||||
*/
|
||||
public function __construct(IL10N $l) {
|
||||
$this->l = $l;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Lowercase a-z and underscore only identifier
|
||||
* @since 11.0.0
|
||||
*/
|
||||
public function getIdentifier() {
|
||||
return 'bookmarks';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string A translated string
|
||||
* @since 11.0.0
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->l->t('Bookmarks');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int whether the filter should be rather on the top or bottom of
|
||||
* the admin section. The filters are arranged in ascending order of the
|
||||
* priority values. It is required to return a value between 0 and 100.
|
||||
* @since 11.0.0
|
||||
*/
|
||||
public function getPriority() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True when the option can be changed for the stream
|
||||
* @since 11.0.0
|
||||
*/
|
||||
public function canChangeStream() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True when the option can be changed for the stream
|
||||
* @since 11.0.0
|
||||
*/
|
||||
public function isDefaultEnabledStream() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True when the option can be changed for the mail
|
||||
* @since 11.0.0
|
||||
*/
|
||||
public function canChangeMail() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True when the option can be changed for the stream
|
||||
* @since 11.0.0
|
||||
*/
|
||||
public function isDefaultEnabledMail() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
namespace OCA\Bookmarks\AppInfo;
|
||||
|
||||
use OCA\Bookmarks\Activity\ActivityPublisher;
|
||||
use OCA\Bookmarks\Events\BeforeDeleteEvent;
|
||||
use OCA\Bookmarks\Events\CreateEvent;
|
||||
use OCA\Bookmarks\Events\MoveEvent;
|
||||
|
@ -47,10 +48,17 @@ class Application extends App {
|
|||
});
|
||||
|
||||
$dispatcher = $this->getContainer()->query(IEventDispatcher::class);
|
||||
|
||||
$dispatcher->addServiceListener(CreateEvent::class, HashManager::class);
|
||||
$dispatcher->addServiceListener(UpdateEvent::class, HashManager::class);
|
||||
$dispatcher->addServiceListener(BeforeDeleteEvent::class, HashManager::class);
|
||||
$dispatcher->addServiceListener(MoveEvent::class, HashManager::class);
|
||||
|
||||
$dispatcher->addServiceListener(CreateEvent::class, ActivityPublisher::class);
|
||||
$dispatcher->addServiceListener(UpdateEvent::class, ActivityPublisher::class);
|
||||
$dispatcher->addServiceListener(BeforeDeleteEvent::class, ActivityPublisher::class);
|
||||
$dispatcher->addServiceListener(MoveEvent::class, ActivityPublisher::class);
|
||||
|
||||
$dispatcher->addServiceListener(BeforeUserDeletedEvent::class, UserGroupListener::class);
|
||||
$dispatcher->addServiceListener(UserAddedEvent::class, UserGroupListener::class);
|
||||
$dispatcher->addServiceListener(UserRemovedEvent::class, UserGroupListener::class);
|
||||
|
|
|
@ -160,12 +160,14 @@ class BookmarkMapper extends QBMapper {
|
|||
$sqlSortColumn = $params->getSortBy('lastmodified', $this->getSortByColumns());
|
||||
|
||||
if ($sqlSortColumn === 'title') {
|
||||
$qb->orderBy($qb->createFunction('UPPER(`b`.`title`)'), 'ASC');
|
||||
$qb->addOrderBy($qb->createFunction('UPPER(`b`.`title`)'), 'ASC');
|
||||
} else if ($sqlSortColumn === 'index') {
|
||||
$qb->orderBy('t.'.$sqlSortColumn, 'ASC');
|
||||
} else {
|
||||
$qb->orderBy('b.'.$sqlSortColumn, 'DESC');
|
||||
$qb->addOrderBy('t.'.$sqlSortColumn, 'ASC');
|
||||
} else {
|
||||
$qb->addOrderBy('b.'.$sqlSortColumn, 'DESC');
|
||||
}
|
||||
// Always sort by id additionally, so the ordering is stable
|
||||
$qb->addOrderBy('b.id', 'ASC');
|
||||
|
||||
if ($params->getLimit() !== -1) {
|
||||
$qb->setMaxResults($params->getLimit());
|
||||
|
@ -321,18 +323,9 @@ class BookmarkMapper extends QBMapper {
|
|||
* @return array|Entity[]
|
||||
*/
|
||||
public function findUntagged($userId, QueryParameters $params): array {
|
||||
// select b.id from oc_bookmarks b LEFT JOIN oc_bookmarks_tags t ON b.id = t.bookmark_id WHERE t.bookmark_id IS NULL
|
||||
$qb = $this->_findByTags($userId);
|
||||
|
||||
$dbType = $this->config->getSystemValue('dbtype', 'sqlite');
|
||||
if ($dbType === 'pgsql') {
|
||||
$tagsCol = $qb->createFunction('array_to_string(array_agg(' . $qb->getColumnName('t.tag') . "), ',')");
|
||||
} else {
|
||||
$tagsCol = $qb->createFunction('GROUP_CONCAT(' . $qb->getColumnName('t.tag') . ')');
|
||||
}
|
||||
|
||||
$qb->groupBy(...Bookmark::$columns);
|
||||
$qb->having($qb->expr()->eq($tagsCol, $qb->createPositionalParameter('', IQueryBuilder::PARAM_STR)));
|
||||
$qb->orHaving($qb->expr()->isNull($tagsCol));
|
||||
$qb->andWhere($qb->expr()->isNull('t.bookmark_id'));
|
||||
|
||||
$this->_queryBuilderSortAndPaginate($qb, $params);
|
||||
return $this->findEntities($qb);
|
||||
|
@ -454,16 +447,7 @@ class BookmarkMapper extends QBMapper {
|
|||
// normalize url
|
||||
$entity->setUrl($this->urlNormalizer->normalize($entity->getUrl()));
|
||||
$entity->setLastmodified(time());
|
||||
|
||||
$newEntity = parent::update($entity);
|
||||
|
||||
// trigger event
|
||||
$this->eventDispatcher->dispatch(
|
||||
UpdateEvent::class,
|
||||
new UpdateEvent(TreeMapper::TYPE_BOOKMARK, $entity->getId())
|
||||
);
|
||||
|
||||
return $newEntity;
|
||||
return parent::update($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -504,10 +488,6 @@ class BookmarkMapper extends QBMapper {
|
|||
}
|
||||
|
||||
parent::insert($entity);
|
||||
|
||||
$this->eventDispatcher->dispatch(CreateEvent::class,
|
||||
new CreateEvent(TreeMapper::TYPE_BOOKMARK, $entity->getId())
|
||||
);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,6 @@ class FolderMapper extends QBMapper {
|
|||
*/
|
||||
public function update(Entity $entity): Entity {
|
||||
parent::update($entity);
|
||||
$this->eventDispatcher->dispatch(UpdateEvent::class, new UpdateEvent(TreeMapper::TYPE_FOLDER, $entity->getId()));
|
||||
return $entity;
|
||||
}
|
||||
|
||||
|
@ -123,7 +122,6 @@ class FolderMapper extends QBMapper {
|
|||
*/
|
||||
public function insert(Entity $entity): Entity {
|
||||
parent::insert($entity);
|
||||
$this->eventDispatcher->dispatch(CreateEvent::class, new CreateEvent(TreeMapper::TYPE_FOLDER, $entity->getId()));
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace OCA\Bookmarks\Db;
|
||||
|
||||
use OCA\Bookmarks\Events\CreateEvent;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
|
@ -19,15 +20,35 @@ class SharedFolderMapper extends QBMapper {
|
|||
* @var IDBConnection
|
||||
*/
|
||||
protected $db;
|
||||
/**
|
||||
* @var \OCP\EventDispatcher\IEventDispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
/**
|
||||
* TagMapper constructor.
|
||||
*
|
||||
* @param IDBConnection $db
|
||||
* @param \OCP\EventDispatcher\IEventDispatcher $eventDispatcher
|
||||
*/
|
||||
public function __construct(IDBConnection $db) {
|
||||
public function __construct(IDBConnection $db, \OCP\EventDispatcher\IEventDispatcher $eventDispatcher) {
|
||||
parent::__construct($db, 'bookmarks_shared_folders', SharedFolder::class);
|
||||
$this->db = $db;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return Entity
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function find(int $id): Entity {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(SharedFolder::$columns)
|
||||
->from('bookmarks_shared_folders', 'sf')
|
||||
->where($qb->expr()->eq('sf.id', $qb->createPositionalParameter($id)));
|
||||
return $this->findEntity($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -190,11 +211,25 @@ class SharedFolderMapper extends QBMapper {
|
|||
return parent::delete($sharedFolder);
|
||||
}
|
||||
|
||||
public function mount(int $id, int $share_id) {
|
||||
public function mount(int $id, int $share_id): void {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->insert('bookmarks_shared_to_shares')->values([
|
||||
'shared_folder_id' => $qb->createPositionalParameter($id),
|
||||
'share_id' => $qb->createPositionalParameter($share_id)
|
||||
])->execute();
|
||||
$this->eventDispatcher->dispatch(CreateEvent::class, new CreateEvent(
|
||||
TreeMapper::TYPE_SHARE,
|
||||
$id
|
||||
));
|
||||
}
|
||||
|
||||
public function findByUser(string $userId): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(array_map(static function ($c) {
|
||||
return 'sf.' . $c;
|
||||
}, SharedFolder::$columns))
|
||||
->from('bookmarks_shared_folders', 'sf')
|
||||
->where($qb->expr()->eq('sf.user_id', $qb->createPositionalParameter($userId)));
|
||||
return $this->findEntities($qb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -217,4 +217,23 @@ class TagMapper {
|
|||
$qb->execute();
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteTag($userId, string $old) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb
|
||||
->select('t.bookmark_id')
|
||||
->from('bookmarks_tags', 't')
|
||||
->innerJoin('t', 'bookmarks', 'bm', $qb->expr()->eq('t.bookmark_id', 'bm.id'))
|
||||
->where($qb->expr()->eq('t.tag', $qb->createNamedParameter($old)))
|
||||
->andWhere($qb->expr()->eq('bm.user_id', $qb->createNamedParameter($userId)));
|
||||
$affectedBookmarks = $qb->execute()->fetchAll(\PDO::FETCH_COLUMN);
|
||||
if (count($affectedBookmarks) !== 0) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb
|
||||
->delete('bookmarks_tags')
|
||||
->where($qb->expr()->in('bookmark_id', array_map([$qb, 'createNamedParameter'], $affectedBookmarks)))
|
||||
->andWhere($qb->expr()->eq('tag', $qb->createNamedParameter($old)));
|
||||
$qb->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace OCA\Bookmarks\Migration;
|
||||
|
||||
|
||||
use OCA\Bookmarks\Db\SharedFolder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\IRepairStep;
|
||||
|
||||
class DeduplicateSharedFoldersRepairStep implements IRepairStep {
|
||||
/**
|
||||
* @var IDBConnection
|
||||
*/
|
||||
private $db;
|
||||
|
||||
public function __construct(IDBConnection $db) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the step's name
|
||||
*/
|
||||
public function getName() {
|
||||
return 'Deduplicate shared bookmark folders';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
*/
|
||||
public function run(IOutput $output) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('p1.id')
|
||||
->from('bookmarks_shared_folders', 'p1')
|
||||
->leftJoin('p1', 'bookmarks_shared_folders', 'p2', $qb->expr()->andX(
|
||||
$qb->expr()->eq('p1.folder_id', 'p2.folder_id'),
|
||||
$qb->expr()->eq('p1.user_id', 'p2.user_id')
|
||||
))
|
||||
->where($qb->expr()->lt('p2.id', 'p1.id'));
|
||||
$duplicateSharedFolders = $qb->execute();
|
||||
$i = 0;
|
||||
while ($sharedFolder = $duplicateSharedFolders->fetchColumn()) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('bookmarks_shared_folders')
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($sharedFolder)))
|
||||
->andWhere($qb->expr()->eq('type', $qb->createPositionalParameter('share')))
|
||||
->execute();
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('bookmarks_shared_folders')
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($sharedFolder)))
|
||||
->execute();
|
||||
$i++;
|
||||
}
|
||||
$output->info("Removed $i duplicate shares");
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@ class OrphanedSharesRepairStep implements IRepairStep {
|
|||
->leftJoin('s', 'bookmarks_folders', 'f', $qb->expr()->eq('f.id', 's.folder_id'))
|
||||
->where($qb->expr()->isNull('f.id'));
|
||||
$shares = $qb->execute();
|
||||
$i = 0;
|
||||
while ($share = $shares->fetchColumn()) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$folders = $qb->select('f.id')
|
||||
|
@ -56,7 +57,10 @@ class OrphanedSharesRepairStep implements IRepairStep {
|
|||
$qb->delete('bookmarks_shares')
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($share)))
|
||||
->execute();
|
||||
$i++;
|
||||
}
|
||||
$output->info("Removed $i orphaned shares");
|
||||
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$publics = $qb->select('p.id')
|
||||
->from('bookmarks_folders_public', 'p')
|
||||
|
@ -64,11 +68,14 @@ class OrphanedSharesRepairStep implements IRepairStep {
|
|||
->where($qb->expr()->isNull('f.id'))
|
||||
->execute()
|
||||
->fetchAll(\PDO::FETCH_COLUMN);
|
||||
$i = 0;
|
||||
foreach ($publics as $publicId) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('bookmarks_folders_public')
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($publicId)))
|
||||
->execute();
|
||||
$i++;
|
||||
}
|
||||
$output->info("Removed $i orphaned public links");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,13 +35,17 @@ class OrphanedTreeItemsRepairStep implements IRepairStep {
|
|||
->leftJoin('t', 'bookmarks', 'b', $qb->expr()->eq('b.id', 't.id'))
|
||||
->where($qb->expr()->isNull('b.id'));
|
||||
$orphanedBookmarks = $qb->execute();
|
||||
$i = 0;
|
||||
while ($bookmark = $orphanedBookmarks->fetchColumn()) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('bookmarks_tree')
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($bookmark)))
|
||||
->andWhere($qb->expr()->eq('type', $qb->createPositionalParameter('bookmark')))
|
||||
->execute();
|
||||
$i++;
|
||||
}
|
||||
$output->info("Removed $i orphaned bookmarks");
|
||||
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('t.id')
|
||||
->from('bookmarks_tree', 't')
|
||||
|
@ -50,12 +54,15 @@ class OrphanedTreeItemsRepairStep implements IRepairStep {
|
|||
->where($qb->expr()->isNull('f.id'))
|
||||
->andWhere($qb->expr()->isNull('r.folder_id'));
|
||||
$orphanedFolders = $qb->execute();
|
||||
$i = 0;
|
||||
while ($folder = $orphanedFolders->fetchColumn()) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('bookmarks_tree')
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($folder)))
|
||||
->andWhere($qb->expr()->eq('type', $qb->createPositionalParameter('folder')))
|
||||
->execute();
|
||||
$i++;
|
||||
}
|
||||
$output->info("Removed $i orphaned bookmark folders");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ class SuperfluousSharedFoldersRepairStep implements IRepairStep {
|
|||
->where($qb->expr()->eq('t.type', $qb->createPositionalParameter('share')))
|
||||
->andWhere($qb->expr()->eq('s.owner', 'sf.user_id'));
|
||||
$superfluousSharedFolders = $qb->execute();
|
||||
$i = 0;
|
||||
while ($sharedFolder = $superfluousSharedFolders->fetchColumn()) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('bookmarks_tree')
|
||||
|
@ -48,6 +49,8 @@ class SuperfluousSharedFoldersRepairStep implements IRepairStep {
|
|||
$qb->delete('bookmarks_shared_folders')
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($sharedFolder)))
|
||||
->execute();
|
||||
$i++;
|
||||
}
|
||||
$output->info("Removed $i superfluous shares");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ use OCA\Bookmarks\Db\Folder;
|
|||
use OCA\Bookmarks\Db\FolderMapper;
|
||||
use OCA\Bookmarks\Db\TagMapper;
|
||||
use OCA\Bookmarks\Db\TreeMapper;
|
||||
use OCA\Bookmarks\Events\CreateEvent;
|
||||
use OCA\Bookmarks\Events\UpdateEvent;
|
||||
use OCA\Bookmarks\Exception\AlreadyExistsError;
|
||||
use OCA\Bookmarks\Exception\UnsupportedOperation;
|
||||
use OCA\Bookmarks\Exception\UrlParseError;
|
||||
|
@ -54,6 +56,10 @@ class BookmarkService {
|
|||
* @var FolderService
|
||||
*/
|
||||
private $folders;
|
||||
/**
|
||||
* @var \OCP\EventDispatcher\IEventDispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
/**
|
||||
* BookmarksService constructor.
|
||||
|
@ -67,8 +73,9 @@ class BookmarkService {
|
|||
* @param BookmarkPreviewer $bookmarkPreviewer
|
||||
* @param FaviconPreviewer $faviconPreviewer
|
||||
* @param FolderService $folders
|
||||
* @param \OCP\EventDispatcher\IEventDispatcher $eventDispatcher
|
||||
*/
|
||||
public function __construct(BookmarkMapper $bookmarkMapper, FolderMapper $folderMapper, TagMapper $tagMapper, TreeMapper $treeMapper, Authorizer $authorizer, LinkExplorer $linkExplorer, BookmarkPreviewer $bookmarkPreviewer, FaviconPreviewer $faviconPreviewer, \OCA\Bookmarks\Service\FolderService $folders) {
|
||||
public function __construct(BookmarkMapper $bookmarkMapper, FolderMapper $folderMapper, TagMapper $tagMapper, TreeMapper $treeMapper, Authorizer $authorizer, LinkExplorer $linkExplorer, BookmarkPreviewer $bookmarkPreviewer, FaviconPreviewer $faviconPreviewer, \OCA\Bookmarks\Service\FolderService $folders, \OCP\EventDispatcher\IEventDispatcher $eventDispatcher) {
|
||||
$this->bookmarkMapper = $bookmarkMapper;
|
||||
$this->treeMapper = $treeMapper;
|
||||
$this->authorizer = $authorizer;
|
||||
|
@ -78,6 +85,7 @@ class BookmarkService {
|
|||
$this->bookmarkPreviewer = $bookmarkPreviewer;
|
||||
$this->faviconPreviewer = $faviconPreviewer;
|
||||
$this->folders = $folders;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -168,6 +176,9 @@ class BookmarkService {
|
|||
$this->tagMapper->setOn($tags, $bookmark->getId());
|
||||
|
||||
$this->treeMapper->addToFolders(TreeMapper::TYPE_BOOKMARK, $bookmark->getId(), $folders);
|
||||
$this->eventDispatcher->dispatch(CreateEvent::class,
|
||||
new CreateEvent(TreeMapper::TYPE_BOOKMARK, $bookmark->getId())
|
||||
);
|
||||
return $bookmark;
|
||||
}
|
||||
|
||||
|
@ -232,11 +243,19 @@ class BookmarkService {
|
|||
* @var $currentOwnFolders Folder[]
|
||||
*/
|
||||
$currentOwnFolders = $this->treeMapper->findParentsOf(TreeMapper::TYPE_BOOKMARK, $bookmark->getId());
|
||||
$currentInaccessibleOwnFolders = array_filter($currentOwnFolders, function($folder) use ($userId) {
|
||||
return $this->folders->findShareByDescendantAndUser($folder, $userId) === null;
|
||||
});
|
||||
if ($bookmark->getUserId() !== $userId) {
|
||||
$currentInaccessibleOwnFolders = array_map(static function ($f) {
|
||||
return $f->getId();
|
||||
}, array_filter($currentOwnFolders, function ($folder) use ($userId) {
|
||||
return $this->folders->findShareByDescendantAndUser($folder, $userId) === null;
|
||||
})
|
||||
);
|
||||
}else{
|
||||
$currentInaccessibleOwnFolders = [];
|
||||
}
|
||||
|
||||
$this->treeMapper->setToFolders(TreeMapper::TYPE_BOOKMARK, $bookmark->getId(), array_merge($currentInaccessibleOwnFolders, $ownFolders));
|
||||
$ownFolders = array_unique(array_merge($currentInaccessibleOwnFolders, $ownFolders));
|
||||
$this->treeMapper->setToFolders(TreeMapper::TYPE_BOOKMARK, $bookmark->getId(), $ownFolders);
|
||||
if (count($ownFolders) === 0) {
|
||||
$this->bookmarkMapper->delete($bookmark);
|
||||
return null;
|
||||
|
@ -246,6 +265,13 @@ class BookmarkService {
|
|||
if ($tags !== null) {
|
||||
$this->tagMapper->setOn($tags, $bookmark->getId());
|
||||
}
|
||||
|
||||
// trigger event
|
||||
$this->eventDispatcher->dispatch(
|
||||
UpdateEvent::class,
|
||||
new UpdateEvent(TreeMapper::TYPE_BOOKMARK, $bookmark->getId())
|
||||
);
|
||||
|
||||
$this->bookmarkMapper->update($bookmark);
|
||||
|
||||
return $bookmark;
|
||||
|
@ -283,7 +309,7 @@ class BookmarkService {
|
|||
$bookmark = $this->bookmarkMapper->find($bookmarkId);
|
||||
if ($folder->getUserId() === $bookmark->getUserId()) {
|
||||
$this->treeMapper->addToFolders(TreeMapper::TYPE_BOOKMARK, $bookmarkId, [$folderId]);
|
||||
}else{
|
||||
} else {
|
||||
$this->_addBookmark($bookmark->getTitle(), $bookmark->getUrl(), $bookmark->getDescription(), $folder->getUserId(), [], [$folder->getId()]);
|
||||
}
|
||||
}
|
||||
|
@ -300,6 +326,9 @@ class BookmarkService {
|
|||
foreach ($parents as $parent) {
|
||||
$this->treeMapper->deleteEntry(TreeMapper::TYPE_BOOKMARK, $bookmark->getId(), $parent->getId());
|
||||
}
|
||||
if (count($parents) === 0) {
|
||||
$this->bookmarkMapper->delete($bookmark);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,6 +13,9 @@ use OCA\Bookmarks\Db\SharedFolder;
|
|||
use OCA\Bookmarks\Db\SharedFolderMapper;
|
||||
use OCA\Bookmarks\Db\ShareMapper;
|
||||
use OCA\Bookmarks\Db\TreeMapper;
|
||||
use OCA\Bookmarks\Events\CreateEvent;
|
||||
use OCA\Bookmarks\Events\MoveEvent;
|
||||
use OCA\Bookmarks\Events\UpdateEvent;
|
||||
use OCA\Bookmarks\Exception\AlreadyExistsError;
|
||||
use OCA\Bookmarks\Exception\HtmlParseError;
|
||||
use OCA\Bookmarks\Exception\UnauthorizedAccessError;
|
||||
|
@ -20,6 +23,7 @@ use OCA\Bookmarks\Exception\UnsupportedOperation;
|
|||
use OCA\Bookmarks\Exception\UserLimitExceededError;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\Share\IShare;
|
||||
|
@ -57,6 +61,10 @@ class FolderService {
|
|||
* @var IL10N
|
||||
*/
|
||||
private $l10n;
|
||||
/**
|
||||
* @var IEventDispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
/**
|
||||
* FolderService constructor.
|
||||
|
@ -69,8 +77,9 @@ class FolderService {
|
|||
* @param IGroupManager $groupManager
|
||||
* @param HtmlImporter $htmlImporter
|
||||
* @param IL10N $l10n
|
||||
* @param IEventDispatcher $eventDispatcher
|
||||
*/
|
||||
public function __construct(FolderMapper $folderMapper, TreeMapper $treeMapper, ShareMapper $shareMapper, SharedFolderMapper $sharedFolderMapper, PublicFolderMapper $publicFolderMapper, IGroupManager $groupManager, \OCA\Bookmarks\Service\HtmlImporter $htmlImporter, IL10N $l10n) {
|
||||
public function __construct(FolderMapper $folderMapper, TreeMapper $treeMapper, ShareMapper $shareMapper, SharedFolderMapper $sharedFolderMapper, PublicFolderMapper $publicFolderMapper, IGroupManager $groupManager, \OCA\Bookmarks\Service\HtmlImporter $htmlImporter, IL10N $l10n, IEventDispatcher $eventDispatcher) {
|
||||
$this->folderMapper = $folderMapper;
|
||||
$this->treeMapper = $treeMapper;
|
||||
$this->shareMapper = $shareMapper;
|
||||
|
@ -79,6 +88,7 @@ class FolderService {
|
|||
$this->groupManager = $groupManager;
|
||||
$this->htmlImporter = $htmlImporter;
|
||||
$this->l10n = $l10n;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,6 +111,8 @@ class FolderService {
|
|||
|
||||
$this->folderMapper->insert($folder);
|
||||
$this->treeMapper->move(TreeMapper::TYPE_FOLDER, $folder->getId(), $parentFolderId);
|
||||
|
||||
$this->eventDispatcher->dispatch(CreateEvent::class, new CreateEvent(TreeMapper::TYPE_FOLDER, $folder->getId()));
|
||||
return $folder;
|
||||
}
|
||||
|
||||
|
@ -170,10 +182,8 @@ class FolderService {
|
|||
foreach ($shares as $share) {
|
||||
$this->deleteShare($share->getId());
|
||||
}
|
||||
$publicFolders = $this->publicFolderMapper->findByFolder($folderId);
|
||||
foreach ($publicFolders as $publicFolder) {
|
||||
$this->publicFolderMapper->delete($publicFolder);
|
||||
}
|
||||
$publicFolder = $this->publicFolderMapper->findByFolder($folderId);
|
||||
$this->publicFolderMapper->delete($publicFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -232,6 +242,7 @@ class FolderService {
|
|||
if (isset($title)) {
|
||||
$folder->setTitle($title);
|
||||
$this->folderMapper->update($folder);
|
||||
$this->eventDispatcher->dispatch(UpdateEvent::class, new UpdateEvent(TreeMapper::TYPE_FOLDER, $folder->getId()));
|
||||
}
|
||||
if (isset($parent_folder)) {
|
||||
$this->treeMapper->move(TreeMapper::TYPE_FOLDER, $folder->getId(), $parent_folder);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "bookmarks",
|
||||
"version": "3.2.1",
|
||||
"version": "3.3.0",
|
||||
"main": "js/index.js",
|
||||
"scripts": {
|
||||
"dev": "webpack --config webpack.dev.js",
|
||||
|
@ -20,25 +20,25 @@
|
|||
},
|
||||
"homepage": "https://github.com/nextcloud/bookmarks#readme",
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.10.1",
|
||||
"@babel/polyfill": "^7.10.4",
|
||||
"@nextcloud/auth": "^1.3.0",
|
||||
"@nextcloud/axios": "^1.3.2",
|
||||
"@nextcloud/axios": "^1.3.3",
|
||||
"@nextcloud/dialogs": "^1.4.0",
|
||||
"@nextcloud/router": "^1.1.0",
|
||||
"@nextcloud/vue": "^2.0.0",
|
||||
"@nextcloud/vue": "^2.2.1",
|
||||
"copy-text-to-clipboard": "^2.2.0",
|
||||
"humanize-duration": "^3.23.0",
|
||||
"humanize-duration": "^3.23.1",
|
||||
"vue": "^2.6.11",
|
||||
"vue-click-outside": "^1.1.0",
|
||||
"vue-router": "^3.3.4",
|
||||
"vue-simple-progress": "^1.1.1",
|
||||
"vuex": "^3.4.0",
|
||||
"vuex": "^3.5.1",
|
||||
"vuex-router-sync": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.2",
|
||||
"@babel/core": "^7.10.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
|
||||
"@babel/preset-env": "^7.10.2",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@nextcloud/eslint-config": "^2.0.0",
|
||||
"@nextcloud/eslint-plugin": "^1.4.0",
|
||||
"@vue/test-utils": "^1.0.3",
|
||||
|
@ -49,7 +49,7 @@
|
|||
"eslint-config-standard": "^12.0.0",
|
||||
"eslint-import-resolver-webpack": "^0.12.1",
|
||||
"eslint-loader": "^3.0.4",
|
||||
"eslint-plugin-import": "^2.21.2",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.1.1",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
|
@ -61,10 +61,10 @@
|
|||
"stylelint-config-recommended-scss": "^3.3.0",
|
||||
"stylelint-scss": "^3.17.2",
|
||||
"stylelint-webpack-plugin": "^0.10.5",
|
||||
"vue-loader": "^15.9.2",
|
||||
"vue-loader": "^15.9.3",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-merge": "^4.2.2",
|
||||
"webpack-node-externals": "^1.7.2"
|
||||
},
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
}">
|
||||
<a
|
||||
:href="url"
|
||||
class="bookmark__click-link" />
|
||||
class="bookmark__click-link"
|
||||
target="_blank" />
|
||||
<template v-if="!renaming">
|
||||
<div v-if="isEditable" class="bookmark__checkbox">
|
||||
<input v-model="selected" class="checkbox" type="checkbox"><label
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
:class="{
|
||||
bookmarkslist: true,
|
||||
'bookmarkslist--gridview': viewMode === 'grid'
|
||||
}">
|
||||
}"
|
||||
@scroll="onScroll">
|
||||
<CreateBookmark v-if="newBookmark" />
|
||||
<CreateFolder v-if="newFolder" />
|
||||
<template v-if="$route.name === routes.FOLDER || $route.name === routes.HOME">
|
||||
|
@ -107,6 +108,16 @@ export default {
|
|||
return this.$store.state.viewMode
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onScroll() {
|
||||
if (
|
||||
this.$el.scrollHeight
|
||||
< this.$el.scrollTop + this.$el.clientHeight + 500
|
||||
) {
|
||||
this.$store.dispatch(actions.FETCH_PAGE)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
|
|
@ -49,9 +49,11 @@
|
|||
</AppNavigationItem>
|
||||
</template>
|
||||
</ul>
|
||||
<AppNavigationSettings v-if="!isPublic">
|
||||
<Settings />
|
||||
</AppNavigationSettings>
|
||||
<template #footer>
|
||||
<AppNavigationSettings v-if="!isPublic">
|
||||
<Settings />
|
||||
</AppNavigationSettings>
|
||||
</template>
|
||||
</AppNavigation>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -60,7 +60,6 @@ export default {
|
|||
},
|
||||
|
||||
async created() {
|
||||
document.addEventListener('scroll', this.onScroll)
|
||||
this.search = new OCA.Search(this.onSearch, this.onResetSearch)
|
||||
// set loading indicator
|
||||
this.$store.commit(mutations.FETCH_START, { type: 'bookmarks' })
|
||||
|
@ -124,15 +123,6 @@ export default {
|
|||
onResetSearch() {
|
||||
this.$router.push({ name: privateRoutes.HOME })
|
||||
},
|
||||
|
||||
onScroll() {
|
||||
if (
|
||||
document.body.scrollHeight
|
||||
< window.scrollY + window.innerHeight + 500
|
||||
) {
|
||||
this.$store.dispatch(actions.FETCH_PAGE)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -47,7 +47,6 @@ export default {
|
|||
},
|
||||
|
||||
async created() {
|
||||
document.addEventListener('scroll', this.onScroll)
|
||||
// this.search = new OCA.Search(this.onSearch, this.onResetSearch)
|
||||
this.$store.commit(mutations.SET_AUTH_TOKEN, this.$route.params.token)
|
||||
// set loading indicator
|
||||
|
@ -97,15 +96,6 @@ export default {
|
|||
onResetSearch() {
|
||||
this.$router.push({ name: this.routes.HOME })
|
||||
},
|
||||
|
||||
onScroll() {
|
||||
if (
|
||||
document.body.scrollHeight
|
||||
< window.scrollY + window.innerHeight + 500
|
||||
) {
|
||||
this.$store.dispatch(actions.FETCH_PAGE)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
const rev = '#1'
|
||||
|
||||
const DYNAMIC_CACHE = 'dynamic-cache-v3.2.1' + rev
|
||||
const STATIC_CACHE = 'static-cache-v3.2.1' + rev
|
||||
const DYNAMIC_CACHE = 'dynamic-cache-v3.2.4' + rev
|
||||
const STATIC_CACHE = 'static-cache-v3.2.4' + rev
|
||||
const FILES_TO_CACHE = [
|
||||
'./',
|
||||
]
|
||||
|
|
|
@ -294,10 +294,10 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
[actions.RENAME_TAG]({ commit, dispatch, state }, { oldName, newName }) {
|
||||
async [actions.RENAME_TAG]({ commit, dispatch, state }, { oldName, newName }) {
|
||||
commit(mutations.FETCH_START, { type: 'tag' })
|
||||
try {
|
||||
const response = axios
|
||||
const response = await axios
|
||||
.put(url(state, `/tag/${oldName}`), {
|
||||
name: newName,
|
||||
})
|
||||
|
@ -315,7 +315,7 @@ export default {
|
|||
commit(mutations.FETCH_END, 'tag')
|
||||
commit(
|
||||
mutations.SET_ERROR,
|
||||
AppGlobal.methods.t('bookmarks', 'Failed to create bookmark')
|
||||
AppGlobal.methods.t('bookmarks', 'Failed to rename tag')
|
||||
)
|
||||
throw err
|
||||
}
|
||||
|
@ -583,6 +583,7 @@ export default {
|
|||
},
|
||||
[actions.FETCH_PAGE]({ dispatch, commit, state }) {
|
||||
if (state.fetchState.reachedEnd) return
|
||||
if (state.loading.bookmarks) return
|
||||
let canceled = false
|
||||
commit(mutations.FETCH_START, {
|
||||
type: 'bookmarks',
|
||||
|
|
|
@ -157,6 +157,13 @@ export default {
|
|||
Vue.set(state.fetchState, 'page', 0)
|
||||
Vue.set(state.fetchState, 'reachedEnd', false)
|
||||
Vue.set(state.fetchState, 'query', query)
|
||||
|
||||
// cancel currently running request
|
||||
if (typeof state.loading['bookmarks'] === 'function') {
|
||||
state.loading['bookmarks']()
|
||||
}
|
||||
// stop loading
|
||||
Vue.set(state.loading, 'bookmarks', false)
|
||||
},
|
||||
[mutations.FETCH_START](state, event) {
|
||||
if (typeof state.loading[event.type] === 'function') {
|
||||
|
|
|
@ -6,6 +6,7 @@ use OCA\Bookmarks\BackgroundJobs\PreviewsJob;
|
|||
use OC\BackgroundJob\JobList;
|
||||
use OCA\Bookmarks\Db\Bookmark;
|
||||
use OCA\Bookmarks\Db\BookmarkMapper;
|
||||
use OCA\Bookmarks\Service\Authorizer;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
|
|
|
@ -196,6 +196,7 @@ class BookmarkControllerTest extends TestCase {
|
|||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function setupBookmarks(): void {
|
||||
$this->authorizer->setUserId($this->userId);
|
||||
$bookmark1 = Bookmark::fromArray([
|
||||
'userId' => $this->userId,
|
||||
'url' => 'https://www.golem.de',
|
||||
|
@ -227,6 +228,7 @@ class BookmarkControllerTest extends TestCase {
|
|||
*/
|
||||
public function setupBookmarksWithPublicFolder(): void {
|
||||
$this->setupBookmarks();
|
||||
$this->authorizer->setUserId($this->userId);
|
||||
|
||||
$this->folder1 = new Folder();
|
||||
$this->folder1->setTitle('foo');
|
||||
|
@ -261,6 +263,7 @@ class BookmarkControllerTest extends TestCase {
|
|||
*/
|
||||
public function setupBookmarksWithSharedFolder(): void {
|
||||
$this->setupBookmarksWithPublicFolder();
|
||||
$this->authorizer->setUserId($this->userId);
|
||||
$this->folders->createShare($this->folder1->getId(), $this->otherUserId,\OCP\Share\IShare::TYPE_USER, true, false);
|
||||
}
|
||||
|
||||
|
@ -392,6 +395,32 @@ class BookmarkControllerTest extends TestCase {
|
|||
$this->assertEquals('', $bookmark->getTitle()); // normalized URL
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AlreadyExistsError
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
* @throws UrlParseError
|
||||
* @throws UserLimitExceededError
|
||||
*/
|
||||
public function testEditBookmarkFolders(): void {
|
||||
$this->cleanUp();
|
||||
$this->setupBookmarksWithPublicFolder();
|
||||
$this->authorizer->setUserId($this->userId);
|
||||
$res = $this->controller->newBookmark('https://www.heise.de', 'Heise', 'PublicNoTag', ['four'], [$this->folder1->getId()]);
|
||||
$this->assertEquals('success', $res->getData()['status'], var_export($res->getData(), true));
|
||||
$id = $res->getData()['item']['id'];
|
||||
|
||||
$this->controller->editBookmark($id, 'https://www.heise.de', '', null, null, [$this->folder2->getId()]);
|
||||
|
||||
$bookmark = $this->bookmarkMapper->find($id);
|
||||
$parents = $this->treeMapper->findParentsOf(TreeMapper::TYPE_BOOKMARK, $id);
|
||||
$this->assertEquals('https://www.heise.de/', $bookmark->getUrl()); // normalized URL
|
||||
$this->assertEquals('', $bookmark->getTitle()); // normalized URL
|
||||
$this->assertEquals([$this->folder2->getId()], array_map(function($f){
|
||||
return $f->getId();
|
||||
}, $parents)); // has the folders we set
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AlreadyExistsError
|
||||
* @throws MultipleObjectsReturnedException
|
||||
|
|
|
@ -219,6 +219,7 @@ class FolderControllerTest extends TestCase {
|
|||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function setupBookmarks() {
|
||||
$this->authorizer->setUserId($this->userId);
|
||||
$this->folder1 = new Folder();
|
||||
$this->folder1->setTitle('foo');
|
||||
$this->folder1->setUserId($this->userId);
|
||||
|
@ -260,6 +261,7 @@ class FolderControllerTest extends TestCase {
|
|||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function setupPublicFolder(): void {
|
||||
$this->authorizer->setUserId($this->userId);
|
||||
$this->publicFolder = new PublicFolder();
|
||||
$this->publicFolder->setFolderId($this->folder1->getId());
|
||||
$this->publicFolderMapper->insert($this->publicFolder);
|
||||
|
@ -275,6 +277,7 @@ class FolderControllerTest extends TestCase {
|
|||
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
||||
*/
|
||||
public function setupSharedFolder() {
|
||||
$this->authorizer->setUserId($this->userId);
|
||||
$this->folders->createShare($this->folder1->getId(), $this->otherUser, \OCP\Share\IShare::TYPE_USER, true, false);
|
||||
}
|
||||
|
||||
|
@ -578,6 +581,7 @@ class FolderControllerTest extends TestCase {
|
|||
$this->cleanUp();
|
||||
$this->setupBookmarks();
|
||||
$this->setupPublicFolder();
|
||||
$this->authorizer->setUserId(null);
|
||||
$output = $this->public->getFolder($this->folder1->getId());
|
||||
$data = $output->getData();
|
||||
$this->assertEquals('success', $data['status'], var_export($data, true));
|
||||
|
@ -594,6 +598,7 @@ class FolderControllerTest extends TestCase {
|
|||
public function testReadPublicFail(): void {
|
||||
$this->cleanUp();
|
||||
$this->setupBookmarks();
|
||||
$this->authorizer->setUserId(null);
|
||||
$output = $this->public->getFolder($this->folder1->getId());
|
||||
$data = $output->getData();
|
||||
$this->assertEquals('error', $data['status'], var_export($data, true));
|
||||
|
@ -609,6 +614,7 @@ class FolderControllerTest extends TestCase {
|
|||
$this->cleanUp();
|
||||
$this->setupBookmarks();
|
||||
$this->setupPublicFolder();
|
||||
$this->authorizer->setUserId(null);
|
||||
$output = $this->public->addFolder('bla', $this->folder1->getId());
|
||||
$data = $output->getData();
|
||||
$this->assertEquals('error', $data['status'], var_export($data, true));
|
||||
|
@ -625,6 +631,7 @@ class FolderControllerTest extends TestCase {
|
|||
$this->cleanUp();
|
||||
$this->setupBookmarks();
|
||||
$this->setupPublicFolder();
|
||||
$this->authorizer->setUserId(null);
|
||||
$output = $this->public->editFolder($this->folder2->getId(), 'blabla');
|
||||
$data = $output->getData();
|
||||
$this->assertEquals('error', $data['status'], var_export($data, true));
|
||||
|
@ -645,6 +652,7 @@ class FolderControllerTest extends TestCase {
|
|||
$this->cleanUp();
|
||||
$this->setupBookmarks();
|
||||
$this->setupPublicFolder();
|
||||
$this->authorizer->setUserId(null);
|
||||
$output = $this->public->deleteFolder($this->folder1->getId());
|
||||
$data = $output->getData();
|
||||
$this->assertEquals('error', $data['status'], var_export($data, true));
|
||||
|
@ -663,6 +671,7 @@ class FolderControllerTest extends TestCase {
|
|||
$this->cleanUp();
|
||||
$this->setupBookmarks();
|
||||
$this->setupPublicFolder();
|
||||
$this->authorizer->setUserId(null);
|
||||
$output = $this->public->getFolderChildrenOrder($this->folder1->getId(), -1);
|
||||
$data = $output->getData();
|
||||
$this->assertEquals('success', $data['status'], var_export($data, true));
|
||||
|
@ -682,6 +691,7 @@ class FolderControllerTest extends TestCase {
|
|||
public function testSetFullHierarchyPublicFail(): void {
|
||||
$this->cleanUp();
|
||||
$this->setupBookmarks();
|
||||
$this->authorizer->setUserId(null);
|
||||
$output = $this->public->setFolderChildrenOrder($this->folder1->getId(), [
|
||||
['type' => 'bookmark', 'id' => $this->bookmark1Id],
|
||||
['type' => 'folder', 'id' => $this->folder2->getId()],
|
||||
|
@ -713,6 +723,7 @@ class FolderControllerTest extends TestCase {
|
|||
$this->cleanUp();
|
||||
$this->setupBookmarks();
|
||||
$this->setupPublicFolder();
|
||||
$this->authorizer->setUserId(null);
|
||||
$output = $this->public->getFolders($this->folder1->getId(), -1);
|
||||
$data = $output->getData();
|
||||
$this->assertCount(1, $data['data']);
|
||||
|
|
|
@ -108,6 +108,21 @@ class TagMapperTest extends TestCase {
|
|||
$this->assertNotContains(['name' => 'four', 'count' => 0], $allTagsWithCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAddToAndFind
|
||||
*/
|
||||
public function testDelete() {
|
||||
$this->tagMapper->deleteTag($this->userId, 'one');
|
||||
$allTags = $this->tagMapper->findAll($this->userId);
|
||||
$this->assertNotContains('one', $allTags);
|
||||
$this->assertContains('two', $allTags);
|
||||
$this->assertContains('three', $allTags);
|
||||
|
||||
$allTagsWithCount = $this->tagMapper->findAllWithCount($this->userId);
|
||||
$this->assertContains(['name' => 'two', 'count' => 2], $allTagsWithCount);
|
||||
$this->assertContains(['name' => 'three', 'count' => 1], $allTagsWithCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAddToAndFind
|
||||
* @dataProvider singleBookmarksProvider
|
||||
|
|
Loading…
Reference in New Issue